diff -u --recursive --new-file v2.1.96/linux/CREDITS linux/CREDITS
--- v2.1.96/linux/CREDITS	Tue Apr 14 14:29:19 1998
+++ linux/CREDITS	Fri Apr 17 22:06:23 1998
@@ -1433,8 +1433,8 @@
 S: France
 
 N: Rik van Riel
-E: H.H.vanRiel@fys.ruu.nl
-W: http://www.fys.ruu.nl/~riel/
+E: H.H.vanRiel@phys.uu.nl
+W: http://www.phys.uu.nl/~riel/
 D: Maintainer of the mm-patches page (see www.linuxhq.com)
 D: Documentation/sysctl/*, kswapd fixes, random kernel hacker
 S: Vorenkampsweg 1
diff -u --recursive --new-file v2.1.96/linux/Documentation/Configure.help linux/Documentation/Configure.help
--- v2.1.96/linux/Documentation/Configure.help	Tue Apr 14 14:29:19 1998
+++ linux/Documentation/Configure.help	Fri Apr 17 22:04:44 1998
@@ -434,6 +434,15 @@
   I/O speeds to be set as well.  See the Documentation/ide.txt and
   ali14xx.c files for more info.
 
+Apple Macintosh builtin IDE interface support (EXPERIMENTAL)
+CONFIG_BLK_DEV_MAC_IDE
+  This is the IDE driver for the builtin IDE interface on some Apple
+  Macintosh models.  It supports both the Quadra/Performa/LC 630 and
+  the PowerBook 190 IDE interface.
+  Say Y if you have such a Macintosh model and want to use IDE devices
+  (hard disks, CD-ROM drives, etc.) that are connected to the builtin
+  IDE interface.
+
 XT hard disk support
 CONFIG_BLK_DEV_XD
   Very old 8 bit hard disk controllers used in the IBM XT computer. To
@@ -956,6 +965,19 @@
   serial ports on the same board to share a single IRQ.  To enable
   support for this in the serial driver, say Y here.
 
+Autodetect IRQ on standard ports (unsafe)
+CONFIG_SERIAL_DETECT_IRQ
+  Enable this option if you want the kernel to try to guess which IRQ
+  is configured during the boot sequence and you're too lazy to edit
+  the boot scripts to use the setserial command.  This option can be
+  unsafe and should not be enabled on most machines.  It is far
+  better to dynamically request autoconfiguration during the boot-time
+  scripts using the setserial command.  You can change the IRQ and/or 
+  request automatic IRQ configuration at any time by using the 
+  "setserial" program.  I wouldn't include this config option at all except
+  people keep bellyaching about it.  I guess they are really are too lazy 
+  to edit their boot scripts.  :-)   If unsure, say No.
+
 Support special multiport boards
 CONFIG_SERIAL_MULTIPORT
   Some multiport serial ports have special ports which are used to
@@ -7301,8 +7323,9 @@
 
 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. ;)
+  This option enables support for the Apple Macintosh series of computers
+  (yes, there is experimental support now, at least for part of the series).  
+  Say N unless you're willing to code the remaining necessary support. ;)
 
 # CONFIG_APOLLO, etc. coming soon (?)
 
diff -u --recursive --new-file v2.1.96/linux/Documentation/pci.txt linux/Documentation/pci.txt
--- v2.1.96/linux/Documentation/pci.txt	Mon Apr  6 17:40:59 1998
+++ linux/Documentation/pci.txt	Fri Apr 17 21:58:48 1998
@@ -53,8 +53,13 @@
 
 4. Obsolete functions
 ~~~~~~~~~~~~~~~~~~~~~
-
 <linux/bios32.h> is obsolete and should not be included in new code.
 
 pcibios_find_(device|class) are also obsolete and should be replaced by
 pci_find_(device|class).
+
+5. Bus mastering
+~~~~~~~~~~~~~~~~
+   If you need to setup a bus-mastering card, just call pci_set_master(). It
+should set PCI_COMMAND_MASTER in the command register and adjust the latency
+timer if needed.
diff -u --recursive --new-file v2.1.96/linux/Documentation/powerpc/ppc_htab.txt linux/Documentation/powerpc/ppc_htab.txt
--- v2.1.96/linux/Documentation/powerpc/ppc_htab.txt	Sat Nov 29 10:33:18 1997
+++ linux/Documentation/powerpc/ppc_htab.txt	Tue Apr 14 17:33:40 1998
@@ -4,6 +4,8 @@
 This document and the related code was written by me (Cort Dougan), please
 email me (cort@cs.nmt.edu) if you have questions, comments or corrections.
 
+Last Change: 2.16.98
+
 This entry in the proc directory is readable by all users but only
 writable by root.
 
diff -u --recursive --new-file v2.1.96/linux/Documentation/powerpc/smp.txt linux/Documentation/powerpc/smp.txt
--- v2.1.96/linux/Documentation/powerpc/smp.txt	Mon Jan 12 15:18:12 1998
+++ linux/Documentation/powerpc/smp.txt	Tue Apr 14 17:33:40 1998
@@ -1,8 +1,11 @@
                  Information about Linux/PPC SMP mode
 =====================================================================
 
-This document and the related code was written by me (Cort Dougan), please
-email me (cort@cs.nmt.edu) if you have questions, comments or corrections.
+This document and the related code was written by me
+(Cort Dougan, cort@cs.nmt.edu) please email me if you have questions,
+comments or corrections.
+
+Last Change: 4.1.98
 
 SMP support for Linux/PPC is still in its early stages and likely to
 be buggy for a while.  If you want to help by writing code or testing
@@ -10,10 +13,16 @@
 
 1. State of Supported Hardware
 
-  UMAX s900
+  PowerSurge Architecture - UMAX s900, Apple 9500/9600/8500/8600/7500/7600
     The second processor on this machine boots up just fine and
     enters its idle loop.  Hopefully a completely working SMP kernel
     on this machine will be done shortly.
+
+    The code makes the assumption of only two processors.  The changes
+    necessary to work with any number would not be overly difficult but
+    I don't have any machines with >2 processors so it's not high on my
+    list of priorities.  If anyone else would like do to the work email
+    me and I can point out the places that need changed.
 
   BeBox
     BeBox support hasn't been added to the 2.1.X kernels from 2.0.X
diff -u --recursive --new-file v2.1.96/linux/Documentation/powerpc/sound.txt linux/Documentation/powerpc/sound.txt
--- v2.1.96/linux/Documentation/powerpc/sound.txt	Sat Nov 29 10:33:18 1997
+++ linux/Documentation/powerpc/sound.txt	Tue Apr 14 17:33:40 1998
@@ -4,26 +4,31 @@
 Please mail me me (Cort Dougan, cort@cs.nmt.edu) if you have questions,
 comments or corrections.
 
-This just covers sound on the PReP systems for now, and later will
-contain information on the PowerMac's.
+Last Change: 3.24.98
 
-Sound has been tested and is working with the PowerStack and IBM Power
-Series onboard sound systems which are based on the cs4231(2) chip.
-The sound options when doing the make config are a bit different from the
-default, though.
+This just covers sound on the PReP and CHRP systems for now and later
+will contain information on the PowerMac's.
+
+Sound on PReP has been tested and is working with the PowerStack and IBM
+Power Series onboard sound systems which are based on the cs4231(2) chip.
+The sound options when doing the make config are a bit different from
+the default, though.
 
 The I/O base, irq and dma lines that you enter during the make config
 are ignored and are set when booting according to the machine type.
 This is so that one binary can be used for Motorola and IBM machines
-which use different values and isn't allowed by the driver, so things are
-hacked together in such a way as to allow this information to be set
-automatically on boot.
-
-1. PowerStack
-
-  Enable support for "Crystal CS4232 based (PnP) cards".  Although the
-  options you set are ignored and determined automatically on boot these
-  are included for information only:
+which use different values and isn't allowed by the driver, so things
+are hacked together in such a way as to allow this information to be
+set automatically on boot.
+
+1. Motorola PowerStack PReP machines
+
+  Enable support for "Crystal CS4232 based (PnP) cards" and for the
+  Microsoft Sound System.  The MSS isn't used, but some of the routines
+  that the CS4232 driver uses are in it.
+
+  Although the options you set are ignored and determined automatically
+  on boot these are included for information only:
 
   (830) CS4232 audio I/O base 530, 604, E80 or F40
   (10) CS4232 audio IRQ 5, 7, 9, 11, 12 or 15
@@ -33,16 +38,31 @@
   This will allow simultaneous record and playback, as 2 different dma
   channels are used.
 
+  The sound will be all left channel and very low volume since the
+  auxiliary input isn't muted by default.  I had the changes necessary
+  for this in the kernel but the sound driver maintainer didn't want
+  to include them since it wasn't common in other machines.  To fix this
+  you need to mute it using a mixer utility of some sort (if you find one
+  please let me know) or by patching the driver yourself and recompiling.
+
+  There is a problem on the PowerStack 2's (PowerStack Pro's) using a
+  different irq/drq than the kernel expects.  Unfortunately, I don't know
+  which irq/drq it is so if anyone knows please email me.
+
   Midi is not supported since the cs4232 driver doesn't support midi yet.
 
-2. IBM machines
+2. IBM PowerPersonal PReP machines and IBM LongTrail CHRP
 
   I've only tested sound on the Power Personal Series of IBM workstations
-  so if you try it on others please let me know the result.
+  so if you try it on others please let me know the result.  I'm especially
+  interested in the 43p's sound system, which I know nothing about.
 
-  Enable support for "Crystal CS4232 based (PnP) cards".  Although the
-  options you set are ignored and determined automatically on boot these
-  are included for information only:
+  Enable support for "Crystal CS4232 based (PnP) cards" and for the
+  Microsoft Sound System.  The MSS isn't used, but some of the routines
+  that the CS4232 driver uses are in it.
+
+  Although the options you set are ignored and determined automatically
+  on boot these are included for information only:
 
   (530) CS4232 audio I/O base 530, 604, E80 or F40
   (5) CS4232 audio IRQ 5, 7, 9, 11, 12 or 15
@@ -54,4 +74,3 @@
   This setup does _NOT_ allow for recording yet.
 
   Midi is not supported since the cs4232 driver doesn't support midi yet.
-
diff -u --recursive --new-file v2.1.96/linux/Documentation/sysctl/README linux/Documentation/sysctl/README
--- v2.1.96/linux/Documentation/sysctl/README	Tue Mar 10 10:03:30 1998
+++ linux/Documentation/sysctl/README	Fri Apr 17 22:06:22 1998
@@ -1,6 +1,6 @@
 
 Documentation for /proc/sys/*/*		version 0.1
-	(c) 1998, Rik van Riel <H.H.vanRiel@fys.ruu.nl>
+	(c) 1998, Rik van Riel <H.H.vanRiel@phys.uu.nl>
 
 'Why', I hear you ask, 'would anyone even _want_ documentation
 for them sysctl files? If anybody really needs it, it's all in
@@ -32,7 +32,7 @@
 you're the last RTFMing person to screw up.
 
 In short, e-mail your suggestions, corrections and / or horror
-stories to: <H.H.vanRiel@fys.ruu.nl>
+stories to: <H.H.vanRiel@phys.uu.nl>
 
 Rik van Riel.
 
diff -u --recursive --new-file v2.1.96/linux/Documentation/sysctl/kernel.txt linux/Documentation/sysctl/kernel.txt
--- v2.1.96/linux/Documentation/sysctl/kernel.txt	Thu Mar 26 15:57:02 1998
+++ linux/Documentation/sysctl/kernel.txt	Fri Apr 17 22:06:22 1998
@@ -1,6 +1,6 @@
 
 Documentation for /proc/sys/kernel/*	version 0.1
-	(c) 1998, Rik van Riel <H.H.vanRiel@fys.ruu.nl>
+	(c) 1998, Rik van Riel <H.H.vanRiel@phys.uu.nl>
 
 For general info and legal blurb, please look in README.
 
diff -u --recursive --new-file v2.1.96/linux/Documentation/sysctl/vm.txt linux/Documentation/sysctl/vm.txt
--- v2.1.96/linux/Documentation/sysctl/vm.txt	Thu Mar 26 15:57:02 1998
+++ linux/Documentation/sysctl/vm.txt	Fri Apr 17 22:06:23 1998
@@ -1,6 +1,6 @@
 
 Documentation for /proc/sys/vm/*	version 0.1
-	(c) 1998, Rik van Riel <H.H.vanRiel@fys.ruu.nl>
+	(c) 1998, Rik van Riel <H.H.vanRiel@phys.uu.nl>
 
 For general info and legal blurb, please look in README.
 
diff -u --recursive --new-file v2.1.96/linux/Makefile linux/Makefile
--- v2.1.96/linux/Makefile	Tue Apr 14 14:29:19 1998
+++ linux/Makefile	Tue Apr 14 14:29:05 1998
@@ -1,6 +1,6 @@
 VERSION = 2
 PATCHLEVEL = 1
-SUBLEVEL = 96
+SUBLEVEL = 97
 
 ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/)
 
diff -u --recursive --new-file v2.1.96/linux/arch/i386/kernel/bios32.c linux/arch/i386/kernel/bios32.c
--- v2.1.96/linux/arch/i386/kernel/bios32.c	Fri Apr 10 13:03:48 1998
+++ linux/arch/i386/kernel/bios32.c	Fri Apr 17 21:58:48 1998
@@ -1,7 +1,7 @@
 /*
  * bios32.c - Low-Level PCI Access
  *
- * $Id: bios32.c,v 1.26 1998/02/18 15:21:09 mj Exp $
+ * $Id: bios32.c,v 1.29 1998/04/17 16:31:15 mj Exp $
  *
  * Sponsored by
  *	iX Multiuser Multitasking Magazine
@@ -73,6 +73,7 @@
 #include <linux/kernel.h>
 #include <linux/pci.h>
 #include <linux/init.h>
+#include <linux/ioport.h>
 
 #include <asm/page.h>
 #include <asm/segment.h>
@@ -86,6 +87,14 @@
 
 #include "irq.h"
 
+#undef DEBUG
+
+#ifdef DEBUG
+#define DBG(x...) printk(x)
+#else
+#define DBG(x...)
+#endif
+
 /*
  * Generic PCI access -- indirect calls according to detected HW.
  */
@@ -158,11 +167,13 @@
 	return access_pci->write_config_dword(bus, device_fn, where, value);
 }
 
-static unsigned int pci_probe = ~0;
-
 #define PCI_PROBE_BIOS 1
 #define PCI_PROBE_CONF1 2
 #define PCI_PROBE_CONF2 4
+#define PCI_NO_SORT 0x100
+#define PCI_BIOS_SORT 0x200
+
+static unsigned int pci_probe = PCI_PROBE_BIOS | PCI_PROBE_CONF1 | PCI_PROBE_CONF2;
 
 /*
  * Direct access to PCI hardware...
@@ -596,8 +607,10 @@
 	return (int) (ret & 0xff00) >> 8;
 }
 
-static int pci_bios_find_device (unsigned short vendor, unsigned short device_id,
-	unsigned short index, unsigned char *bus, unsigned char *device_fn)
+#endif
+
+__initfunc(static int pci_bios_find_device (unsigned short vendor, unsigned short device_id,
+	unsigned short index, unsigned char *bus, unsigned char *device_fn))
 {
 	unsigned short bx;
 	unsigned short ret;
@@ -621,8 +634,6 @@
 	return (int) (ret & 0xff00) >> 8;
 }
 
-#endif
-
 static int pci_bios_read_config_byte(unsigned char bus,
 	unsigned char device_fn, unsigned char where, unsigned char *value)
 {
@@ -803,13 +814,13 @@
 				check->fields.revision, check);
 			continue;
 		}
-		printk ("PCI: BIOS32 Service Directory structure at 0x%p\n", check);
+		DBG("PCI: BIOS32 Service Directory structure at 0x%p\n", check);
 		if (check->fields.entry >= 0x100000) {
-			printk("PCI: BIOS32 entry in high memory, cannot use.\n");
+			printk("PCI: BIOS32 entry (0x%p) in high memory, cannot use.\n", check);
 			return NULL;
 		} else {
 			bios32_entry = check->fields.entry;
-			printk ("PCI: BIOS32 Service Directory entry at 0x%lx\n", bios32_entry);
+			DBG("PCI: BIOS32 Service Directory entry at 0x%lx\n", bios32_entry);
 			bios32_indirect.address = bios32_entry + PAGE_OFFSET;
 			if (check_pcibios())
 				return &pci_bios_access;
@@ -817,18 +828,104 @@
 		break;	/* Hopefully more than one BIOS32 cannot happen... */
 	}
 
-	/*
-	 * If we were told to use the PCI BIOS and it's not present, avoid
-	 * touching the hardware.
-	 */
-	pci_probe = 0;
 	return NULL;
 }
 
+/*
+ * Sort the device list according to PCI BIOS.
+ */
+
+__initfunc(void pcibios_sort(void))
+{
+	struct pci_dev *dev = pci_devices;
+	struct pci_dev **last = &pci_devices;
+	struct pci_dev *d, **dd, *e;
+	int idx;
+	unsigned char bus, devfn;
+
+	DBG("PCI: Sorting device list...\n");
+	while ((e = dev)) {
+		idx = 0;
+		while (pci_bios_find_device(e->vendor, e->device, idx, &bus, &devfn) == PCIBIOS_SUCCESSFUL) {
+			idx++;
+			for(dd=&dev; (d = *dd); dd = &d->next) {
+				if (d->bus->number == bus && d->devfn == devfn) {
+					*dd = d->next;
+					*last = d;
+					last = &d->next;
+					break;
+				}
+			}
+			if (!d)
+				printk("PCI: BIOS reporting unknown device %02x:%02x\n", bus, devfn);
+		}
+		if (!idx) {
+			printk("PCI: Device %02x:%02x not found by BIOS\n",
+				dev->bus->number, dev->devfn);
+			d = dev;
+			dev = dev->next;
+			*last = d;
+			last = &d->next;
+		}
+	}
+	*last = NULL;
+}
+
 #endif
 
 /*
- * Arch-dependent fixups.
+ * Several BIOS'es forget to assign addresses to I/O ranges.
+ * We try to fix it here, expecting there are free addresses
+ * starting with 0x5800. Ugly, but until we come with better
+ * resource management, it's the only simple solution.
+ */
+
+static int pci_last_io_addr __initdata = 0x5800;
+
+__initfunc(void pcibios_fixup_io_addr(struct pci_dev *dev, int idx))
+{
+	unsigned short cmd;
+	unsigned int reg = PCI_BASE_ADDRESS_0 + 4*idx;
+	unsigned int size, addr, try;
+	unsigned int bus = dev->bus->number;
+	unsigned int devfn = dev->devfn;
+
+	if (!pci_last_io_addr) {
+		printk("PCI: Unassigned I/O space for %02x:%02x\n", bus, devfn);
+		return;
+	}
+	pcibios_read_config_word(bus, devfn, PCI_COMMAND, &cmd);
+	pcibios_write_config_word(bus, devfn, PCI_COMMAND, cmd & ~PCI_COMMAND_IO);
+	pcibios_write_config_dword(bus, devfn, reg, ~0);
+	pcibios_read_config_dword(bus, devfn, reg, &size);
+	size = (~(size & PCI_BASE_ADDRESS_IO_MASK) & 0xffff) + 1;
+	addr = 0;
+	if (!size || size > 0x100)
+		printk("PCI: Unable to handle I/O allocation for %02x:%02x (%04x), tell <mj@ucw.cz>\n", bus, devfn, size);
+	else {
+		do {
+			addr = (pci_last_io_addr + size - 1) & ~(size-1);
+			pci_last_io_addr = addr + size;
+		} while (check_region(addr, size));
+		printk("PCI: Assigning I/O space %04x-%04x to device %02x:%02x\n", addr, addr+size-1, bus, devfn);
+		pcibios_write_config_dword(bus, devfn, reg, addr | PCI_BASE_ADDRESS_SPACE_IO);
+		pcibios_read_config_dword(bus, devfn, reg, &try);
+		if ((try & PCI_BASE_ADDRESS_IO_MASK) != addr) {
+			addr = 0;
+			printk("PCI: Address setup failed, got %04x\n", try);
+		} else
+			dev->base_address[idx] = try;
+	}
+	if (!addr) {
+		pcibios_write_config_dword(bus, devfn, reg, 0);
+		dev->base_address[idx] = 0;
+	}
+	pcibios_write_config_word(bus, devfn, PCI_COMMAND, cmd);
+}
+
+/*
+ * Arch-dependent fixups. We need to fix here base addresses, I/O
+ * and memory enables and IRQ's as the PCI BIOS'es are buggy as hell.
  */
 
 __initfunc(void pcibios_fixup(void))
@@ -836,7 +933,6 @@
 	struct pci_dev *dev;
 	int i, has_io, has_mem;
 	unsigned short cmd;
-	unsigned char pin;
 
 	for(dev = pci_devices; dev; dev=dev->next) {
 		/*
@@ -849,18 +945,15 @@
 		for(i=0; i<6; i++) {
 			unsigned long a = dev->base_address[i];
 			if (a & PCI_BASE_ADDRESS_SPACE_IO) {
-				has_io |= 1;
+				has_io = 1;
 				a &= PCI_BASE_ADDRESS_IO_MASK;
-				if (!a || a == PCI_BASE_ADDRESS_IO_MASK) {
-					printk(KERN_WARNING "PCI: BIOS forgot to assign address #%d to device %02x:%02x,"
-						" please report to <mj@ucw.cz>\n", i, dev->bus->number, dev->devfn);
-					has_io |= 2;
-				}
+				if (!a || a == PCI_BASE_ADDRESS_IO_MASK)
+					pcibios_fixup_io_addr(dev, i);
 			} else if (a & PCI_BASE_ADDRESS_MEM_MASK)
 				has_mem = 1;
 		}
 		pci_read_config_word(dev, PCI_COMMAND, &cmd);
-		if (has_io == 1 && !(cmd & PCI_COMMAND_IO)) {
+		if (has_io && !(cmd & PCI_COMMAND_IO)) {
 			printk("PCI: Enabling I/O for device %02x:%02x\n",
 				dev->bus->number, dev->devfn);
 			cmd |= PCI_COMMAND_IO;
@@ -872,14 +965,15 @@
 			cmd |= PCI_COMMAND_MEMORY;
 			pci_write_config_word(dev, PCI_COMMAND, cmd);
 		}
-		pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin);
 #ifdef __SMP__
 		/*
 		 * Recalculate IRQ numbers if we use the I/O APIC
 		 */
 		{
 		int irq;
+		unsigned char pin;
 
+		pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin);
 		if (pin) {
 			pin--;		/* interrupt pins are numbered starting from 1 */
 			irq = IO_APIC_get_PCI_irq_vector (dev->bus->number, PCI_SLOT(dev->devfn), pin);
@@ -896,30 +990,38 @@
 		 */
 		if (dev->irq >= NR_IRQS)
 			dev->irq = 0;
-		if (pin && !dev->irq)
-			printk(KERN_WARNING "PCI: Bogus IRQ for device %02x:%02x [pin=%x], please report to <mj@ucw.cz>\n",
-				dev->bus->number, dev->devfn, pin);
 	}
+
+#ifdef CONFIG_PCI_BIOS
+	if ((pci_probe & PCI_BIOS_SORT) && !(pci_probe & PCI_NO_SORT))
+		pcibios_sort();
+#endif
 }
 
 /*
- * Initialization. Try all known PCI access methods.
+ * Initialization. Try all known PCI access methods. Note that we support
+ * using both PCI BIOS and direct access: in such cases, we use I/O ports
+ * to access config space, but we still keep BIOS order of cards to be
+ * compatible with 2.0.X. This should go away in 2.3.
  */
 
 __initfunc(void pcibios_init(void))
 {
-	struct pci_access *a = NULL;
+	struct pci_access *bios = NULL;
+	struct pci_access *dir = NULL;
 
 #ifdef CONFIG_PCI_BIOS
-	if (pci_probe & PCI_PROBE_BIOS)
-		a = pci_find_bios();
+	if ((pci_probe & PCI_PROBE_BIOS) && ((bios = pci_find_bios())))
+		pci_probe |= PCI_BIOS_SORT;
 #endif
 #ifdef CONFIG_PCI_DIRECT
-	if (!a && (pci_probe & (PCI_PROBE_CONF1 | PCI_PROBE_CONF2)))
-		a = pci_check_direct();
+	if (pci_probe & (PCI_PROBE_CONF1 | PCI_PROBE_CONF2))
+		dir = pci_check_direct();
 #endif
-	if (a)
-		access_pci = a;
+	if (dir)
+		access_pci = dir;
+	else if (bios)
+		access_pci = bios;
 }
 
 #if !defined(CONFIG_PCI_BIOS) && !defined(CONFIG_PCI_DIRECT)
@@ -928,25 +1030,35 @@
 
 __initfunc(char *pcibios_setup(char *str))
 {
-	if (!strncmp(str, "off", 3)) {
+	if (!strcmp(str, "off")) {
 		pci_probe = 0;
 		return NULL;
+	} else if (!strncmp(str, "io=", 3)) {
+		char *p;
+		unsigned int x = simple_strtoul(str+3, &p, 16);
+		if (p && *p)
+			return str;
+		pci_last_io_addr = x;
+		return NULL;
 	}
 #ifdef CONFIG_PCI_BIOS
-	else if (!strncmp(str, "bios", 4)) {
+	else if (!strcmp(str, "bios")) {
 		pci_probe = PCI_PROBE_BIOS;
 		return NULL;
-	} else if (!strncmp(str, "nobios", 6)) {
+	} else if (!strcmp(str, "nobios")) {
 		pci_probe &= ~PCI_PROBE_BIOS;
 		return NULL;
+	} else if (!strcmp(str, "nosort")) {
+		pci_probe |= PCI_NO_SORT;
+		return NULL;
 	}
 #endif
 #ifdef CONFIG_PCI_DIRECT
-	else if (!strncmp(str, "conf1", 5)) {
+	else if (!strcmp(str, "conf1")) {
 		pci_probe = PCI_PROBE_CONF1;
 		return NULL;
 	}
-	else if (!strncmp(str, "conf2", 5)) {
+	else if (!strcmp(str, "conf2")) {
 		pci_probe = PCI_PROBE_CONF2;
 		return NULL;
 	}
diff -u --recursive --new-file v2.1.96/linux/arch/i386/kernel/ldt.c linux/arch/i386/kernel/ldt.c
--- v2.1.96/linux/arch/i386/kernel/ldt.c	Tue Apr 14 14:29:20 1998
+++ linux/arch/i386/kernel/ldt.c	Wed Apr 15 12:33:43 1998
@@ -64,7 +64,7 @@
 	 */
 	if (!mm->segments) {
 		for (i=1 ; i<NR_TASKS ; i++) {
-			if (task[i]->mm == mm) {
+			if (task[i] == current) {
 				if (!(mm->segments = (void *) vmalloc(LDT_ENTRIES*LDT_ENTRY_SIZE)))
 					return -ENOMEM;
 				memset(mm->segments, 0, LDT_ENTRIES*LDT_ENTRY_SIZE);
diff -u --recursive --new-file v2.1.96/linux/arch/i386/kernel/process.c linux/arch/i386/kernel/process.c
--- v2.1.96/linux/arch/i386/kernel/process.c	Tue Apr 14 14:29:20 1998
+++ linux/arch/i386/kernel/process.c	Wed Apr 15 12:33:43 1998
@@ -488,7 +488,7 @@
 			memcpy(ldt, old_mm->segments, LDT_ENTRIES*LDT_ENTRY_SIZE);
 		}
 	}
-	set_ldt_desc(gdt+(nr<<1)+FIRST_LDT_ENTRY, ldt, LDT_ENTRIES);
+	set_ldt_desc(gdt+(nr<<1)+FIRST_LDT_ENTRY, ldt, ldt_size);
 }
 
 int copy_thread(int nr, unsigned long clone_flags, unsigned long esp,
diff -u --recursive --new-file v2.1.96/linux/arch/i386/math-emu/fpu_system.h linux/arch/i386/math-emu/fpu_system.h
--- v2.1.96/linux/arch/i386/math-emu/fpu_system.h	Tue Apr 14 14:29:20 1998
+++ linux/arch/i386/math-emu/fpu_system.h	Wed Apr 15 12:33:44 1998
@@ -20,7 +20,7 @@
    of the stack frame of math_emulate() */
 #define SETUP_DATA_AREA(arg)	FPU_info = (struct info *) &arg
 
-#define LDT_DESCRIPTOR(s)	(((struct ldt_struct *)current->mm->segments)[(s) >> 3])
+#define LDT_DESCRIPTOR(s)	(((struct desc_struct *)current->mm->segments)[(s) >> 3])
 #define SEG_D_SIZE(x)		((x).b & (3 << 21))
 #define SEG_G_BIT(x)		((x).b & (1 << 23))
 #define SEG_GRANULARITY(x)	(((x).b & (1 << 23)) ? 4096 : 1)
diff -u --recursive --new-file v2.1.96/linux/arch/ppc/8xx_io/Makefile linux/arch/ppc/8xx_io/Makefile
--- v2.1.96/linux/arch/ppc/8xx_io/Makefile	Wed Dec 31 16:00:00 1969
+++ linux/arch/ppc/8xx_io/Makefile	Tue Apr 14 17:33:40 1998
@@ -0,0 +1,13 @@
+#
+# Makefile for the linux MPC8xx ppc-specific parts of comm processor
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definition is now in the main makefile...
+
+O_TARGET := 8xx_io.a
+O_OBJS	= commproc.o uart.o enet.o
+
+include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v2.1.96/linux/arch/ppc/8xx_io/commproc.c linux/arch/ppc/8xx_io/commproc.c
--- v2.1.96/linux/arch/ppc/8xx_io/commproc.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/ppc/8xx_io/commproc.c	Tue Apr 14 17:33:40 1998
@@ -0,0 +1,223 @@
+
+/*
+ * General Purpose functions for the global management of the
+ * Communication Processor Module.
+ * Copyright (c) 1997 Dan Malek (dmalek@jlc.net)
+ *
+ * In addition to the individual control of the communication
+ * channels, there are a few functions that globally affect the
+ * communication processor.
+ *
+ * Buffer descriptors must be allocated from the dual ported memory
+ * space.  The allocator for that is here.  When the communication
+ * process is reset, we reclaim the memory available.  There is
+ * currently no deallocator for this memory.
+ * The amount of space available is platform dependent.  On the
+ * MBX, the EPPC software loads additional microcode into the
+ * communication processor, and uses some of the DP ram for this
+ * purpose.  Current, the first 512 bytes and the last 256 bytes of
+ * memory are used.  Right now I am conservative and only use the
+ * memory that can never be used for microcode.  If there are
+ * applications that require more DP ram, we can expand the boundaries
+ * but then we have to be careful of any downloaded microcode.
+ */
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/param.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <asm/irq.h>
+#include <asm/mbx.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/8xx_immap.h>
+#include "commproc.h"
+
+static	uint	dp_alloc_base;	/* Starting offset in DP ram */
+static	uint	dp_alloc_top;	/* Max offset + 1 */
+static	uint	host_buffer;	/* One page of host buffer */
+static	uint	host_end;	/* end + 1 */
+cpm8xx_t	*cpmp;		/* Pointer to comm processor space */
+
+/* CPM interrupt vector functions.
+*/
+struct	cpm_action {
+	void	(*handler)(void *);
+	void	*dev_id;
+};
+static	struct	cpm_action cpm_vecs[CPMVEC_NR];
+static	void	cpm_interrupt(int irq, void * dev, struct pt_regs * regs);
+static	void	cpm_error_interrupt(void *);
+
+void
+mbx_cpm_reset(uint host_page_addr)
+{
+	volatile immap_t	 *imp;
+	volatile cpm8xx_t	*commproc;
+	pte_t			*pte;
+
+	imp = (immap_t *)MBX_IMAP_ADDR;
+	commproc = (cpm8xx_t *)&imp->im_cpm;
+
+#ifdef notdef
+	/* We can't do this.  It seems to blow away the microcode
+	 * patch that EPPC-Bug loaded for us.  EPPC-Bug uses SCC1 for
+	 * Ethernet, SMC1 for the console, and I2C for serial EEPROM.
+	 * Our own drivers quickly reset all of these.
+	 */
+
+	/* Perform a reset.
+	*/
+	commproc->cp_cpcr = (CPM_CR_RST | CPM_CR_FLG);
+
+	/* Wait for it.
+	*/
+	while (commproc->cp_cpcr & CPM_CR_FLG);
+#endif
+
+	/* Set SDMA Bus Request priority 5.
+	*/
+	imp->im_siu_conf.sc_sdcr = 1;
+
+	/* Reclaim the DP memory for our use.
+	*/
+	dp_alloc_base = CPM_DATAONLY_BASE;
+	dp_alloc_top = dp_alloc_base + CPM_DATAONLY_SIZE;
+
+	/* Set the host page for allocation.
+	*/
+	host_buffer = host_page_addr;	/* Host virtual page address */
+	host_end = host_page_addr + PAGE_SIZE;
+	pte = va_to_pte(&init_task, host_page_addr);
+	pte_val(*pte) |= _PAGE_NO_CACHE;
+	flush_tlb_page(current->mm->mmap, host_buffer);
+
+	/* Tell everyone where the comm processor resides.
+	*/
+	cpmp = (cpm8xx_t *)commproc;
+
+	/* Initialize the CPM interrupt controller.
+	*/
+	((immap_t *)MBX_IMAP_ADDR)->im_cpic.cpic_cicr =
+	    (CICR_SCD_SCC4 | CICR_SCC_SCC3 | CICR_SCB_SCC2 | CICR_SCA_SCC1) |
+		((CPM_INTERRUPT/2) << 13) | CICR_HP_MASK;
+	((immap_t *)MBX_IMAP_ADDR)->im_cpic.cpic_cimr = 0;
+
+	/* Set our interrupt handler with the core CPU.
+	*/
+	if (request_irq(CPM_INTERRUPT, cpm_interrupt, 0, "cpm", NULL) != 0)
+		panic("Could not allocate CPM IRQ!");
+
+	/* Install our own error handler.
+	*/
+	cpm_install_handler(CPMVEC_ERROR, cpm_error_interrupt, NULL);
+
+	((immap_t *)MBX_IMAP_ADDR)->im_cpic.cpic_cicr |= CICR_IEN;
+}
+
+/* CPM interrupt controller interrupt.
+*/
+static	void
+cpm_interrupt(int irq, void * dev, struct pt_regs * regs)
+{
+	uint	vec;
+
+	/* Get the vector by setting the ACK bit and then reading
+	 * the register.
+	 */
+	((volatile immap_t *)MBX_IMAP_ADDR)->im_cpic.cpic_civr = 1;
+	vec = ((volatile immap_t *)MBX_IMAP_ADDR)->im_cpic.cpic_civr;
+	vec >>= 11;
+
+	if (cpm_vecs[vec].handler != 0)
+		(*cpm_vecs[vec].handler)(cpm_vecs[vec].dev_id);
+	else
+		((immap_t *)MBX_IMAP_ADDR)->im_cpic.cpic_cimr &= ~(1 << vec);
+
+	/* After servicing the interrupt, we have to remove the status
+	 * indicator.
+	 */
+	((immap_t *)MBX_IMAP_ADDR)->im_cpic.cpic_cisr |= (1 << vec);
+	
+}
+
+/* The CPM can generate the error interrupt when there is a race condition
+ * between generating and masking interrupts.  All we have to do is ACK it
+ * and return.  This is a no-op function so we don't need any special
+ * tests in the interrupt handler.
+ */
+static	void
+cpm_error_interrupt(void *dev)
+{
+}
+
+/* Install a CPM interrupt handler.
+*/
+void
+cpm_install_handler(int vec, void (*handler)(void *), void *dev_id)
+{
+	if (cpm_vecs[vec].handler != 0)
+		printk("CPM interrupt %x replacing %x\n",
+			(uint)handler, (uint)cpm_vecs[vec].handler);
+	cpm_vecs[vec].handler = handler;
+	cpm_vecs[vec].dev_id = dev_id;
+	((immap_t *)MBX_IMAP_ADDR)->im_cpic.cpic_cimr |= (1 << vec);
+}
+
+/* Allocate some memory from the dual ported ram.  We may want to
+ * enforce alignment restrictions, but right now everyone is a good
+ * citizen.
+ */
+uint
+mbx_cpm_dpalloc(uint size)
+{
+	uint	retloc;
+
+	if ((dp_alloc_base + size) >= dp_alloc_top)
+		return(CPM_DP_NOSPACE);
+
+	retloc = dp_alloc_base;
+	dp_alloc_base += size;
+
+	return(retloc);
+}
+
+/* We also own one page of host buffer space for the allocation of
+ * UART "fifos" and the like.
+ */
+uint
+mbx_cpm_hostalloc(uint size)
+{
+	uint	retloc;
+
+	if ((host_buffer + size) >= host_end)
+		return(0);
+
+	retloc = host_buffer;
+	host_buffer += size;
+
+	return(retloc);
+}
+
+/* Set a baud rate generator.  This needs lots of work.  There are
+ * four BRGs, any of which can be wired to any channel.
+ * The internal baud rate clock is the system clock divided by 16.
+ * I need to find a way to get this system clock frequency, which is
+ * part of the VPD.......
+ */
+#define BRG_INT_CLK	(40000000/16)
+
+void
+mbx_cpm_setbrg(uint brg, uint rate)
+{
+	volatile uint	*bp;
+
+	/* This is good enough to get SMCs running.....
+	*/
+	bp = (uint *)&cpmp->cp_brgc1;
+	bp += brg;
+	*bp = ((BRG_INT_CLK / rate) << 1) | CPM_BRG_EN;
+}
diff -u --recursive --new-file v2.1.96/linux/arch/ppc/8xx_io/commproc.h linux/arch/ppc/8xx_io/commproc.h
--- v2.1.96/linux/arch/ppc/8xx_io/commproc.h	Wed Dec 31 16:00:00 1969
+++ linux/arch/ppc/8xx_io/commproc.h	Tue Apr 14 17:33:40 1998
@@ -0,0 +1,464 @@
+
+/*
+ * MPC8xx Communication Processor Module.
+ * Copyright (c) 1997 Dan Malek (dmalek@jlc.net)
+ *
+ * This file contains structures and information for the communication
+ * processor channels.  Some CPM control and status is available
+ * throught the MPC8xx internal memory map.  See immap.h for details.
+ * This file only contains what I need for the moment, not the total
+ * CPM capabilities.  I (or someone else) will add definitions as they
+ * are needed.  -- Dan
+ *
+ * On the MBX board, EPPC-Bug loads CPM microcode into the first 512
+ * bytes of the DP RAM and relocates the I2C parameter area to the
+ * IDMA1 space.  The remaining DP RAM is available for buffer descriptors
+ * or other use.
+ */
+#ifndef __CPM_8XX__
+#define __CPM_8XX__
+
+#include <asm/8xx_immap.h>
+
+/* CPM Command register.
+*/
+#define CPM_CR_RST	((ushort)0x8000)
+#define CPM_CR_OPCODE	((ushort)0x0f00)
+#define CPM_CR_CHAN	((ushort)0x00f0)
+#define CPM_CR_FLG	((ushort)0x0001)
+
+/* Some commands (there are more...later)
+*/
+#define CPM_CR_INIT_TRX		((ushort)0x0000)
+#define CPM_CR_INIT_RX		((ushort)0x0001)
+#define CPM_CR_INIT_TX		((ushort)0x0002)
+#define CPM_CR_STOP_TX		((ushort)0x0004)
+#define CPM_CR_RESTART_TX	((ushort)0x0006)
+#define CPM_CR_SET_GADDR	((ushort)0x0008)
+
+/* Channel numbers.
+*/
+#define CPM_CR_CH_SCC1	((ushort)0x0000)
+#define CPM_CR_CH_I2C	((ushort)0x0001)	/* I2C and IDMA1 */
+#define CPM_CR_CH_SCC2	((ushort)0x0004)
+#define CPM_CR_CH_SPI	((ushort)0x0005)	/* SPI / IDMA2 / Timers */
+#define CPM_CR_CH_SCC3	((ushort)0x0008)
+#define CPM_CR_CH_SMC1	((ushort)0x0009)	/* SMC1 / DSP1 */
+#define CPM_CR_CH_SCC4	((ushort)0x000c)
+#define CPM_CR_CH_SMC2	((ushort)0x000d)	/* SMC2 / DSP2 */
+
+#define mk_cr_cmd(CH, CMD)	((CMD << 8) | (CH << 4))
+
+/* The dual ported RAM is multi-functional.  Some areas can be (and are
+ * being) used for microcode.  There is an area that can only be used
+ * as data ram for buffer descriptors, which is all we use right now.
+ * Currently the first 512 and last 256 bytes are used for microcode.
+ */
+#define CPM_DATAONLY_BASE	((uint)0x0800)
+#define CPM_DATAONLY_SIZE	((uint)0x0700)
+#define CPM_DP_NOSPACE		((uint)0x7fffffff)
+
+/* Export the base address of the communication processor registers
+ * and dual port ram.
+ */
+extern	cpm8xx_t	*cpmp;		/* Pointer to comm processor */
+uint		mbx_cpm_dpalloc(uint size);
+uint		mbx_cpm_hostalloc(uint size);
+void		mbx_cpm_setbrg(uint brg, uint rate);
+
+/* Buffer descriptors used by many of the CPM protocols.
+*/
+typedef struct cpm_buf_desc {
+	ushort	cbd_sc;		/* Status and Control */
+	ushort	cbd_datlen;	/* Data length in buffer */
+	uint	cbd_bufaddr;	/* Buffer address in host memory */
+} cbd_t;
+
+#define BD_SC_EMPTY	((ushort)0x8000)	/* Recieve is empty */
+#define BD_SC_READY	((ushort)0x8000)	/* Transmit is ready */
+#define BD_SC_WRAP	((ushort)0x2000)	/* Last buffer descriptor */
+#define BD_SC_INTRPT	((ushort)0x1000)	/* Interrupt on change */
+#define BD_SC_CM	((ushort)0x0200)	/* Continous mode */
+#define BD_SC_ID	((ushort)0x0100)	/* Rec'd too many idles */
+#define BD_SC_P		((ushort)0x0100)	/* xmt preamble */
+#define BD_SC_BR	((ushort)0x0020)	/* Break received */
+#define BD_SC_FR	((ushort)0x0010)	/* Framing error */
+#define BD_SC_PR	((ushort)0x0008)	/* Parity error */
+#define BD_SC_OV	((ushort)0x0002)	/* Overrun */
+#define BD_SC_CD	((ushort)0x0001)	/* ?? */
+
+/* Define enough so I can at least use the MBX serial port as a UART.
+ * The MBX uses SMC1 as the host serial port.
+ */
+typedef struct smc_uart {
+	ushort	smc_rbase;	/* Rx Buffer descriptor base address */
+	ushort	smc_tbase;	/* Tx Buffer descriptor base address */
+	u_char	smc_rfcr;	/* Rx function code */
+	u_char	smc_tfcr;	/* Tx function code */
+	ushort	smc_mrblr;	/* Max receive buffer length */
+	uint	smc_rstate;	/* Internal */
+	uint	smc_idp;	/* Internal */
+	ushort	smc_rbptr;	/* Internal */
+	ushort	smc_ibc;	/* Internal */
+	uint	smc_rxtmp;	/* Internal */
+	uint	smc_tstate;	/* Internal */
+	uint	smc_tdp;	/* Internal */
+	ushort	smc_tbptr;	/* Internal */
+	ushort	smc_tbc;	/* Internal */
+	uint	smc_txtmp;	/* Internal */
+	ushort	smc_maxidl;	/* Maximum idle characters */
+	ushort	smc_tmpidl;	/* Temporary idle counter */
+	ushort	smc_brklen;	/* Last received break length */
+	ushort	smc_brkec;	/* rcv'd break condition counter */
+	ushort	smc_brkcr;	/* xmt break count register */
+	ushort	smc_rmask;	/* Temporary bit mask */
+} smc_uart_t;
+
+#define PROFF_SMC1	((uint)0x0280)	/* Offset in Parameter RAM */
+#define PROFF_SMC2	((uint)0x0380)
+
+/* Function code bits.
+*/
+#define SMC_EB	((u_char)0x10)	/* Set big endian byte order */
+
+/* SMC uart mode register.
+*/
+#define	SMCMR_REN	((ushort)0x0001)
+#define SMCMR_TEN	((ushort)0x0002)
+#define SMCMR_DM	((ushort)0x000c)
+#define SMCMR_SM_GCI	((ushort)0x0000)
+#define SMCMR_SM_UART	((ushort)0x0020)
+#define SMCMR_SM_TRANS	((ushort)0x0030)
+#define SMCMR_SM_MASK	((ushort)0x0030)
+#define SMCMR_PM_EVEN	((ushort)0x0100)	/* Even parity, else odd */
+#define SMCMR_PEN	((ushort)0x0200)	/* Parity enable */
+#define SMCMR_SL	((ushort)0x0400)	/* Two stops, else one */
+#define SMCR_CLEN_MASK	((ushort)0x7800)	/* Character length */
+#define smcr_mk_clen(C)	(((C) << 11) & SMCR_CLEN_MASK)
+
+/* SMC Event and Mask register.
+*/
+#define	SMCM_TXE	((unsigned char)0x10)
+#define	SMCM_BSY	((unsigned char)0x14)
+#define	SMCM_TX		((unsigned char)0x02)
+#define	SMCM_RX		((unsigned char)0x01)
+
+/* Baud rate generators.
+*/
+#define CPM_BRG_RST		((uint)0x00020000)
+#define CPM_BRG_EN		((uint)0x00010000)
+#define CPM_BRG_EXTC_INT	((uint)0x00000000)
+#define CPM_BRG_EXTC_CLK2	((uint)0x00004000)
+#define CPM_BRG_EXTC_CLK6	((uint)0x00008000)
+#define CPM_BRG_ATB		((uint)0x00002000)
+#define CPM_BRG_CD_MASK		((uint)0x00001ffe)
+#define CPM_BRG_DIV16		((uint)0x00000001)
+
+/* SCCs.
+*/
+#define SCC_GSMRH_IRP		((uint)0x00040000)
+#define SCC_GSMRH_GDE		((uint)0x00010000)
+#define SCC_GSMRH_TCRC_CCITT	((uint)0x00008000)
+#define SCC_GSMRH_TCRC_BISYNC	((uint)0x00004000)
+#define SCC_GSMRH_TCRC_HDLC	((uint)0x00000000)
+#define SCC_GSMRH_REVD		((uint)0x00002000)
+#define SCC_GSMRH_TRX		((uint)0x00001000)
+#define SCC_GSMRH_TTX		((uint)0x00000800)
+#define SCC_GSMRH_CDP		((uint)0x00000400)
+#define SCC_GSMRH_CTSP		((uint)0x00000200)
+#define SCC_GSMRH_CDS		((uint)0x00000100)
+#define SCC_GSMRH_CTSS		((uint)0x00000080)
+#define SCC_GSMRH_TFL		((uint)0x00000040)
+#define SCC_GSMRH_RFW		((uint)0x00000020)
+#define SCC_GSMRH_TXSY		((uint)0x00000010)
+#define SCC_GSMRH_SYNL16	((uint)0x0000000c)
+#define SCC_GSMRH_SYNL8		((uint)0x00000008)
+#define SCC_GSMRH_SYNL4		((uint)0x00000004)
+#define SCC_GSMRH_RTSM		((uint)0x00000002)
+#define SCC_GSMRH_RSYN		((uint)0x00000001)
+
+#define SCC_GSMRL_SIR		((uint)0x80000000)	/* SCC2 only */
+#define SCC_GSMRL_EDGE_NONE	((uint)0x60000000)
+#define SCC_GSMRL_EDGE_NEG	((uint)0x40000000)
+#define SCC_GSMRL_EDGE_POS	((uint)0x20000000)
+#define SCC_GSMRL_EDGE_BOTH	((uint)0x00000000)
+#define SCC_GSMRL_TCI		((uint)0x10000000)
+#define SCC_GSMRL_TSNC_3	((uint)0x0c000000)
+#define SCC_GSMRL_TSNC_4	((uint)0x08000000)
+#define SCC_GSMRL_TSNC_14	((uint)0x04000000)
+#define SCC_GSMRL_TSNC_INF	((uint)0x00000000)
+#define SCC_GSMRL_RINV		((uint)0x02000000)
+#define SCC_GSMRL_TINV		((uint)0x01000000)
+#define SCC_GSMRL_TPL_128	((uint)0x00c00000)
+#define SCC_GSMRL_TPL_64	((uint)0x00a00000)
+#define SCC_GSMRL_TPL_48	((uint)0x00800000)
+#define SCC_GSMRL_TPL_32	((uint)0x00600000)
+#define SCC_GSMRL_TPL_16	((uint)0x00400000)
+#define SCC_GSMRL_TPL_8		((uint)0x00200000)
+#define SCC_GSMRL_TPL_NONE	((uint)0x00000000)
+#define SCC_GSMRL_TPP_ALL1	((uint)0x00180000)
+#define SCC_GSMRL_TPP_01	((uint)0x00100000)
+#define SCC_GSMRL_TPP_10	((uint)0x00080000)
+#define SCC_GSMRL_TPP_ZEROS	((uint)0x00000000)
+#define SCC_GSMRL_TEND		((uint)0x00040000)
+#define SCC_GSMRL_TDCR_32	((uint)0x00030000)
+#define SCC_GSMRL_TDCR_16	((uint)0x00020000)
+#define SCC_GSMRL_TDCR_8	((uint)0x00010000)
+#define SCC_GSMRL_TDCR_1	((uint)0x00000000)
+#define SCC_GSMRL_RDCR_32	((uint)0x0000c000)
+#define SCC_GSMRL_RDCR_16	((uint)0x00008000)
+#define SCC_GSMRL_RDCR_8	((uint)0x00004000)
+#define SCC_GSMRL_RDCR_1	((uint)0x00000000)
+#define SCC_GSMRL_RENC_DFMAN	((uint)0x00003000)
+#define SCC_GSMRL_RENC_MANCH	((uint)0x00002000)
+#define SCC_GSMRL_RENC_FM0	((uint)0x00001000)
+#define SCC_GSMRL_RENC_NRZI	((uint)0x00000800)
+#define SCC_GSMRL_RENC_NRZ	((uint)0x00000000)
+#define SCC_GSMRL_TENC_DFMAN	((uint)0x00000600)
+#define SCC_GSMRL_TENC_MANCH	((uint)0x00000400)
+#define SCC_GSMRL_TENC_FM0	((uint)0x00000200)
+#define SCC_GSMRL_TENC_NRZI	((uint)0x00000100)
+#define SCC_GSMRL_TENC_NRZ	((uint)0x00000000)
+#define SCC_GSMRL_DIAG_LE	((uint)0x000000c0)	/* Loop and echo */
+#define SCC_GSMRL_DIAG_ECHO	((uint)0x00000080)
+#define SCC_GSMRL_DIAG_LOOP	((uint)0x00000040)
+#define SCC_GSMRL_DIAG_NORM	((uint)0x00000000)
+#define SCC_GSMRL_ENR		((uint)0x00000020)
+#define SCC_GSMRL_ENT		((uint)0x00000010)
+#define SCC_GSMRL_MODE_ENET	((uint)0x0000000c)
+#define SCC_GSMRL_MODE_DDCMP	((uint)0x00000009)
+#define SCC_GSMRL_MODE_BISYNC	((uint)0x00000008)
+#define SCC_GSMRL_MODE_V14	((uint)0x00000007)
+#define SCC_GSMRL_MODE_AHDLC	((uint)0x00000006)
+#define SCC_GSMRL_MODE_PROFIBUS	((uint)0x00000005)
+#define SCC_GSMRL_MODE_UART	((uint)0x00000004)
+#define SCC_GSMRL_MODE_SS7	((uint)0x00000003)
+#define SCC_GSMRL_MODE_ATALK	((uint)0x00000002)
+#define SCC_GSMRL_MODE_HDLC	((uint)0x00000000)
+
+#define SCC_TODR_TOD		((ushort)0x8000)
+
+typedef struct scc_param {
+	ushort	scc_rbase;	/* Rx Buffer descriptor base address */
+	ushort	scc_tbase;	/* Tx Buffer descriptor base address */
+	u_char	scc_rfcr;	/* Rx function code */
+	u_char	scc_tfcr;	/* Tx function code */
+	ushort	scc_mrblr;	/* Max receive buffer length */
+	uint	scc_rstate;	/* Internal */
+	uint	scc_idp;	/* Internal */
+	ushort	scc_rbptr;	/* Internal */
+	ushort	scc_ibc;	/* Internal */
+	uint	scc_rxtmp;	/* Internal */
+	uint	scc_tstate;	/* Internal */
+	uint	scc_tdp;	/* Internal */
+	ushort	scc_tbptr;	/* Internal */
+	ushort	scc_tbc;	/* Internal */
+	uint	scc_txtmp;	/* Internal */
+	uint	scc_rcrc;	/* Internal */
+	uint	scc_tcrc;	/* Internal */
+} sccp_t;
+
+/* Function code bits.
+*/
+#define SCC_EB	((u_char)0x10)	/* Set big endian byte order */
+
+/* CPM Ethernet through SCC1.
+ */
+typedef struct scc_enet {
+	sccp_t	sen_genscc;
+	uint	sen_cpres;	/* Preset CRC */
+	uint	sen_cmask;	/* Constant mask for CRC */
+	uint	sen_crcec;	/* CRC Error counter */
+	uint	sen_alec;	/* alignment error counter */
+	uint	sen_disfc;	/* discard frame counter */
+	ushort	sen_pads;	/* Tx short frame pad character */
+	ushort	sen_retlim;	/* Retry limit threshold */
+	ushort	sen_retcnt;	/* Retry limit counter */
+	ushort	sen_maxflr;	/* maximum frame length register */
+	ushort	sen_minflr;	/* minimum frame length register */
+	ushort	sen_maxd1;	/* maximum DMA1 length */
+	ushort	sen_maxd2;	/* maximum DMA2 length */
+	ushort	sen_maxd;	/* Rx max DMA */
+	ushort	sen_dmacnt;	/* Rx DMA counter */
+	ushort	sen_maxb;	/* Max BD byte count */
+	ushort	sen_gaddr1;	/* Group address filter */
+	ushort	sen_gaddr2;
+	ushort	sen_gaddr3;
+	ushort	sen_gaddr4;
+	uint	sen_tbuf0data0;	/* Save area 0 - current frame */
+	uint	sen_tbuf0data1;	/* Save area 1 - current frame */
+	uint	sen_tbuf0rba;	/* Internal */
+	uint	sen_tbuf0crc;	/* Internal */
+	ushort	sen_tbuf0bcnt;	/* Internal */
+	ushort	sen_paddrh;	/* physical address (MSB) */
+	ushort	sen_paddrm;
+	ushort	sen_paddrl;	/* physical address (LSB) */
+	ushort	sen_pper;	/* persistence */
+	ushort	sen_rfbdptr;	/* Rx first BD pointer */
+	ushort	sen_tfbdptr;	/* Tx first BD pointer */
+	ushort	sen_tlbdptr;	/* Tx last BD pointer */
+	uint	sen_tbuf1data0;	/* Save area 0 - current frame */
+	uint	sen_tbuf1data1;	/* Save area 1 - current frame */
+	uint	sen_tbuf1rba;	/* Internal */
+	uint	sen_tbuf1crc;	/* Internal */
+	ushort	sen_tbuf1bcnt;	/* Internal */
+	ushort	sen_txlen;	/* Tx Frame length counter */
+	ushort	sen_iaddr1;	/* Individual address filter */
+	ushort	sen_iaddr2;
+	ushort	sen_iaddr3;
+	ushort	sen_iaddr4;
+	ushort	sen_boffcnt;	/* Backoff counter */
+
+	/* NOTE: Some versions of the manual have the following items
+	 * incorrectly documented.  Below is the proper order.
+	 */
+	ushort	sen_taddrh;	/* temp address (MSB) */
+	ushort	sen_taddrm;
+	ushort	sen_taddrl;	/* temp address (LSB) */
+} scc_enet_t;
+
+#define PROFF_SCC1	((uint)0x0000)	/* Offset in Parameter RAM */
+
+/* Bits in parallel I/O port registers that have to be set/cleared
+ * to configure the pins for SCC1 use.  The TCLK and RCLK seem unique
+ * to the MBX860 board.  Any two of the four available clocks could be
+ * used, and the MPC860 cookbook manual has an example using different
+ * clock pins.
+ */
+#define PA_ENET_RXD	((ushort)0x0001)
+#define PA_ENET_TXD	((ushort)0x0002)
+#define PA_ENET_TCLK	((ushort)0x0200)
+#define PA_ENET_RCLK	((ushort)0x0800)
+#define PC_ENET_TENA	((ushort)0x0001)
+#define PC_ENET_CLSN	((ushort)0x0010)
+#define PC_ENET_RENA	((ushort)0x0020)
+
+/* Control bits in the SICR to route TCLK (CLK2) and RCLK (CLK4) to
+ * SCC1.  Also, make sure GR1 (bit 24) and SC1 (bit 25) are zero.
+ */
+#define SICR_ENET_MASK	((uint)0x000000ff)
+#define SICR_ENET_CLKRT	((uint)0x0000003d)
+
+/* SCC Event register as used by Ethernet.
+*/
+#define SCCE_ENET_GRA	((ushort)0x0080)	/* Graceful stop complete */
+#define SCCE_ENET_TXE	((ushort)0x0010)	/* Transmit Error */
+#define SCCE_ENET_RXF	((ushort)0x0008)	/* Full frame received */
+#define SCCE_ENET_BSY	((ushort)0x0004)	/* All incoming buffers full */
+#define SCCE_ENET_TXB	((ushort)0x0002)	/* A buffer was transmitted */
+#define SCCE_ENET_RXB	((ushort)0x0001)	/* A buffer was received */
+
+/* SCC Mode Register (PMSR) as used by Ethernet.
+*/
+#define SCC_PMSR_HBC	((ushort)0x8000)	/* Enable heartbeat */
+#define SCC_PMSR_FC	((ushort)0x4000)	/* Force collision */
+#define SCC_PMSR_RSH	((ushort)0x2000)	/* Receive short frames */
+#define SCC_PMSR_IAM	((ushort)0x1000)	/* Check individual hash */
+#define SCC_PMSR_ENCRC	((ushort)0x0800)	/* Ethernet CRC mode */
+#define SCC_PMSR_PRO	((ushort)0x0200)	/* Promiscuous mode */
+#define SCC_PMSR_BRO	((ushort)0x0100)	/* Catch broadcast pkts */
+#define SCC_PMSR_SBT	((ushort)0x0080)	/* Special backoff timer */
+#define SCC_PMSR_LPB	((ushort)0x0040)	/* Set Loopback mode */
+#define SCC_PMSR_SIP	((ushort)0x0020)	/* Sample Input Pins */
+#define SCC_PMSR_LCW	((ushort)0x0010)	/* Late collision window */
+#define SCC_PMSR_NIB22	((ushort)0x000a)	/* Start frame search */
+#define SCC_PMSR_FDE	((ushort)0x0001)	/* Full duplex enable */
+
+/* Buffer descriptor control/status used by Ethernet receive.
+*/
+#define BD_ENET_RX_EMPTY	((ushort)0x8000)
+#define BD_ENET_RX_WRAP		((ushort)0x2000)
+#define BD_ENET_RX_INTR		((ushort)0x1000)
+#define BD_ENET_RX_LAST		((ushort)0x0800)
+#define BD_ENET_RX_FIRST	((ushort)0x0400)
+#define BD_ENET_RX_MISS		((ushort)0x0100)
+#define BD_ENET_RX_LG		((ushort)0x0020)
+#define BD_ENET_RX_NO		((ushort)0x0010)
+#define BD_ENET_RX_SH		((ushort)0x0008)
+#define BD_ENET_RX_CR		((ushort)0x0004)
+#define BD_ENET_RX_OV		((ushort)0x0002)
+#define BD_ENET_RX_CL		((ushort)0x0001)
+#define BD_ENET_RX_STATS	((ushort)0x013f)	/* All status bits */
+
+/* Buffer descriptor control/status used by Ethernet transmit.
+*/
+#define BD_ENET_TX_READY	((ushort)0x8000)
+#define BD_ENET_TX_PAD		((ushort)0x4000)
+#define BD_ENET_TX_WRAP		((ushort)0x2000)
+#define BD_ENET_TX_INTR		((ushort)0x1000)
+#define BD_ENET_TX_LAST		((ushort)0x0800)
+#define BD_ENET_TX_TC		((ushort)0x0400)
+#define BD_ENET_TX_DEF		((ushort)0x0200)
+#define BD_ENET_TX_HB		((ushort)0x0100)
+#define BD_ENET_TX_LC		((ushort)0x0080)
+#define BD_ENET_TX_RL		((ushort)0x0040)
+#define BD_ENET_TX_RCMASK	((ushort)0x003c)
+#define BD_ENET_TX_UN		((ushort)0x0002)
+#define BD_ENET_TX_CSL		((ushort)0x0001)
+#define BD_ENET_TX_STATS	((ushort)0x03ff)	/* All status bits */
+
+/* SCC Event and Mask registers when it is used as a UART.
+*/
+#define UART_SCCM_GLR		((ushort)0x1000)
+#define UART_SCCM_GLT		((ushort)0x0800)
+#define UART_SCCM_AB		((ushort)0x0200)
+#define UART_SCCM_IDL		((ushort)0x0100)
+#define UART_SCCM_GRA		((ushort)0x0080)
+#define UART_SCCM_BRKE		((ushort)0x0040)
+#define UART_SCCM_BRKS		((ushort)0x0020)
+#define UART_SCCM_CCR		((ushort)0x0008)
+#define UART_SCCM_BSY		((ushort)0x0004)
+#define UART_SCCM_TX		((ushort)0x0002)
+#define UART_SCCM_RX		((ushort)0x0001)
+
+/* CPM interrupts.  There are nearly 32 interrupts generated by CPM
+ * channels or devices.  All of these are presented to the PPC core
+ * as a single interrupt.  The CPM interrupt handler dispatches its
+ * own handlers, in a similar fashion to the PPC core handler.  We
+ * use the table as defined in the manuals (i.e. no special high
+ * priority and SCC1 == SCCa, etc...).
+ */
+#define CPMVEC_NR		32
+#define	CPMVEC_PIO_PC15		((ushort)0x1f)
+#define	CPMVEC_SCC1		((ushort)0x1e)
+#define	CPMVEC_SCC2		((ushort)0x1d)
+#define	CPMVEC_SCC3		((ushort)0x1c)
+#define	CPMVEC_SCC4		((ushort)0x1b)
+#define	CPMVEC_PIO_PC14		((ushort)0x1a)
+#define	CPMVEC_TIMER1		((ushort)0x19)
+#define	CPMVEC_PIO_PC13		((ushort)0x18)
+#define	CPMVEC_PIO_PC12		((ushort)0x17)
+#define	CPMVEC_SDMA_CB_ERR	((ushort)0x16)
+#define CPMVEC_IDMA1		((ushort)0x15)
+#define CPMVEC_IDMA2		((ushort)0x14)
+#define CPMVEC_TIMER2		((ushort)0x12)
+#define CPMVEC_RISCTIMER	((ushort)0x11)
+#define CPMVEC_I2C		((ushort)0x10)
+#define	CPMVEC_PIO_PC11		((ushort)0x0f)
+#define	CPMVEC_PIO_PC10		((ushort)0x0e)
+#define CPMVEC_TIMER3		((ushort)0x0c)
+#define	CPMVEC_PIO_PC9		((ushort)0x0b)
+#define	CPMVEC_PIO_PC8		((ushort)0x0a)
+#define	CPMVEC_PIO_PC7		((ushort)0x09)
+#define CPMVEC_TIMER4		((ushort)0x07)
+#define	CPMVEC_PIO_PC6		((ushort)0x06)
+#define	CPMVEC_SPI		((ushort)0x05)
+#define	CPMVEC_SMC1		((ushort)0x04)
+#define	CPMVEC_SMC2		((ushort)0x03)
+#define	CPMVEC_PIO_PC5		((ushort)0x02)
+#define	CPMVEC_PIO_PC4		((ushort)0x01)
+#define	CPMVEC_ERROR		((ushort)0x00)
+
+extern void cpm_install_handler(int vec, void (*handler)(void *), void *dev_id);
+
+/* CPM interrupt configuration vector.
+*/
+#define	CICR_SCD_SCC4		((uint)0x00c00000)	/* SCC4 @ SCCd */
+#define	CICR_SCC_SCC3		((uint)0x00200000)	/* SCC3 @ SCCc */
+#define	CICR_SCB_SCC2		((uint)0x00040000)	/* SCC2 @ SCCb */
+#define	CICR_SCA_SCC1		((uint)0x00000000)	/* SCC1 @ SCCa */
+#define CICR_IRL_MASK		((uint)0x0000e000)	/* Core interrrupt */
+#define CICR_HP_MASK		((uint)0x00001f00)	/* Hi-pri int. */
+#define CICR_IEN		((uint)0x00000080)	/* Int. enable */
+#define CICR_SPS		((uint)0x00000001)	/* SCC Spread */
+#endif /* __CPM_8XX__ */
diff -u --recursive --new-file v2.1.96/linux/arch/ppc/8xx_io/enet.c linux/arch/ppc/8xx_io/enet.c
--- v2.1.96/linux/arch/ppc/8xx_io/enet.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/ppc/8xx_io/enet.c	Tue Apr 14 17:33:40 1998
@@ -0,0 +1,913 @@
+/*
+ * Ethernet driver for Motorola MPC8xx.
+ * Copyright (c) 1997 Dan Malek (dmalek@jlc.net)
+ *
+ * I copied the basic skeleton from the lance driver, because I did not
+ * know how to write the Linux driver, but I did know how the LANCE worked.
+ * This version of the driver is specific to the MBX implementation,
+ * since the board contains control registers external to the processor
+ * for the control of the MC68160 SIA/transceiver.  The MPC860 manual
+ * describes connections using the internal parallel port I/O.
+ *
+ * The MBX860 uses the CPM SCC1 serial port for the Ethernet interface.
+ * Buffer descriptors are kept in the CPM dual port RAM, and the frame
+ * buffers are in the host memory.
+ *
+ * Right now, I am very watseful with the buffers.  I allocate memory
+ * pages and then divide them into 2K frame buffers.  This way I know I
+ * have buffers large enough to hold one frame within one buffer descriptor.
+ * Once I get this working, I will use 64 or 128 byte CPM buffers, which
+ * will be much more memory efficient and will easily handle lots of
+ * small packets.
+ *
+ */
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/malloc.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/bios32.h>
+#include <linux/init.h>
+#include <asm/bitops.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include <asm/8xx_immap.h>
+#include <asm/pgtable.h>
+#include <asm/mbx.h>
+#include "commproc.h"
+#include <linux/delay.h>
+
+/*
+ *				Theory of Operation
+ *
+ * The MPC8xx CPM performs the Ethernet processing on SCC1.  It can use
+ * an aribtrary number of buffers on byte boundaries, but must have at
+ * least two receive buffers to prevent constand overrun conditions.
+ *
+ * The buffer descriptors are allocated from the CPM dual port memory
+ * with the data buffers allocated from host memory, just like all other
+ * serial communication protocols.  The host memory buffers are allocated
+ * from the free page pool, and then divided into smaller receive and
+ * transmit buffers.  The size of the buffers should be a power of two,
+ * since that nicely divides the page.  This creates a ring buffer
+ * structure similar to the LANCE and other controllers.
+ *
+ * Like the LANCE driver:
+ * The driver runs as two independent, single-threaded flows of control.  One
+ * is the send-packet routine, which enforces single-threaded use by the
+ * dev->tbusy flag.  The other thread is the interrupt handler, which is single
+ * threaded by the hardware and other software.
+ *
+ * The send packet thread has partial control over the Tx ring and 'dev->tbusy'
+ * flag.  It sets the tbusy flag whenever it's queuing a Tx packet. If the next
+ * queue slot is empty, it clears the tbusy flag when finished otherwise it sets
+ * the 'lp->tx_full' flag.
+ *
+ * The MBX has a control register external to the MPC8xx that has some
+ * control of the Ethernet interface.  Control Register 1 has the
+ * following format:
+ *	bit 0 - Set to enable Ethernet transceiver
+ *	bit 1 - Set to enable Ethernet internal loopback
+ *	bit 2 - Set to auto select AUI or TP port
+ *	bit 3 - if bit 2 is 0, set to select TP port
+ *	bit 4 - Set to disable full duplex (loopback)
+ *	bit 5 - Set to disable XCVR collision test
+ *	bit 6, 7 - Used for RS-232 control.
+ *
+ * EPPC-Bug sets this register to 0x98 for normal Ethernet operation,
+ * so we should not have to touch it.
+ *
+ * The following I/O is used by the MBX implementation of the MPC8xx to
+ * the MC68160 transceiver.  It DOES NOT exactly follow the cookbook
+ * example from the MPC860 manual.
+ *	Port A, 15 - SCC1 Ethernet Rx
+ *	Port A, 14 - SCC1 Ethernet Tx
+ *	Port A, 6 (CLK2) - SCC1 Ethernet Tx Clk
+ *	Port A, 4 (CLK4) - SCC1 Ethernet Rx Clk
+ *	Port C, 15 - SCC1 Ethernet Tx Enable
+ *	Port C, 11 - SCC1 Ethernet Collision
+ *	Port C, 10 - SCC1 Ethernet Rx Enable
+ */
+
+/* The number of Tx and Rx buffers.  These are allocated from the page
+ * pool.  The code may assume these are power of two, so it it best
+ * to keep them that size.
+ * We don't need to allocate pages for the transmitter.  We just use
+ * the skbuffer directly.
+ */
+#define CPM_ENET_RX_PAGES	4
+#define CPM_ENET_RX_FRSIZE	2048
+#define CPM_ENET_RX_FRPPG	(PAGE_SIZE / CPM_ENET_RX_FRSIZE)
+#define RX_RING_SIZE		(CPM_ENET_RX_FRPPG * CPM_ENET_RX_PAGES)
+#define TX_RING_SIZE		8	/* Must be power of two */
+#define TX_RING_MOD_MASK	7	/*   for this to work */
+
+/* The CPM stores dest/src/type, data, and checksum for receive packets.
+ */
+#define PKT_MAXBUF_SIZE		1518
+#define PKT_MINBUF_SIZE		64
+#define PKT_MAXBLR_SIZE		1520
+
+/* The CPM buffer descriptors track the ring buffers.  The rx_bd_base and
+ * tx_bd_base always point to the base of the buffer descriptors.  The
+ * cur_rx and cur_tx point to the currently available buffer.
+ * The dirty_tx tracks the current buffer that is being sent by the
+ * controller.  The cur_tx and dirty_tx are equal under both completely
+ * empty and completely full conditions.  The empty/ready indicator in
+ * the buffer descriptor determines the actual condition.
+ */
+struct cpm_enet_private {
+	/* The saved address of a sent-in-place packet/buffer, for skfree(). */
+	struct	sk_buff* tx_skbuff[TX_RING_SIZE];
+	ushort	skb_cur;
+	ushort	skb_dirty;
+
+	/* CPM dual port RAM relative addresses.
+	*/
+	cbd_t	*rx_bd_base;		/* Address of Rx and Tx buffers. */
+	cbd_t	*tx_bd_base;
+	cbd_t	*cur_rx, *cur_tx;		/* The next free ring entry */
+	cbd_t	*dirty_tx;	/* The ring entries to be free()ed. */
+	scc_t	*sccp;
+	struct	net_device_stats stats;
+	char	tx_full;
+	unsigned long lock;
+};
+
+static int cpm_enet_open(struct device *dev);
+static int cpm_enet_start_xmit(struct sk_buff *skb, struct device *dev);
+static int cpm_enet_rx(struct device *dev);
+static void cpm_enet_interrupt(void *dev_id);
+static int cpm_enet_close(struct device *dev);
+static struct net_device_stats *cpm_enet_get_stats(struct device *dev);
+static void set_multicast_list(struct device *dev);
+
+/* GET THIS FROM THE VPD!!!!
+*/
+static	ushort	my_enet_addr[] = { 0x0800, 0x3e26, 0x1559 };
+
+/* Initialize the CPM Ethernet on SCC1.  If EPPC-Bug loaded us, or performed
+ * some other network I/O, a whole bunch of this has already been set up.
+ * It is no big deal if we do it again, we just have to disable the
+ * transmit and receive to make sure we don't catch the CPM with some
+ * inconsistent control information.
+ */
+__initfunc(int cpm_enet_init(void))
+{
+	struct device *dev;
+	struct cpm_enet_private *cep;
+	int i, j;
+	unsigned char	*eap;
+	unsigned long	mem_addr;
+	pte_t		*pte;
+	volatile	cbd_t		*bdp;
+	volatile	cpm8xx_t	*cp;
+	volatile	scc_t		*sccp;
+	volatile	scc_enet_t	*ep;
+	volatile	immap_t		*immap;
+
+	cp = cpmp;	/* Get pointer to Communication Processor */
+
+	immap = (immap_t *)MBX_IMAP_ADDR;	/* and to internal registers */
+
+	/* Allocate some private information.
+	*/
+	cep = (struct cpm_enet_private *)kmalloc(sizeof(*cep), GFP_KERNEL);
+	memset(cep, 0, sizeof(*cep));
+
+	/* Create an Ethernet device instance.
+	*/
+	dev = init_etherdev(0, 0);
+
+	/* Get pointer to SCC1 area in parameter RAM.
+	*/
+	ep = (scc_enet_t *)(&cp->cp_dparam[PROFF_SCC1]);
+
+	/* And another to the SCC register area.
+	*/
+	sccp = (volatile scc_t *)(&cp->cp_scc[0]);
+	cep->sccp = (scc_t *)sccp;		/* Keep the pointer handy */
+
+	/* Disable receive and transmit in case EPPC-Bug started it.
+	*/
+	sccp->scc_gsmrl &= ~(SCC_GSMRL_ENR | SCC_GSMRL_ENT);
+
+	/* Cookbook style from the MPC860 manual.....
+	 * Not all of this is necessary if EPPC-Bug has initialized
+	 * the network.
+	 */
+
+	/* Configure port A pins for Txd and Rxd.
+	*/
+	immap->im_ioport.iop_papar |= (PA_ENET_RXD | PA_ENET_TXD);
+	immap->im_ioport.iop_padir &= ~(PA_ENET_RXD | PA_ENET_TXD);
+	immap->im_ioport.iop_paodr &= ~PA_ENET_TXD;
+
+	/* Configure port C pins to enable CLSN and RENA.
+	*/
+	immap->im_ioport.iop_pcpar &= ~(PC_ENET_CLSN | PC_ENET_RENA);
+	immap->im_ioport.iop_pcdir &= ~(PC_ENET_CLSN | PC_ENET_RENA);
+	immap->im_ioport.iop_pcso |= (PC_ENET_CLSN | PC_ENET_RENA);
+
+	/* Configure port A for TCLK and RCLK.
+	*/
+	immap->im_ioport.iop_papar |= (PA_ENET_TCLK | PA_ENET_RCLK);
+	immap->im_ioport.iop_padir &= ~(PA_ENET_TCLK | PA_ENET_RCLK);
+
+	/* Configure Serial Interface clock routing.
+	 * First, clear all SCC1 bits to zero, then set the ones we want.
+	 */
+	cp->cp_sicr &= ~SICR_ENET_MASK;
+	cp->cp_sicr |= SICR_ENET_CLKRT;
+
+	/* Manual says set SDDR, but I can't find anything with that
+	 * name.  I think it is a misprint, and should be SDCR.  This
+	 * has already been set by the communication processor initialization.
+	 */
+
+	/* Allocate space for the buffer descriptors in the DP ram.
+	 * These are relative offsets in the DP ram address space.
+	 * Initialize base addresses for the buffer descriptors.
+	 */
+	i = mbx_cpm_dpalloc(sizeof(cbd_t) * RX_RING_SIZE);
+	ep->sen_genscc.scc_rbase = i;
+	cep->rx_bd_base = (cbd_t *)&cp->cp_dpmem[i];
+
+	i = mbx_cpm_dpalloc(sizeof(cbd_t) * TX_RING_SIZE);
+	ep->sen_genscc.scc_tbase = i;
+	cep->tx_bd_base = (cbd_t *)&cp->cp_dpmem[i];
+
+	cep->dirty_tx = cep->cur_tx = cep->tx_bd_base;
+	cep->cur_rx = cep->rx_bd_base;
+
+	/* Issue init Rx BD command for SCC1.
+	 * Manual says to perform an Init Rx parameters here.  We have
+	 * to perform both Rx and Tx because the SCC may have been
+	 * already running.
+	 * In addition, we have to do it later because we don't yet have
+	 * all of the BD control/status set properly.
+	cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SCC1, CPM_CR_INIT_RX) | CPM_CR_FLG;
+	while (cp->cp_cpcr & CPM_CR_FLG);
+	 */
+
+	/* Initialize function code registers for big-endian.
+	*/
+	ep->sen_genscc.scc_rfcr = SCC_EB;
+	ep->sen_genscc.scc_tfcr = SCC_EB;
+
+	/* Set maximum bytes per receive buffer.
+	 * This appears to be an Ethernet frame size, not the buffer
+	 * fragment size.  It must be a multiple of four.
+	 */
+	ep->sen_genscc.scc_mrblr = PKT_MAXBLR_SIZE;
+
+	/* Set CRC preset and mask.
+	*/
+	ep->sen_cpres = 0xffffffff;
+	ep->sen_cmask = 0xdebb20e3;
+
+	ep->sen_crcec = 0;	/* CRC Error counter */
+	ep->sen_alec = 0;	/* alignment error counter */
+	ep->sen_disfc = 0;	/* discard frame counter */
+
+	ep->sen_pads = 0x8888;	/* Tx short frame pad character */
+	ep->sen_retlim = 15;	/* Retry limit threshold */
+
+	ep->sen_maxflr = PKT_MAXBUF_SIZE;   /* maximum frame length register */
+	ep->sen_minflr = PKT_MINBUF_SIZE;  /* minimum frame length register */
+
+	ep->sen_maxd1 = PKT_MAXBUF_SIZE;	/* maximum DMA1 length */
+	ep->sen_maxd2 = PKT_MAXBUF_SIZE;	/* maximum DMA2 length */
+
+	/* Clear hash tables.
+	*/
+	ep->sen_gaddr1 = 0;
+	ep->sen_gaddr2 = 0;
+	ep->sen_gaddr3 = 0;
+	ep->sen_gaddr4 = 0;
+	ep->sen_iaddr1 = 0;
+	ep->sen_iaddr2 = 0;
+	ep->sen_iaddr3 = 0;
+	ep->sen_iaddr4 = 0;
+
+	/* Set Ethernet station address.  This must come from the
+	 * Vital Product Data (VPD) EEPROM.....as soon as I get the
+	 * I2C interface working.....
+	 *
+	 * Since we performed a diskless boot, the Ethernet controller
+	 * has been initialized and we copy the address out into our
+	 * own structure.
+	 */
+#ifdef notdef
+	ep->sen_paddrh = my_enet_addr[0];
+	ep->sen_paddrm = my_enet_addr[1];
+	ep->sen_paddrl = my_enet_addr[2];
+#else
+	eap = (unsigned char *)&(ep->sen_paddrh);
+	for (i=5; i>=0; i--)
+		dev->dev_addr[i] = *eap++;
+#endif
+
+	ep->sen_pper = 0;	/* 'cause the book says so */
+	ep->sen_taddrl = 0;	/* temp address (LSB) */
+	ep->sen_taddrm = 0;
+	ep->sen_taddrh = 0;	/* temp address (MSB) */
+
+	/* Now allocate the host memory pages and initialize the
+	 * buffer descriptors.
+	 */
+	bdp = cep->tx_bd_base;
+	for (i=0; i<TX_RING_SIZE; i++) {
+
+		/* Initialize the BD for every fragment in the page.
+		*/
+		bdp->cbd_sc = 0;
+		bdp->cbd_bufaddr = 0;
+		bdp++;
+	}
+
+	/* Set the last buffer to wrap.
+	*/
+	bdp--;
+	bdp->cbd_sc |= BD_SC_WRAP;
+
+	bdp = cep->rx_bd_base;
+	for (i=0; i<CPM_ENET_RX_PAGES; i++) {
+
+		/* Allocate a page.
+		*/
+		mem_addr = __get_free_page(GFP_KERNEL);
+
+		/* Make it uncached.
+		*/
+		pte = va_to_pte(&init_task, mem_addr);
+		pte_val(*pte) |= _PAGE_NO_CACHE;
+		flush_tlb_page(current->mm->mmap, mem_addr);
+
+		/* Initialize the BD for every fragment in the page.
+		*/
+		for (j=0; j<CPM_ENET_RX_FRPPG; j++) {
+			bdp->cbd_sc = BD_ENET_RX_EMPTY | BD_ENET_RX_INTR;
+			bdp->cbd_bufaddr = __pa(mem_addr);
+			mem_addr += CPM_ENET_RX_FRSIZE;
+			bdp++;
+		}
+	}
+
+	/* Set the last buffer to wrap.
+	*/
+	bdp--;
+	bdp->cbd_sc |= BD_SC_WRAP;
+
+	/* Let's re-initialize the channel now.  We have to do it later
+	 * than the manual describes because we have just now finished
+	 * the BD initialization.
+	 */
+	cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SCC1, CPM_CR_INIT_TRX) | CPM_CR_FLG;
+	while (cp->cp_cpcr & CPM_CR_FLG);
+
+	cep->skb_cur = cep->skb_dirty = 0;
+
+	sccp->scc_scce = 0xffff;	/* Clear any pending events */
+
+	/* Enable interrupts for transmit error, complete frame
+	 * received, and any transmit buffer we have also set the
+	 * interrupt flag.
+	 */
+	sccp->scc_sccm = (SCCE_ENET_TXE | SCCE_ENET_RXF | SCCE_ENET_TXB);
+
+	/* Install our interrupt handler.
+	*/
+	cpm_install_handler(CPMVEC_SCC1, cpm_enet_interrupt, dev);
+
+	/* Set GSMR_H to enable all normal operating modes.
+	 * Set GSMR_L to enable Ethernet to MC68160.
+	 */
+	sccp->scc_gsmrh = 0;
+	sccp->scc_gsmrl = (SCC_GSMRL_TCI | SCC_GSMRL_TPL_48 | SCC_GSMRL_TPP_10 | SCC_GSMRL_MODE_ENET);
+
+	/* Set sync/delimiters.
+	*/
+	sccp->scc_dsr = 0xd555;
+
+	/* Set processing mode.  Use Ethernet CRC, catch broadcast, and
+	 * start frame search 22 bit times after RENA.
+	 */
+	sccp->scc_pmsr = (SCC_PMSR_ENCRC | SCC_PMSR_BRO | SCC_PMSR_NIB22);
+
+	/* It is now OK to enable the Ethernet transmitter.
+	*/
+	immap->im_ioport.iop_pcpar |= PC_ENET_TENA;
+	immap->im_ioport.iop_pcdir &= ~PC_ENET_TENA;
+
+	dev->base_addr = (unsigned long)ep;
+	dev->priv = cep;
+	dev->name = "CPM_ENET";
+
+	/* The CPM Ethernet specific entries in the device structure. */
+	dev->open = cpm_enet_open;
+	dev->hard_start_xmit = cpm_enet_start_xmit;
+	dev->stop = cpm_enet_close;
+	dev->get_stats = cpm_enet_get_stats;
+	dev->set_multicast_list = set_multicast_list;
+
+	/* And last, enable the transmit and receive processing.
+	*/
+	sccp->scc_gsmrl |= (SCC_GSMRL_ENR | SCC_GSMRL_ENT);
+
+	printk("CPM ENET Version 0.1, ");
+	for (i=0; i<5; i++)
+		printk("%02x:", dev->dev_addr[i]);
+	printk("%02x\n", dev->dev_addr[5]);
+
+	return 0;
+}
+
+static int
+cpm_enet_open(struct device *dev)
+{
+
+	/* I should reset the ring buffers here, but I don't yet know
+	 * a simple way to do that.
+	 */
+
+	dev->tbusy = 0;
+	dev->interrupt = 0;
+	dev->start = 1;
+
+	return 0;					/* Always succeed */
+}
+
+static int
+cpm_enet_start_xmit(struct sk_buff *skb, struct device *dev)
+{
+	struct cpm_enet_private *cep = (struct cpm_enet_private *)dev->priv;
+	volatile cbd_t	*bdp;
+	unsigned long flags;
+
+	/* Transmitter timeout, serious problems. */
+	if (dev->tbusy) {
+		int tickssofar = jiffies - dev->trans_start;
+		if (tickssofar < 20)
+			return 1;
+		printk("%s: transmit timed out.\n", dev->name);
+		cep->stats.tx_errors++;
+#ifndef final_version
+		{
+			int	i;
+			cbd_t	*bdp;
+			printk(" Ring data dump: cur_tx %x%s cur_rx %x.\n",
+				   cep->cur_tx, cep->tx_full ? " (full)" : "",
+				   cep->cur_rx);
+			bdp = cep->tx_bd_base;
+			for (i = 0 ; i < TX_RING_SIZE; i++)
+				printk("%04x %04x %08x\n",
+					bdp->cbd_sc,
+					bdp->cbd_datlen,
+					bdp->cbd_bufaddr);
+			bdp = cep->rx_bd_base;
+			for (i = 0 ; i < RX_RING_SIZE; i++)
+				printk("%04x %04x %08x\n",
+					bdp->cbd_sc,
+					bdp->cbd_datlen,
+					bdp->cbd_bufaddr);
+		}
+#endif
+
+		dev->tbusy=0;
+		dev->trans_start = jiffies;
+
+		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. */
+	if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) {
+		printk("%s: Transmitter access conflict.\n", dev->name);
+		return 1;
+	}
+
+	if (test_and_set_bit(0, (void*)&cep->lock) != 0) {
+		printk("%s: tx queue lock!.\n", dev->name);
+		/* don't clear dev->tbusy flag. */
+		return 1;
+	}
+
+	/* Fill in a Tx ring entry */
+	bdp = cep->cur_tx;
+
+#ifndef final_version
+	if (bdp->cbd_sc & BD_ENET_TX_READY) {
+		/* Ooops.  All transmit buffers are full.  Bail out.
+		 * This should not happen, since dev->tbusy should be set.
+		 */
+		printk("%s: tx queue full!.\n", dev->name);
+		cep->lock = 0;
+		return 1;
+	}
+#endif
+
+	/* Clear all of the status flags.
+	 */
+	bdp->cbd_sc &= ~BD_ENET_TX_STATS;
+
+	/* If the frame is short, tell CPM to pad it.
+	*/
+	if (skb->len <= ETH_ZLEN)
+		bdp->cbd_sc |= BD_ENET_TX_PAD;
+	else
+		bdp->cbd_sc &= ~BD_ENET_TX_PAD;
+
+	/* Set buffer length and buffer pointer.
+	*/
+	bdp->cbd_datlen = skb->len;
+	bdp->cbd_bufaddr = __pa(skb->data);
+
+	/* Save skb pointer.
+	*/
+	cep->tx_skbuff[cep->skb_cur] = skb;
+
+	cep->stats.tx_bytes += skb->len;
+	cep->skb_cur = (cep->skb_cur+1) & TX_RING_MOD_MASK;
+	
+	/* Push the data cache so the CPM does not get stale memory
+	 * data.
+	 */
+	/*flush_dcache_range(skb->data, skb->data + skb->len);*/
+
+	/* Send it on its way.  Tell CPM its ready, interrupt when done,
+	 * its the last BD of the frame, and to put the CRC on the end.
+	 */
+	bdp->cbd_sc |= (BD_ENET_TX_READY | BD_ENET_TX_INTR | BD_ENET_TX_LAST | BD_ENET_TX_TC);
+
+	dev->trans_start = jiffies;
+
+	/* If this was the last BD in the ring, start at the beginning again.
+	*/
+	if (bdp->cbd_sc & BD_ENET_TX_WRAP)
+		bdp = cep->tx_bd_base;
+	else
+		bdp++;
+
+	save_flags(flags);
+	cli();
+	cep->lock = 0;
+	if (bdp->cbd_sc & BD_ENET_TX_READY)
+		cep->tx_full = 1;
+	else
+		dev->tbusy=0;
+	restore_flags(flags);
+
+	cep->cur_tx = (cbd_t *)bdp;
+
+	return 0;
+}
+
+/* The interrupt handler.
+ * This is called from the CPM handler, not the MPC core interrupt.
+ */
+static void
+cpm_enet_interrupt(void *dev_id)
+{
+	struct	device *dev = dev_id;
+	struct	cpm_enet_private *cep;
+	volatile cbd_t	*bdp;
+	ushort	int_events;
+	int	must_restart;
+
+	cep = (struct cpm_enet_private *)dev->priv;
+	if (dev->interrupt)
+		printk("%s: Re-entering the interrupt handler.\n", dev->name);
+
+	dev->interrupt = 1;
+
+	/* Get the interrupt events that caused us to be here.
+	*/
+	int_events = cep->sccp->scc_scce;
+	must_restart = 0;
+
+	/* Handle receive event in its own function.
+	*/
+	if (int_events & SCCE_ENET_RXF)
+		cpm_enet_rx(dev_id);
+
+	/* Check for a transmit error.  The manual is a little unclear
+	 * about this, so the debug code until I get it figured out.  It
+	 * appears that if TXE is set, then TXB is not set.  However,
+	 * if carrier sense is lost during frame transmission, the TXE
+	 * bit is set, "and continues the buffer transmission normally."
+	 * I don't know if "normally" implies TXB is set when the buffer
+	 * descriptor is closed.....trial and error :-).
+	 */
+	if (int_events & SCCE_ENET_TXE) {
+
+		/* Transmission errors.
+		*/
+		bdp = cep->dirty_tx;
+#ifndef final_version
+		printk("CPM ENET xmit error %x\n", bdp->cbd_sc);
+		if (bdp->cbd_sc & BD_ENET_TX_READY)
+			printk("HEY! Enet xmit interrupt and TX_READY.\n");
+#endif
+		if (bdp->cbd_sc & BD_ENET_TX_HB)	/* No heartbeat */
+			cep->stats.tx_heartbeat_errors++;
+		if (bdp->cbd_sc & BD_ENET_TX_LC)	/* Late collision */
+			cep->stats.tx_window_errors++;
+		if (bdp->cbd_sc & BD_ENET_TX_RL)	/* Retrans limit */
+			cep->stats.tx_aborted_errors++;
+		if (bdp->cbd_sc & BD_ENET_TX_UN)	/* Underrun */
+			cep->stats.tx_fifo_errors++;
+		if (bdp->cbd_sc & BD_ENET_TX_CSL)	/* Carrier lost */
+			cep->stats.tx_carrier_errors++;
+
+		cep->stats.tx_errors++;
+
+		/* No heartbeat or Lost carrier are not really bad errors.
+		 * The others require a restart transmit command.
+		 */
+		if (bdp->cbd_sc &
+		    (BD_ENET_TX_LC | BD_ENET_TX_RL | BD_ENET_TX_UN))
+			must_restart = 1;
+	}
+
+	/* Transmit OK, or non-fatal error.  Update the buffer descriptors.
+	*/
+	if (int_events & (SCCE_ENET_TXE | SCCE_ENET_TXB)) {
+		cep->stats.tx_packets++;
+		bdp = cep->dirty_tx;
+#ifndef final_version
+		if (bdp->cbd_sc & BD_ENET_TX_READY)
+			printk("HEY! Enet xmit interrupt and TX_READY.\n");
+#endif
+		/* Deferred means some collisions occurred during transmit,
+		 * but we eventually sent the packet OK.
+		 */
+		if (bdp->cbd_sc & BD_ENET_TX_DEF)
+			cep->stats.collisions++;
+
+		/* Free the sk buffer associated with this last transmit.
+		*/
+		dev_kfree_skb(cep->tx_skbuff[cep->skb_dirty]/*, FREE_WRITE*/);
+		cep->skb_dirty = (cep->skb_dirty + 1) & TX_RING_MOD_MASK;
+
+		/* Update pointer to next buffer descriptor to be transmitted.
+		*/
+		if (bdp->cbd_sc & BD_ENET_TX_WRAP)
+			bdp = cep->tx_bd_base;
+		else
+			bdp++;
+
+		/* I don't know if we can be held off from processing these
+		 * interrupts for more than one frame time.  I really hope
+		 * not.  In such a case, we would now want to check the
+		 * currently available BD (cur_tx) and determine if any
+		 * buffers between the dirty_tx and cur_tx have also been
+		 * sent.  We would want to process anything in between that
+		 * does not have BD_ENET_TX_READY set.
+		 */
+
+		/* Since we have freed up a buffer, the ring is no longer
+		 * full.
+		 */
+		if (cep->tx_full && dev->tbusy) {
+			cep->tx_full = 0;
+			dev->tbusy = 0;
+			mark_bh(NET_BH);
+		}
+
+		cep->dirty_tx = (cbd_t *)bdp;
+	}
+
+	if (must_restart) {
+		volatile cpm8xx_t *cp;
+
+		/* Some transmit errors cause the transmitter to shut
+		 * down.  We now issue a restart transmit.  Since the
+		 * errors close the BD and update the pointers, the restart
+		 * _should_ pick up without having to reset any of our
+		 * pointers either.
+		 */
+		cp = cpmp;
+		cp->cp_cpcr =
+		    mk_cr_cmd(CPM_CR_CH_SCC1, CPM_CR_RESTART_TX) | CPM_CR_FLG;
+		while (cp->cp_cpcr & CPM_CR_FLG);
+	}
+
+	/* Check for receive busy, i.e. packets coming but no place to
+	 * put them.  This "can't happen" because the receive interrupt
+	 * is tossing previous frames.
+	 */
+	if (int_events & SCCE_ENET_BSY) {
+		cep->stats.rx_dropped++;
+		printk("CPM ENET: BSY can't happen.\n");
+	}
+
+	/* Write the SCC event register with the events we have handled
+	 * to clear them.  Maybe we should do this sooner?
+	 */
+	cep->sccp->scc_scce = int_events;
+
+	dev->interrupt = 0;
+
+	return;
+}
+
+/* During a receive, the cur_rx points to the current incoming buffer.
+ * When we update through the ring, if the next incoming buffer has
+ * not been given to the system, we just set the empty indicator,
+ * effectively tossing the packet.
+ */
+static int
+cpm_enet_rx(struct device *dev)
+{
+	struct	cpm_enet_private *cep;
+	volatile cbd_t	*bdp;
+	struct	sk_buff *skb;
+	ushort	pkt_len;
+
+	cep = (struct cpm_enet_private *)dev->priv;
+
+	/* First, grab all of the stats for the incoming packet.
+	 * These get messed up if we get called due to a busy condition.
+	 */
+	bdp = cep->cur_rx;
+
+for (;;) {
+	if (bdp->cbd_sc & BD_ENET_RX_EMPTY)
+		break;
+		
+#ifndef final_version
+	/* Since we have allocated space to hold a complete frame, both
+	 * the first and last indicators should be set.
+	 */
+	if ((bdp->cbd_sc & (BD_ENET_RX_FIRST | BD_ENET_RX_LAST)) !=
+		(BD_ENET_RX_FIRST | BD_ENET_RX_LAST))
+			printk("CPM ENET: rcv is not first+last\n");
+#endif
+
+	/* Frame too long or too short.
+	*/
+	if (bdp->cbd_sc & (BD_ENET_RX_LG | BD_ENET_RX_SH))
+		cep->stats.rx_length_errors++;
+	if (bdp->cbd_sc & BD_ENET_RX_NO)	/* Frame alignment */
+		cep->stats.rx_frame_errors++;
+	if (bdp->cbd_sc & BD_ENET_RX_CR)	/* CRC Error */
+		cep->stats.rx_crc_errors++;
+	if (bdp->cbd_sc & BD_ENET_RX_OV)	/* FIFO overrun */
+		cep->stats.rx_crc_errors++;
+
+	/* Report late collisions as a frame error.
+	 * On this error, the BD is closed, but we don't know what we
+	 * have in the buffer.  So, just drop this frame on the floor.
+	 */
+	if (bdp->cbd_sc & BD_ENET_RX_CL) {
+		cep->stats.rx_frame_errors++;
+	}
+	else {
+
+		/* Process the incoming frame.
+		*/
+		cep->stats.rx_packets++;
+		pkt_len = bdp->cbd_datlen;
+		cep->stats.rx_bytes += pkt_len;
+
+		/* This does 16 byte alignment, much more than we need.
+		*/
+		skb = dev_alloc_skb(pkt_len);
+
+		if (skb == NULL) {
+			printk("%s: Memory squeeze, dropping packet.\n", dev->name);
+			cep->stats.rx_dropped++;
+		}
+		else {
+			skb->dev = dev;
+			skb_put(skb,pkt_len);	/* Make room */
+			eth_copy_and_sum(skb,
+				(unsigned char *)__va(bdp->cbd_bufaddr),
+				pkt_len, 0);
+			skb->protocol=eth_type_trans(skb,dev);
+			netif_rx(skb);
+		}
+	}
+
+	/* Clear the status flags for this buffer.
+	*/
+	bdp->cbd_sc &= ~BD_ENET_RX_STATS;
+
+	/* Mark the buffer empty.
+	*/
+	bdp->cbd_sc |= BD_ENET_RX_EMPTY;
+
+	/* Update BD pointer to next entry.
+	*/
+	if (bdp->cbd_sc & BD_ENET_RX_WRAP)
+		bdp = cep->rx_bd_base;
+	else
+		bdp++;
+
+   }
+	cep->cur_rx = (cbd_t *)bdp;
+
+	return 0;
+}
+
+static int
+cpm_enet_close(struct device *dev)
+{
+	/* Don't know what to do yet.
+	*/
+
+	return 0;
+}
+
+static struct net_device_stats *cpm_enet_get_stats(struct device *dev)
+{
+	struct cpm_enet_private *cep = (struct cpm_enet_private *)dev->priv;
+
+	return &cep->stats;
+}
+
+/* Set or clear the multicast filter for this adaptor.
+ * Skeleton taken from sunlance driver.
+ * The CPM Ethernet implementation allows Multicast as well as individual
+ * MAC address filtering.  Some of the drivers check to make sure it is
+ * a group multicast address, and discard those that are not.  I guess I
+ * will do the same for now, but just remove the test if you want
+ * individual filtering as well (do the upper net layers want or support
+ * this kind of feature?).
+ */
+
+static void set_multicast_list(struct device *dev)
+{
+	struct	cpm_enet_private *cep;
+	struct	dev_mc_list *dmi;
+	u_char	*mcptr, *tdptr;
+	volatile scc_enet_t *ep;
+	int	i, j;
+
+	cep = (struct cpm_enet_private *)dev->priv;
+
+	/* Get pointer to SCC1 area in parameter RAM.
+	*/
+	ep = (scc_enet_t *)dev->base_addr;
+
+	if (dev->flags&IFF_PROMISC) {
+		/* Log any net taps. */
+		printk("%s: Promiscuous mode enabled.\n", dev->name);
+		cep->sccp->scc_pmsr |= SCC_PMSR_PRO;
+	} else {
+
+		cep->sccp->scc_pmsr &= ~SCC_PMSR_PRO;
+
+		if (dev->flags & IFF_ALLMULTI) {
+			/* Catch all multicast addresses, so set the
+			 * filter to all 1's.
+			 */
+			ep->sen_gaddr1 = 0xffff;
+			ep->sen_gaddr2 = 0xffff;
+			ep->sen_gaddr3 = 0xffff;
+			ep->sen_gaddr4 = 0xffff;
+		}
+		else {
+			/* Clear filter and add the addresses in the list.
+			*/
+			ep->sen_gaddr1 = 0;
+			ep->sen_gaddr2 = 0;
+			ep->sen_gaddr3 = 0;
+			ep->sen_gaddr4 = 0;
+
+			dmi = dev->mc_list;
+
+			for (i=0; i<dev->mc_count; i++) {
+				
+				/* Only support group multicast for now.
+				*/
+				if (!(dmi->dmi_addr[0] & 1))
+					continue;
+
+				/* The address in dmi_addr is LSB first,
+				 * and taddr is MSB first.  We have to
+				 * copy bytes MSB first from dmi_addr.
+				 */
+				mcptr = (u_char *)dmi->dmi_addr + 5;
+				tdptr = (u_char *)&ep->sen_taddrh;
+				for (j=0; j<6; j++)
+					*tdptr++ = *mcptr--;
+
+				/* Ask CPM to run CRC and set bit in
+				 * filter mask.
+				 */
+				cpmp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SCC1, CPM_CR_SET_GADDR) | CPM_CR_FLG;
+				while (cpmp->cp_cpcr & CPM_CR_FLG);
+			}
+		}
+	}
+}
diff -u --recursive --new-file v2.1.96/linux/arch/ppc/8xx_io/uart.c linux/arch/ppc/8xx_io/uart.c
--- v2.1.96/linux/arch/ppc/8xx_io/uart.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/ppc/8xx_io/uart.c	Tue Apr 14 17:33:40 1998
@@ -0,0 +1,2530 @@
+/*
+ *  UART driver for MPC860 CPM SCC or SMC
+ *  Copyright (c) 1997 Dan Malek (dmalek@jlc.net)
+ *
+ * I used the serial.c driver as the framework for this driver.
+ * Give credit to those guys.
+ * The original code was written for the MBX860 board.  I tried to make
+ * it generic, but there may be some assumptions in the structures that
+ * have to be fixed later.
+ * To save porting time, I did not bother to change any object names
+ * that are not accessed outside of this file.
+ * It still needs lots of work........When it was easy, I included code
+ * to support the SCCs, but this has never been tested, nor is it complete.
+ * Only the SCCs support modem control, so that is not complete either.
+ *
+ * This module exports the following rs232 io functions:
+ *
+ *	int rs_8xx_init(void);
+ */
+
+#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/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/ptrace.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <asm/uaccess.h>
+
+#include "commproc.h"
+
+#ifdef CONFIG_SERIAL_CONSOLE
+#include <linux/console.h>
+
+/* this defines the index into rs_table for the port to use
+*/
+#ifndef CONFIG_SERIAL_CONSOLE_PORT
+#define CONFIG_SERIAL_CONSOLE_PORT	0
+#endif
+#endif
+
+#define TX_WAKEUP	ASYNC_SHARE_IRQ
+
+static char *serial_name = "CPM UART driver";
+static char *serial_version = "0.01";
+
+static DECLARE_TASK_QUEUE(tq_serial);
+
+static struct tty_driver serial_driver, callout_driver;
+static int serial_refcount;
+
+/*
+ * Serial driver configuration section.  Here are the various options:
+ */
+#define SERIAL_PARANOIA_CHECK
+#define CONFIG_SERIAL_NOPAUSE_IO
+#define SERIAL_DO_RESTART
+
+/* Set of debugging defines */
+
+#undef SERIAL_DEBUG_INTR
+#undef SERIAL_DEBUG_OPEN
+#undef SERIAL_DEBUG_FLOW
+#undef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
+
+#define _INLINE_ inline
+  
+#define DBG_CNT(s)
+
+/* We overload some of the items in the data structure to meet our
+ * needs.  For example, the port address is the CPM parameter ram
+ * offset for the SCC or SMC.  The maximum number of ports is 4 SCCs and
+ * 2 SMCs.  The "hub6" field is used to indicate the channel number, with
+ * 0 and 1 indicating the SMCs and 2, 3, 4, and 5 are the SCCs.
+ * Since these ports are so versatile, I don't yet have a strategy for
+ * their management.  For example, SCC1 is used for Ethernet.  Right
+ * now, just don't put them in the table.  Of course, right now I just
+ * want the SMC to work as a uart :-)..
+ * The "type" field is currently set to 0, for PORT_UNKNOWN.  It is
+ * not currently used.  I should probably use it to indicate the port
+ * type of CMS or SCC.
+ * The SMCs do not support any modem control signals.
+ */
+#define smc_scc_num	hub6
+#define SCC_NUM_BASE	2
+
+static struct serial_state rs_table[] = {
+	/* UART CLK   PORT          IRQ      FLAGS  NUM   */
+	{ 0,     0, PROFF_SMC1, CPMVEC_SMC1,   0,    0 },    /* SMC1 ttyS0 */
+	{ 0,     0, PROFF_SMC2, CPMVEC_SMC2,   0,    1 },    /* SMC1 ttyS0 */
+};
+
+#define NR_PORTS	(sizeof(rs_table)/sizeof(struct serial_state))
+
+static struct tty_struct *serial_table[NR_PORTS];
+static struct termios *serial_termios[NR_PORTS];
+static struct termios *serial_termios_locked[NR_PORTS];
+
+/* The number of buffer descriptors and their sizes.
+*/
+#define RX_NUM_FIFO	4
+#define RX_BUF_SIZE	32
+#define TX_NUM_FIFO	4
+#define TX_BUF_SIZE	32
+
+#ifndef MIN
+#define MIN(a,b)	((a) < (b) ? (a) : (b))
+#endif
+
+/* The async_struct in serial.h does not really give us what we
+ * need, so define our own here.
+ */
+typedef struct serial_info {
+	int			magic;
+	int			flags;
+	struct serial_state	*state;
+	struct tty_struct 	*tty;
+	int			read_status_mask;
+	int			ignore_status_mask;
+	int			timeout;
+	int			line;
+	int			x_char;	/* xon/xoff character */
+	int			close_delay;
+	unsigned short		closing_wait;
+	unsigned short		closing_wait2;
+	unsigned long		event;
+	unsigned long		last_active;
+	int			blocked_open; /* # of blocked opens */
+	long			session; /* Session of opening process */
+	long			pgrp; /* pgrp of opening process */
+	struct tq_struct	tqueue;
+	struct tq_struct	tqueue_hangup;
+	struct wait_queue	*open_wait;
+	struct wait_queue	*close_wait;
+
+	/* CPM Buffer Descriptor pointers.
+	*/
+	cbd_t			*rx_bd_base;
+	cbd_t			*rx_cur;
+	cbd_t			*tx_bd_base;
+	cbd_t			*tx_cur;
+} ser_info_t;
+
+static void change_speed(ser_info_t *info);
+static void rs_8xx_wait_until_sent(struct tty_struct *tty, int timeout);
+
+static inline int serial_paranoia_check(ser_info_t *info,
+					kdev_t device, const char *routine)
+{
+#ifdef SERIAL_PARANOIA_CHECK
+	static const char *badmagic =
+		"Warning: bad magic number for serial struct (%s) in %s\n";
+	static const char *badinfo =
+		"Warning: null async_struct for (%s) in %s\n";
+
+	if (!info) {
+		printk(badinfo, kdevname(device), routine);
+		return 1;
+	}
+	if (info->magic != SERIAL_MAGIC) {
+		printk(badmagic, kdevname(device), routine);
+		return 1;
+	}
+#endif
+	return 0;
+}
+
+/*
+ * This is used to figure out the divisor speeds and the timeouts,
+ * indexed by the termio value.  The generic CPM functions are responsible
+ * for setting and assigning baud rate generators for us.
+ */
+static int baud_table[] = {
+	0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
+	9600, 19200, 38400, 57600, 115200, 230400, 460800, 0 };
+
+
+/*
+ * ------------------------------------------------------------
+ * rs_stop() and rs_start()
+ *
+ * This routines are called before setting or resetting tty->stopped.
+ * They enable or disable transmitter interrupts, as necessary.
+ * ------------------------------------------------------------
+ */
+static void rs_8xx_stop(struct tty_struct *tty)
+{
+	ser_info_t *info = (ser_info_t *)tty->driver_data;
+	int	idx;
+	unsigned long flags;
+	volatile scc_t	*sccp;
+	volatile smc_t	*smcp;
+
+	if (serial_paranoia_check(info, tty->device, "rs_stop"))
+		return;
+	
+	save_flags(flags); cli();
+	if ((idx = info->state->smc_scc_num) < SCC_NUM_BASE) {
+		smcp = &cpmp->cp_smc[idx];
+		smcp->smc_smcm &= ~SMCM_TX;
+	}
+	else {
+		sccp = &cpmp->cp_scc[idx - SCC_NUM_BASE];
+		sccp->scc_sccm &= ~UART_SCCM_TX;
+	}
+	restore_flags(flags);
+}
+
+static void rs_8xx_start(struct tty_struct *tty)
+{
+	ser_info_t *info = (ser_info_t *)tty->driver_data;
+	int	idx;
+	unsigned long flags;
+	volatile scc_t	*sccp;
+	volatile smc_t	*smcp;
+
+	if (serial_paranoia_check(info, tty->device, "rs_stop"))
+		return;
+	
+	save_flags(flags); cli();
+	if ((idx = info->state->smc_scc_num) < SCC_NUM_BASE) {
+		smcp = &cpmp->cp_smc[idx];
+		smcp->smc_smcm |= SMCM_TX;
+	}
+	else {
+		sccp = &cpmp->cp_scc[idx - SCC_NUM_BASE];
+		sccp->scc_sccm |= UART_SCCM_TX;
+	}
+	restore_flags(flags);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Here starts the interrupt handling routines.  All of the following
+ * subroutines are declared as inline and are folded into
+ * rs_interrupt().  They were separated out for readability's sake.
+ *
+ * Note: rs_interrupt() is a "fast" interrupt, which means that it
+ * runs with interrupts turned off.  People who may want to modify
+ * rs_interrupt() should try to keep the interrupt handler as fast as
+ * possible.  After you are done making modifications, it is not a bad
+ * idea to do:
+ * 
+ * gcc -S -DKERNEL -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer serial.c
+ *
+ * and look at the resulting assemble code in serial.s.
+ *
+ * 				- Ted Ts'o (tytso@mit.edu), 7-Mar-93
+ * -----------------------------------------------------------------------
+ */
+
+/*
+ * This routine is used by the interrupt handler to schedule
+ * processing in the software interrupt portion of the driver.
+ */
+static _INLINE_ void rs_sched_event(ser_info_t *info,
+				  int event)
+{
+	info->event |= 1 << event;
+	queue_task(&info->tqueue, &tq_serial);
+	mark_bh(SERIAL_BH);
+}
+
+static _INLINE_ void receive_chars(ser_info_t *info)
+{
+	struct tty_struct *tty = info->tty;
+	unsigned char ch, *cp;
+	int	ignored = 0;
+	int	i;
+	ushort	status;
+	struct	async_icount *icount;
+	volatile cbd_t	*bdp;
+
+	icount = &info->state->icount;
+
+	/* Just loop through the closed BDs and copy the characters into
+	 * the buffer.
+	 */
+	bdp = info->rx_cur;
+	for (;;) {
+		if (bdp->cbd_sc & BD_SC_EMPTY)	/* If this one is empty */
+			break;			/*   we are all done */
+
+		/* The read status mask tell us what we should do with
+		 * incoming characters, especially if errors occur.
+		 * One special case is the use of BD_SC_EMPTY.  If
+		 * this is not set, we are supposed to be ignoring
+		 * inputs.  In this case, just mark the buffer empty and
+		 * continue.
+		if (!(info->read_status_mask & BD_SC_EMPTY)) {
+			bdp->cbd_sc |= BD_SC_EMPTY;
+			bdp->cbd_sc &=
+				~(BD_SC_BR | BD_SC_FR | BD_SC_PR | BD_SC_OV);
+
+			if (bdp->cbd_sc & BD_SC_WRAP)
+				bdp = info->rx_bd_base;
+			else
+				bdp++;
+			continue;
+		}
+		 */
+
+		/* Get the number of characters and the buffer pointer.
+		*/
+		i = bdp->cbd_datlen;
+		cp = (unsigned char *)__va(bdp->cbd_bufaddr);
+		status = bdp->cbd_sc;
+
+		/* Check to see if there is room in the tty buffer for
+		 * the characters in our BD buffer.  If not, we exit
+		 * now, leaving the BD with the characters.  We'll pick
+		 * them up again on the next receive interrupt (which could
+		 * be a timeout).
+		 */
+		if ((tty->flip.count + i) >= TTY_FLIPBUF_SIZE)
+			break;
+
+		while (i-- > 0) {
+			ch = *cp++;
+			*tty->flip.char_buf_ptr = ch;
+			icount->rx++;
+
+#ifdef SERIAL_DEBUG_INTR
+			printk("DR%02x:%02x...", ch, *status);
+#endif
+			*tty->flip.flag_buf_ptr = 0;
+			if (status & (BD_SC_BR | BD_SC_FR |
+				       BD_SC_PR | BD_SC_OV)) {
+				/*
+				 * For statistics only
+				 */
+				if (status & BD_SC_BR)
+					icount->brk++;
+				else if (status & BD_SC_PR)
+					icount->parity++;
+				else if (status & BD_SC_FR)
+					icount->frame++;
+				if (status & BD_SC_OV)
+					icount->overrun++;
+
+				/*
+				 * Now check to see if character should be
+				 * ignored, and mask off conditions which
+				 * should be ignored.
+				if (status & info->ignore_status_mask) {
+					if (++ignored > 100)
+						break;
+					continue;
+				}
+				 */
+				status &= info->read_status_mask;
+		
+				if (status & (BD_SC_BR)) {
+#ifdef SERIAL_DEBUG_INTR
+					printk("handling break....");
+#endif
+					*tty->flip.flag_buf_ptr = TTY_BREAK;
+					if (info->flags & ASYNC_SAK)
+						do_SAK(tty);
+				} else if (status & BD_SC_PR)
+					*tty->flip.flag_buf_ptr = TTY_PARITY;
+				else if (status & BD_SC_FR)
+					*tty->flip.flag_buf_ptr = TTY_FRAME;
+				if (status & BD_SC_OV) {
+					/*
+					 * Overrun is special, since it's
+					 * reported immediately, and doesn't
+					 * affect the current character
+					 */
+					if (tty->flip.count < TTY_FLIPBUF_SIZE) {
+						tty->flip.count++;
+						tty->flip.flag_buf_ptr++;
+						tty->flip.char_buf_ptr++;
+						*tty->flip.flag_buf_ptr =
+								TTY_OVERRUN;
+					}
+				}
+			}
+			if (tty->flip.count >= TTY_FLIPBUF_SIZE)
+				break;
+
+			tty->flip.flag_buf_ptr++;
+			tty->flip.char_buf_ptr++;
+			tty->flip.count++;
+		}
+
+		/* This BD is ready to be used again.  Clear status.
+		 * Get next BD.
+		 */
+		bdp->cbd_sc |= BD_SC_EMPTY;
+		bdp->cbd_sc &= ~(BD_SC_BR | BD_SC_FR | BD_SC_PR | BD_SC_OV);
+
+		if (bdp->cbd_sc & BD_SC_WRAP)
+			bdp = info->rx_bd_base;
+		else
+			bdp++;
+	}
+
+	info->rx_cur = (cbd_t *)bdp;
+
+	queue_task(&tty->flip.tqueue, &tq_timer);
+}
+
+static _INLINE_ void transmit_chars(ser_info_t *info)
+{
+	
+	if (info->flags & TX_WAKEUP) {
+		rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
+	}
+
+#ifdef SERIAL_DEBUG_INTR
+	printk("THRE...");
+#endif
+}
+
+#ifdef notdef
+	/* I need to do this for the SCCs, so it is left as a reminder.
+	*/
+static _INLINE_ void check_modem_status(struct async_struct *info)
+{
+	int	status;
+	struct	async_icount *icount;
+	
+	status = serial_in(info, UART_MSR);
+
+	if (status & UART_MSR_ANY_DELTA) {
+		icount = &info->state->icount;
+		/* update input line counters */
+		if (status & UART_MSR_TERI)
+			icount->rng++;
+		if (status & UART_MSR_DDSR)
+			icount->dsr++;
+		if (status & UART_MSR_DDCD) {
+			icount->dcd++;
+#ifdef CONFIG_HARD_PPS
+			if ((info->flags & ASYNC_HARDPPS_CD) &&
+			    (status & UART_MSR_DCD))
+				hardpps();
+#endif
+		}
+		if (status & UART_MSR_DCTS)
+			icount->cts++;
+		wake_up_interruptible(&info->delta_msr_wait);
+	}
+
+	if ((info->flags & ASYNC_CHECK_CD) && (status & UART_MSR_DDCD)) {
+#if (defined(SERIAL_DEBUG_OPEN) || defined(SERIAL_DEBUG_INTR))
+		printk("ttys%d CD now %s...", info->line,
+		       (status & UART_MSR_DCD) ? "on" : "off");
+#endif		
+		if (status & UART_MSR_DCD)
+			wake_up_interruptible(&info->open_wait);
+		else if (!((info->flags & ASYNC_CALLOUT_ACTIVE) &&
+			   (info->flags & ASYNC_CALLOUT_NOHUP))) {
+#ifdef SERIAL_DEBUG_OPEN
+			printk("scheduling hangup...");
+#endif
+			queue_task(&info->tqueue_hangup,
+					   &tq_scheduler);
+		}
+	}
+	if (info->flags & ASYNC_CTS_FLOW) {
+		if (info->tty->hw_stopped) {
+			if (status & UART_MSR_CTS) {
+#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW))
+				printk("CTS tx start...");
+#endif
+				info->tty->hw_stopped = 0;
+				info->IER |= UART_IER_THRI;
+				serial_out(info, UART_IER, info->IER);
+				rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
+				return;
+			}
+		} else {
+			if (!(status & UART_MSR_CTS)) {
+#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW))
+				printk("CTS tx stop...");
+#endif
+				info->tty->hw_stopped = 1;
+				info->IER &= ~UART_IER_THRI;
+				serial_out(info, UART_IER, info->IER);
+			}
+		}
+	}
+}
+#endif
+
+/*
+ * This is the serial driver's interrupt routine for a single port
+ */
+static void rs_8xx_interrupt(void *dev_id)
+{
+	u_char	events;
+	int	idx;
+	ser_info_t *info;
+	volatile smc_t	*smcp;
+	
+	info = (ser_info_t *)dev_id;
+
+	if ((idx = info->state->smc_scc_num) < SCC_NUM_BASE) {
+		smcp = &cpmp->cp_smc[idx];
+	}
+	else {
+		panic("SCC UART Interrupt....not ready");
+	}
+	
+	events = smcp->smc_smce;
+#ifdef SERIAL_DEBUG_INTR
+	printk("rs_interrupt_single(%d, %x)...",
+					info->state->smc_scc_num, events);
+#endif
+	if (events & SMCM_RX)
+		receive_chars(info);
+	if (events & SMCM_TX)
+		transmit_chars(info);
+	smcp->smc_smce = events;
+#ifdef modem_control
+	check_modem_status(info);
+#endif
+	info->last_active = jiffies;
+#ifdef SERIAL_DEBUG_INTR
+	printk("end.\n");
+#endif
+}
+
+
+/*
+ * -------------------------------------------------------------------
+ * Here ends the serial interrupt routines.
+ * -------------------------------------------------------------------
+ */
+
+/*
+ * This routine is used to handle the "bottom half" processing for the
+ * serial driver, known also the "software interrupt" processing.
+ * This processing is done at the kernel interrupt level, after the
+ * rs_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON.  This
+ * is where time-consuming activities which can not be done in the
+ * interrupt driver proper are done; the interrupt driver schedules
+ * them using rs_sched_event(), and they get done here.
+ */
+static void do_serial_bh(void)
+{
+	run_task_queue(&tq_serial);
+}
+
+static void do_softint(void *private_)
+{
+	ser_info_t	*info = (ser_info_t *) private_;
+	struct tty_struct	*tty;
+	
+	tty = info->tty;
+	if (!tty)
+		return;
+
+	if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) {
+		if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+		    tty->ldisc.write_wakeup)
+			(tty->ldisc.write_wakeup)(tty);
+		wake_up_interruptible(&tty->write_wait);
+	}
+}
+
+/*
+ * 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);
+}
+
+static void rs_8xx_timer(void)
+{
+	printk("rs_8xx_timer\n");
+}
+
+
+static int startup(ser_info_t *info)
+{
+	unsigned long flags;
+	int	retval=0;
+	int	idx;
+	struct serial_state *state= info->state;
+	volatile smc_t		*smcp;
+	volatile scc_t		*sccp;
+	volatile smc_uart_t	*up;
+
+
+	save_flags(flags); cli();
+
+	if (info->flags & ASYNC_INITIALIZED) {
+		goto errout;
+	}
+
+#ifdef maybe
+	if (!state->port || !state->type) {
+		if (info->tty)
+			set_bit(TTY_IO_ERROR, &info->tty->flags);
+		goto errout;
+	}
+#endif
+
+#ifdef SERIAL_DEBUG_OPEN
+	printk("starting up ttys%d (irq %d)...", info->line, state->irq);
+#endif
+
+
+#ifdef modem_control
+	info->MCR = 0;
+	if (info->tty->termios->c_cflag & CBAUD)
+		info->MCR = UART_MCR_DTR | UART_MCR_RTS;
+#endif
+	
+	if (info->tty)
+		clear_bit(TTY_IO_ERROR, &info->tty->flags);
+
+	/*
+	 * and set the speed of the serial port
+	 */
+	change_speed(info);
+	
+	if ((idx = info->state->smc_scc_num) < SCC_NUM_BASE) {
+		smcp = &cpmp->cp_smc[idx];
+
+		/* Enable interrupts and I/O.
+		*/
+		smcp->smc_smcm |= (SMCM_RX | SMCM_TX);
+		smcp->smc_smcmr |= (SMCMR_REN | SMCMR_TEN);
+
+		/* We can tune the buffer length and idle characters
+		 * to take advantage of the entire incoming buffer size.
+		 * If mrblr is something other than 1, maxidl has to be
+		 * non-zero or we never get an interrupt.  The maxidl
+		 * is the number of character times we wait after reception
+		 * of the last character before we decide no more characters
+		 * are coming.
+		 */
+		up = (smc_uart_t *)&cpmp->cp_dparam[state->port];
+		up->smc_mrblr = 1;	/* receive buffer length */
+		up->smc_maxidl = 0;	/* wait forever for next char */
+		up->smc_brkcr = 1;	/* number of break chars */
+	}
+	else {
+		sccp = &cpmp->cp_scc[idx - SCC_NUM_BASE];
+		sccp->scc_sccm |= UART_SCCM_RX;
+	}
+
+	info->flags |= ASYNC_INITIALIZED;
+	restore_flags(flags);
+	return 0;
+	
+errout:
+	restore_flags(flags);
+	return retval;
+}
+
+/*
+ * This routine will shutdown a serial port; interrupts are disabled, and
+ * DTR is dropped if the hangup on close termio flag is on.
+ */
+static void shutdown(ser_info_t * info)
+{
+	unsigned long	flags;
+	struct serial_state *state;
+	int		idx;
+	volatile smc_t	*smcp;
+	volatile scc_t	*sccp;
+
+	if (!(info->flags & ASYNC_INITIALIZED))
+		return;
+
+	state = info->state;
+
+#ifdef SERIAL_DEBUG_OPEN
+	printk("Shutting down serial port %d (irq %d)....", info->line,
+	       state->irq);
+#endif
+	
+	save_flags(flags); cli(); /* Disable interrupts */
+
+	if ((idx = info->state->smc_scc_num) < SCC_NUM_BASE) {
+		smcp = &cpmp->cp_smc[idx];
+
+		/* Disable interrupts and I/O.
+		*/
+		smcp->smc_smcm &= ~(SMCM_RX | SMCM_TX);
+#ifdef CONFIG_SERIAL_CONSOLE
+		/* We can't disable the transmitter if this is the
+		 * system console.
+		 */
+		if (idx != CONFIG_SERIAL_CONSOLE_PORT)
+#endif
+			smcp->smc_smcmr &= ~(SMCMR_REN | SMCMR_TEN);
+	}
+	else {
+		sccp = &cpmp->cp_scc[idx - SCC_NUM_BASE];
+		sccp->scc_sccm &= ~UART_SCCM_RX;
+	}
+
+	
+	if (info->tty)
+		set_bit(TTY_IO_ERROR, &info->tty->flags);
+
+	info->flags &= ~ASYNC_INITIALIZED;
+	restore_flags(flags);
+}
+
+/*
+ * This routine is called to set the UART divisor registers to match
+ * the specified baud rate for a serial port.
+ */
+static void change_speed(ser_info_t *info)
+{
+	int	baud_rate;
+	unsigned cflag, cval, prev_mode;
+	int	i, bits, idx;
+	unsigned long	flags;
+	volatile smc_t	*smcp;
+	volatile scc_t	*sccp;
+
+	if (!info->tty || !info->tty->termios)
+		return;
+	cflag = info->tty->termios->c_cflag;
+
+	/* Character length programmed into the mode register is the
+	 * sum of: 1 start bit, number of data bits, 0 or 1 parity bit,
+	 * 1 or 2 stop bits, minus 1.
+	 * The value 'bits' counts this for us.
+	 */
+	cval = 0;
+
+	/* byte size and parity */
+	switch (cflag & CSIZE) {
+	      case CS5: bits = 5; break;
+	      case CS6: bits = 6; break;
+	      case CS7: bits = 7; break;
+	      case CS8: bits = 8; break;
+	      /* Never happens, but GCC is too dumb to figure it out */
+	      default:  bits = 8; break;
+	}
+	if (cflag & CSTOPB) {
+		cval |= SMCMR_SL;	/* Two stops */
+		bits++;
+	}
+	if (cflag & PARENB) {
+		cval |= SMCMR_PEN;
+		bits++;
+	}
+	if (!(cflag & PARODD))
+		cval |= SMCMR_PM_EVEN;
+
+	/* Determine divisor based on baud rate */
+	i = cflag & CBAUD;
+	if (i & CBAUDEX) {
+		i &= ~CBAUDEX;
+		if (i < 1 || i > 4) 
+			info->tty->termios->c_cflag &= ~CBAUDEX;
+		else
+			i += 15;
+	}
+
+	baud_rate = baud_table[i];
+
+	info->timeout = (TX_BUF_SIZE*HZ*bits);
+	info->timeout += HZ/50;		/* Add .02 seconds of slop */
+
+#ifdef modem_control
+	/* CTS flow control flag and modem status interrupts */
+	info->IER &= ~UART_IER_MSI;
+	if (info->flags & ASYNC_HARDPPS_CD)
+		info->IER |= UART_IER_MSI;
+	if (cflag & CRTSCTS) {
+		info->flags |= ASYNC_CTS_FLOW;
+		info->IER |= UART_IER_MSI;
+	} else
+		info->flags &= ~ASYNC_CTS_FLOW;
+	if (cflag & CLOCAL)
+		info->flags &= ~ASYNC_CHECK_CD;
+	else {
+		info->flags |= ASYNC_CHECK_CD;
+		info->IER |= UART_IER_MSI;
+	}
+	serial_out(info, UART_IER, info->IER);
+#endif
+
+	/*
+	 * Set up parity check flag
+	 */
+#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
+
+	info->read_status_mask = (BD_SC_EMPTY | BD_SC_OV);
+	if (I_INPCK(info->tty))
+		info->read_status_mask |= BD_SC_FR | BD_SC_PR;
+	if (I_BRKINT(info->tty) || I_PARMRK(info->tty))
+		info->read_status_mask |= BD_SC_BR;
+	
+	/*
+	 * Characters to ignore
+	 */
+	info->ignore_status_mask = 0;
+	if (I_IGNPAR(info->tty))
+		info->ignore_status_mask |= BD_SC_PR | BD_SC_FR;
+	if (I_IGNBRK(info->tty)) {
+		info->ignore_status_mask |= BD_SC_BR;
+		/*
+		 * If we're ignore parity and break indicators, ignore 
+		 * overruns too.  (For real raw support).
+		 */
+		if (I_IGNPAR(info->tty))
+			info->ignore_status_mask |= BD_SC_OV;
+	}
+	/*
+	 * !!! ignore all characters if CREAD is not set
+	 */
+	if ((cflag & CREAD) == 0)
+		info->read_status_mask &= ~BD_SC_EMPTY;
+	save_flags(flags); cli();
+
+	/* Start bit has not been added (so don't, because we would just
+	 * subtract it later), and we need to add one for the number of
+	 * stops bits (there is always at least one).
+	 */
+	bits++;
+	if ((idx = info->state->smc_scc_num) < SCC_NUM_BASE) {
+		smcp = &cpmp->cp_smc[idx];
+
+		/* Set the mode register.  We want to keep a copy of the
+		 * enables, because we want to put them back if they were
+		 * present.
+		 */
+		prev_mode = smcp->smc_smcmr;
+		smcp->smc_smcmr = smcr_mk_clen(bits) | cval |  SMCMR_SM_UART;
+		smcp->smc_smcmr |= (prev_mode & (SMCMR_REN | SMCMR_TEN));
+	}
+	else {
+		sccp = &cpmp->cp_scc[idx - SCC_NUM_BASE];
+		sccp->scc_sccm &= ~UART_SCCM_RX;
+	}
+
+	mbx_cpm_setbrg(info->state->smc_scc_num, baud_rate);
+
+	restore_flags(flags);
+}
+
+static void rs_8xx_put_char(struct tty_struct *tty, unsigned char ch)
+{
+	ser_info_t *info = (ser_info_t *)tty->driver_data;
+	volatile cbd_t	*bdp;
+
+	if (serial_paranoia_check(info, tty->device, "rs_put_char"))
+		return;
+
+	if (!tty)
+		return;
+
+	bdp = info->tx_cur;
+	while (bdp->cbd_sc & BD_SC_READY);
+
+	*((char *)__va(bdp->cbd_bufaddr)) = ch;
+	bdp->cbd_datlen = 1;
+	bdp->cbd_sc |= BD_SC_READY;
+
+	/* Get next BD.
+	*/
+	if (bdp->cbd_sc & BD_SC_WRAP)
+		bdp = info->tx_bd_base;
+	else
+		bdp++;
+
+	info->tx_cur = (cbd_t *)bdp;
+
+}
+
+static int rs_8xx_write(struct tty_struct * tty, int from_user,
+		    const unsigned char *buf, int count)
+{
+	int	c, ret = 0;
+	ser_info_t *info = (ser_info_t *)tty->driver_data;
+	volatile cbd_t *bdp;
+
+	if (serial_paranoia_check(info, tty->device, "rs_write"))
+		return 0;
+
+	if (!tty) 
+		return 0;
+
+	bdp = info->tx_cur;
+
+	while (1) {
+		c = MIN(count, TX_BUF_SIZE);
+
+		if (c <= 0)
+			break;
+
+		if (bdp->cbd_sc & BD_SC_READY) {
+			info->flags |= TX_WAKEUP;
+			break;
+		}
+
+		if (from_user) {
+			if (c !=
+			    copy_from_user(__va(bdp->cbd_bufaddr), buf, c)) {
+				if (!ret)
+					ret = -EFAULT;
+				break;
+			}
+		} else {
+			memcpy(__va(bdp->cbd_bufaddr), buf, c);
+		}
+
+		bdp->cbd_datlen = c;
+		bdp->cbd_sc |= BD_SC_READY;
+
+		buf += c;
+		count -= c;
+		ret += c;
+
+		/* Get next BD.
+		*/
+		if (bdp->cbd_sc & BD_SC_WRAP)
+			bdp = info->tx_bd_base;
+		else
+			bdp++;
+		info->tx_cur = (cbd_t *)bdp;
+	}
+	return ret;
+}
+
+static int rs_8xx_write_room(struct tty_struct *tty)
+{
+	ser_info_t *info = (ser_info_t *)tty->driver_data;
+	int	ret;
+
+	if (serial_paranoia_check(info, tty->device, "rs_write_room"))
+		return 0;
+
+	if ((info->tx_cur->cbd_sc & BD_SC_READY) == 0) {
+		info->flags &= ~TX_WAKEUP;
+		ret = TX_BUF_SIZE;
+	}
+	else {
+		info->flags |= TX_WAKEUP;
+		ret = 0;
+	}
+	return ret;
+}
+
+/* I could track this with transmit counters....maybe later.
+*/
+static int rs_8xx_chars_in_buffer(struct tty_struct *tty)
+{
+	ser_info_t *info = (ser_info_t *)tty->driver_data;
+				
+	if (serial_paranoia_check(info, tty->device, "rs_chars_in_buffer"))
+		return 0;
+	return 0;
+}
+
+static void rs_8xx_flush_buffer(struct tty_struct *tty)
+{
+	ser_info_t *info = (ser_info_t *)tty->driver_data;
+				
+	if (serial_paranoia_check(info, tty->device, "rs_flush_buffer"))
+		return;
+
+	/* There is nothing to "flush", whatever we gave the CPM
+	 * is on its way out.
+	 */
+	wake_up_interruptible(&tty->write_wait);
+	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+	    tty->ldisc.write_wakeup)
+		(tty->ldisc.write_wakeup)(tty);
+	info->flags &= ~TX_WAKEUP;
+}
+
+/*
+ * This function is used to send a high-priority XON/XOFF character to
+ * the device
+ */
+static void rs_8xx_send_xchar(struct tty_struct *tty, char ch)
+{
+	volatile cbd_t	*bdp;
+
+	ser_info_t *info = (ser_info_t *)tty->driver_data;
+
+	if (serial_paranoia_check(info, tty->device, "rs_send_char"))
+		return;
+
+	bdp = info->tx_cur;
+	while (bdp->cbd_sc & BD_SC_READY);
+
+	*((char *)__va(bdp->cbd_bufaddr)) = ch;
+	bdp->cbd_datlen = 1;
+	bdp->cbd_sc |= BD_SC_READY;
+
+	/* Get next BD.
+	*/
+	if (bdp->cbd_sc & BD_SC_WRAP)
+		bdp = info->tx_bd_base;
+	else
+		bdp++;
+
+	info->tx_cur = (cbd_t *)bdp;
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_throttle()
+ * 
+ * This routine is called by the upper-layer tty layer to signal that
+ * incoming characters should be throttled.
+ * ------------------------------------------------------------
+ */
+static void rs_8xx_throttle(struct tty_struct * tty)
+{
+	ser_info_t *info = (ser_info_t *)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))
+		rs_8xx_send_xchar(tty, STOP_CHAR(tty));
+
+#ifdef modem_control
+	if (tty->termios->c_cflag & CRTSCTS)
+		info->MCR &= ~UART_MCR_RTS;
+
+	cli();
+	serial_out(info, UART_MCR, info->MCR);
+	sti();
+#endif
+}
+
+static void rs_8xx_unthrottle(struct tty_struct * tty)
+{
+	ser_info_t *info = (ser_info_t *)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)) {
+		if (info->x_char)
+			info->x_char = 0;
+		else
+			rs_8xx_send_xchar(tty, START_CHAR(tty));
+	}
+#ifdef modem_control
+	if (tty->termios->c_cflag & CRTSCTS)
+		info->MCR |= UART_MCR_RTS;
+	cli();
+	serial_out(info, UART_MCR, info->MCR);
+	sti();
+#endif
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_ioctl() and friends
+ * ------------------------------------------------------------
+ */
+
+#ifdef maybe
+/*
+ * get_lsr_info - get line status register info
+ *
+ * Purpose: Let user call ioctl() to get info when the UART physically
+ * 	    is emptied.  On bus types like RS485, the transmitter must
+ * 	    release the bus after transmitting. This must be done when
+ * 	    the transmit shift register is empty, not be done when the
+ * 	    transmit holding register is empty.  This functionality
+ * 	    allows an RS485 driver to be written in user space. 
+ */
+static int get_lsr_info(struct async_struct * info, unsigned int *value)
+{
+	unsigned char status;
+	unsigned int result;
+
+	cli();
+	status = serial_in(info, UART_LSR);
+	sti();
+	result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0);
+	return put_user(result,value);
+}
+#endif
+
+static int get_modem_info(ser_info_t *info, unsigned int *value)
+{
+	unsigned int result = 0;
+#ifdef modem_control
+	unsigned char control, status;
+
+	control = info->MCR;
+	cli();
+	status = serial_in(info, UART_MSR);
+	sti();
+	result =  ((control & UART_MCR_RTS) ? TIOCM_RTS : 0)
+		| ((control & UART_MCR_DTR) ? TIOCM_DTR : 0)
+#ifdef TIOCM_OUT1
+		| ((control & UART_MCR_OUT1) ? TIOCM_OUT1 : 0)
+		| ((control & UART_MCR_OUT2) ? TIOCM_OUT2 : 0)
+#endif
+		| ((status  & UART_MSR_DCD) ? TIOCM_CAR : 0)
+		| ((status  & UART_MSR_RI) ? TIOCM_RNG : 0)
+		| ((status  & UART_MSR_DSR) ? TIOCM_DSR : 0)
+		| ((status  & UART_MSR_CTS) ? TIOCM_CTS : 0);
+#endif
+	return put_user(result,value);
+}
+
+static int set_modem_info(ser_info_t *info, unsigned int cmd,
+			  unsigned int *value)
+{
+	int error;
+	unsigned int arg;
+
+	error = get_user(arg, value);
+	if (error)
+		return error;
+#ifdef modem_control
+	switch (cmd) {
+	case TIOCMBIS: 
+		if (arg & TIOCM_RTS)
+			info->MCR |= UART_MCR_RTS;
+		if (arg & TIOCM_DTR)
+			info->MCR |= UART_MCR_DTR;
+#ifdef TIOCM_OUT1
+		if (arg & TIOCM_OUT1)
+			info->MCR |= UART_MCR_OUT1;
+		if (arg & TIOCM_OUT2)
+			info->MCR |= UART_MCR_OUT2;
+#endif
+		break;
+	case TIOCMBIC:
+		if (arg & TIOCM_RTS)
+			info->MCR &= ~UART_MCR_RTS;
+		if (arg & TIOCM_DTR)
+			info->MCR &= ~UART_MCR_DTR;
+#ifdef TIOCM_OUT1
+		if (arg & TIOCM_OUT1)
+			info->MCR &= ~UART_MCR_OUT1;
+		if (arg & TIOCM_OUT2)
+			info->MCR &= ~UART_MCR_OUT2;
+#endif
+		break;
+	case TIOCMSET:
+		info->MCR = ((info->MCR & ~(UART_MCR_RTS |
+#ifdef TIOCM_OUT1
+					    UART_MCR_OUT1 |
+					    UART_MCR_OUT2 |
+#endif
+					    UART_MCR_DTR))
+			     | ((arg & TIOCM_RTS) ? UART_MCR_RTS : 0)
+#ifdef TIOCM_OUT1
+			     | ((arg & TIOCM_OUT1) ? UART_MCR_OUT1 : 0)
+			     | ((arg & TIOCM_OUT2) ? UART_MCR_OUT2 : 0)
+#endif
+			     | ((arg & TIOCM_DTR) ? UART_MCR_DTR : 0));
+		break;
+	default:
+		return -EINVAL;
+	}
+	cli();
+	serial_out(info, UART_MCR, info->MCR);
+	sti();
+#endif
+	return 0;
+}
+
+/* Sending a break is a two step process on the SMC/SCC.  It is accomplished
+ * by sending a STOP TRANSMIT command followed by a RESTART TRANSMIT
+ * command.  We take advantage of the begin/end functions to make this
+ * happen.
+ */
+static void begin_break(ser_info_t *info)
+{
+	volatile cpm8xx_t *cp;
+	ushort	chan;
+	ushort	num;
+
+	cp = cpmp;
+
+	if ((num = info->state->smc_scc_num) < SCC_NUM_BASE) {
+		if (num == 0)
+			chan = CPM_CR_CH_SMC1;
+		else
+			chan = CPM_CR_CH_SMC2;
+	}
+	else {
+		num -= SCC_NUM_BASE;
+		switch (num) {
+		case 0: chan = CPM_CR_CH_SCC1; break;
+		case 1: chan = CPM_CR_CH_SCC2; break;
+		case 2: chan = CPM_CR_CH_SCC3; break;
+		case 3: chan = CPM_CR_CH_SCC4; break;
+		default: return;
+		}
+	}
+	cp->cp_cpcr = mk_cr_cmd(chan, CPM_CR_STOP_TX) | CPM_CR_FLG;
+	while (cp->cp_cpcr & CPM_CR_FLG);
+}
+
+static void end_break(ser_info_t *info)
+{
+	volatile cpm8xx_t *cp;
+	ushort	chan;
+	ushort	num;
+
+	cp = cpmp;
+
+	if ((num = info->state->smc_scc_num) < SCC_NUM_BASE) {
+		if (num == 0)
+			chan = CPM_CR_CH_SMC1;
+		else
+			chan = CPM_CR_CH_SMC2;
+	}
+	else {
+		num -= SCC_NUM_BASE;
+		switch (num) {
+		case 0: chan = CPM_CR_CH_SCC1; break;
+		case 1: chan = CPM_CR_CH_SCC2; break;
+		case 2: chan = CPM_CR_CH_SCC3; break;
+		case 3: chan = CPM_CR_CH_SCC4; break;
+		default: return;
+		}
+	}
+	cp->cp_cpcr = mk_cr_cmd(chan, CPM_CR_RESTART_TX) | CPM_CR_FLG;
+	while (cp->cp_cpcr & CPM_CR_FLG);
+}
+
+/*
+ * This routine sends a break character out the serial port.
+ */
+static void send_break(ser_info_t *info, int duration)
+{
+	current->state = TASK_INTERRUPTIBLE;
+	current->timeout = jiffies + duration;
+#ifdef SERIAL_DEBUG_SEND_BREAK
+	printk("rs_send_break(%d) jiff=%lu...", duration, jiffies);
+#endif
+	begin_break(info);
+	schedule();
+	end_break(info);
+#ifdef SERIAL_DEBUG_SEND_BREAK
+	printk("done jiffies=%lu\n", jiffies);
+#endif
+}
+
+
+static int rs_8xx_ioctl(struct tty_struct *tty, struct file * file,
+		    unsigned int cmd, unsigned long arg)
+{
+	int error;
+	ser_info_t *info = (ser_info_t *)tty->driver_data;
+	int retval;
+	struct async_icount cnow;	/* kernel counter temps */
+	struct serial_icounter_struct *p_cuser;	/* user space */
+
+	if (serial_paranoia_check(info, tty->device, "rs_ioctl"))
+		return -ENODEV;
+
+	if ((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 (signal_pending(current))
+				return -EINTR;
+			if (!arg) {
+				send_break(info, HZ/4);	/* 1/4 second */
+				if (signal_pending(current))
+					return -EINTR;
+			}
+			return 0;
+		case TCSBRKP:	/* support for POSIX tcsendbreak() */
+			retval = tty_check_change(tty);
+			if (retval)
+				return retval;
+			tty_wait_until_sent(tty, 0);
+			if (signal_pending(current))
+				return -EINTR;
+			send_break(info, arg ? arg*(HZ/10) : HZ/4);
+			if (signal_pending(current))
+				return -EINTR;
+			return 0;
+		case TIOCSBRK:
+			retval = tty_check_change(tty);
+			if (retval)
+				return retval;
+			tty_wait_until_sent(tty, 0);
+			begin_break(info);
+			return 0;
+		case TIOCCBRK:
+			retval = tty_check_change(tty);
+			if (retval)
+				return retval;
+			end_break(info);
+			return 0;
+		case TIOCGSOFTCAR:
+			return put_user(C_CLOCAL(tty) ? 1 : 0, (int *) arg);
+		case TIOCSSOFTCAR:
+			error = get_user(arg, (unsigned int *) arg);
+			if (error)
+				return error;
+			tty->termios->c_cflag =
+				((tty->termios->c_cflag & ~CLOCAL) |
+				 (arg ? CLOCAL : 0));
+			return 0;
+		case TIOCMGET:
+			return get_modem_info(info, (unsigned int *) arg);
+		case TIOCMBIS:
+		case TIOCMBIC:
+		case TIOCMSET:
+			return set_modem_info(info, cmd, (unsigned int *) arg);
+#ifdef maybe
+		case TIOCSERGETLSR: /* Get line status register */
+			return get_lsr_info(info, (unsigned int *) arg);
+#endif
+		/*
+		 * 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:
+#ifdef modem_control
+			cli();
+			/* note the counters on entry */
+			cprev = info->state->icount;
+			sti();
+			while (1) {
+				interruptible_sleep_on(&info->delta_msr_wait);
+				/* see if a signal did it */
+				if (signal_pending(current))
+					return -ERESTARTSYS;
+				cli();
+				cnow = info->state->icount; /* atomic copy */
+				sti();
+				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 */
+#else
+			return 0;
+#endif
+
+		/* 
+		 * 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:
+			cli();
+			cnow = info->state->icount;
+			sti();
+			p_cuser = (struct serial_icounter_struct *) arg;
+			error = put_user(cnow.cts, &p_cuser->cts);
+			if (error) return error;
+			error = put_user(cnow.dsr, &p_cuser->dsr);
+			if (error) return error;
+			error = put_user(cnow.rng, &p_cuser->rng);
+			if (error) return error;
+			error = put_user(cnow.dcd, &p_cuser->dcd);
+			if (error) return error;
+			return 0;
+
+		default:
+			return -ENOIOCTLCMD;
+		}
+	return 0;
+}
+
+/* FIX UP modem control here someday......
+*/
+static void rs_8xx_set_termios(struct tty_struct *tty, struct termios *old_termios)
+{
+	ser_info_t *info = (ser_info_t *)tty->driver_data;
+
+	if (   (tty->termios->c_cflag == old_termios->c_cflag)
+	    && (   RELEVANT_IFLAG(tty->termios->c_iflag) 
+		== RELEVANT_IFLAG(old_termios->c_iflag)))
+	  return;
+
+	change_speed(info);
+
+#ifdef modem_control
+	/* Handle transition to B0 status */
+	if ((old_termios->c_cflag & CBAUD) &&
+	    !(tty->termios->c_cflag & CBAUD)) {
+		info->MCR &= ~(UART_MCR_DTR|UART_MCR_RTS);
+		cli();
+		serial_out(info, UART_MCR, info->MCR);
+		sti();
+	}
+	
+	/* Handle transition away from B0 status */
+	if (!(old_termios->c_cflag & CBAUD) &&
+	    (tty->termios->c_cflag & CBAUD)) {
+		info->MCR |= UART_MCR_DTR;
+		if (!tty->hw_stopped ||
+		    !(tty->termios->c_cflag & CRTSCTS)) {
+			info->MCR |= UART_MCR_RTS;
+		}
+		cli();
+		serial_out(info, UART_MCR, info->MCR);
+		sti();
+	}
+	
+	/* Handle turning off CRTSCTS */
+	if ((old_termios->c_cflag & CRTSCTS) &&
+	    !(tty->termios->c_cflag & CRTSCTS)) {
+		tty->hw_stopped = 0;
+		rs_8xx_start(tty);
+	}
+#endif
+
+#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_8xx_close(struct tty_struct *tty, struct file * filp)
+{
+	ser_info_t *info = (ser_info_t *)tty->driver_data;
+	struct serial_state *state;
+	unsigned long	flags;
+	int		idx;
+	volatile smc_t	*smcp;
+	volatile scc_t	*sccp;
+
+	if (!info || serial_paranoia_check(info, tty->device, "rs_close"))
+		return;
+
+	state = info->state;
+	
+	save_flags(flags); cli();
+	
+	if (tty_hung_up_p(filp)) {
+		DBG_CNT("before DEC-hung");
+		MOD_DEC_USE_COUNT;
+		restore_flags(flags);
+		return;
+	}
+	
+#ifdef SERIAL_DEBUG_OPEN
+	printk("rs_close ttys%d, count = %d\n", info->line, state->count);
+#endif
+	if ((tty->count == 1) && (state->count != 1)) {
+		/*
+		 * Uh, oh.  tty->count is 1, which means that the tty
+		 * structure will be freed.  state->count should always
+		 * be one in these conditions.  If it's greater than
+		 * one, we've got real problems, since it means the
+		 * serial port won't be shutdown.
+		 */
+		printk("rs_close: bad serial port count; tty->count is 1, "
+		       "state->count is %d\n", state->count);
+		state->count = 1;
+	}
+	if (--state->count < 0) {
+		printk("rs_close: bad serial port count for ttys%d: %d\n",
+		       info->line, state->count);
+		state->count = 0;
+	}
+	if (state->count) {
+		DBG_CNT("before DEC-2");
+		MOD_DEC_USE_COUNT;
+		restore_flags(flags);
+		return;
+	}
+	info->flags |= ASYNC_CLOSING;
+	/*
+	 * Save the termios structure, since this port may have
+	 * separate termios for callout and dialin.
+	 */
+	if (info->flags & ASYNC_NORMAL_ACTIVE)
+		info->state->normal_termios = *tty->termios;
+	if (info->flags & ASYNC_CALLOUT_ACTIVE)
+		info->state->callout_termios = *tty->termios;
+	/*
+	 * Now we wait for the transmit buffer to clear; and we notify 
+	 * the line discipline to only process XON/XOFF characters.
+	 */
+	tty->closing = 1;
+	if (info->closing_wait != ASYNC_CLOSING_WAIT_NONE)
+		tty_wait_until_sent(tty, info->closing_wait);
+	/*
+	 * At this point we stop accepting input.  To do this, we
+	 * disable the receive line status interrupts, and tell the
+	 * interrupt driver to stop checking the data ready bit in the
+	 * line status register.
+	 */
+	info->read_status_mask &= ~BD_SC_EMPTY;
+	if (info->flags & ASYNC_INITIALIZED) {
+		if ((idx = info->state->smc_scc_num) < SCC_NUM_BASE) {
+			smcp = &cpmp->cp_smc[idx];
+			smcp->smc_smcm &= ~SMCM_RX;
+			smcp->smc_smcmr &= ~SMCMR_REN;
+		}
+		else {
+			sccp = &cpmp->cp_scc[idx - SCC_NUM_BASE];
+			sccp->scc_sccm &= ~UART_SCCM_RX;
+		}
+		/*
+		 * Before we drop DTR, make sure the UART transmitter
+		 * has completely drained; this is especially
+		 * important if there is a transmit FIFO!
+		 */
+		rs_8xx_wait_until_sent(tty, info->timeout);
+	}
+	shutdown(info);
+	if (tty->driver.flush_buffer)
+		tty->driver.flush_buffer(tty);
+	if (tty->ldisc.flush_buffer)
+		tty->ldisc.flush_buffer(tty);
+	tty->closing = 0;
+	info->event = 0;
+	info->tty = 0;
+	if (info->blocked_open) {
+		if (info->close_delay) {
+			current->state = TASK_INTERRUPTIBLE;
+			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_wait_until_sent() --- wait until the transmitter is empty
+ */
+static void rs_8xx_wait_until_sent(struct tty_struct *tty, int timeout)
+{
+	ser_info_t *info = (ser_info_t *)tty->driver_data;
+	unsigned long orig_jiffies, char_time;
+	int lsr;
+	volatile cbd_t *bdp;
+	
+	if (serial_paranoia_check(info, tty->device, "rs_wait_until_sent"))
+		return;
+
+#ifdef maybe
+	if (info->state->type == PORT_UNKNOWN)
+		return;
+#endif
+
+	orig_jiffies = jiffies;
+	/*
+	 * Set the check interval to be 1/5 of the estimated time to
+	 * send a single character, and make it at least 1.  The check
+	 * interval should also be less than the timeout.
+	 * 
+	 * Note: we have to use pretty tight timings here to satisfy
+	 * the NIST-PCTS.
+	 */
+	char_time = 1;
+	if (timeout)
+		char_time = MIN(char_time, timeout);
+#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
+	printk("In rs_wait_until_sent(%d) check=%lu...", timeout, char_time);
+	printk("jiff=%lu...", jiffies);
+#endif
+
+	/* We go through the loop at least once because we can't tell
+	 * exactly when the last character exits the shifter.  There can
+	 * be at least two characters waiting to be sent after the buffers
+	 * are empty.
+	 */
+	do {
+#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
+		printk("lsr = %d (jiff=%lu)...", lsr, jiffies);
+#endif
+		current->state = TASK_INTERRUPTIBLE;
+/*		current->counter = 0;	/* make us low-priority */
+		current->timeout = jiffies + char_time;
+		schedule();
+		if (signal_pending(current))
+			break;
+		if (timeout && ((orig_jiffies + timeout) < jiffies))
+			break;
+		bdp = info->tx_cur;
+	} while (bdp->cbd_sc & BD_SC_READY);
+	current->state = TASK_RUNNING;
+#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
+	printk("lsr = %d (jiff=%lu)...done\n", lsr, jiffies);
+#endif
+}
+
+/*
+ * rs_hangup() --- called by tty_hangup() when a hangup is signaled.
+ */
+static void rs_8xx_hangup(struct tty_struct *tty)
+{
+	ser_info_t *info = (ser_info_t *)tty->driver_data;
+	struct serial_state *state = info->state;
+	
+	if (serial_paranoia_check(info, tty->device, "rs_hangup"))
+		return;
+
+	state = info->state;
+	
+	rs_8xx_flush_buffer(tty);
+	shutdown(info);
+	info->event = 0;
+	state->count = 0;
+	info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE);
+	info->tty = 0;
+	wake_up_interruptible(&info->open_wait);
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_open() and friends
+ * ------------------------------------------------------------
+ */
+static int block_til_ready(struct tty_struct *tty, struct file * filp,
+			   ser_info_t *info)
+{
+#ifdef DO_THIS_LATER
+	struct wait_queue wait = { current, NULL };
+#endif
+	struct serial_state *state = info->state;
+	int		retval;
+	int		do_clocal = 0;
+
+	/*
+	 * 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 this is an SMC port, we don't have modem control to wait
+	 * for, so just get out here.
+	 */
+	if ((filp->f_flags & O_NONBLOCK) ||
+	    (tty->flags & (1 << TTY_IO_ERROR)) ||
+	    (info->state->smc_scc_num < SCC_NUM_BASE)) {
+		if (info->flags & ASYNC_CALLOUT_ACTIVE)
+			return -EBUSY;
+		info->flags |= ASYNC_NORMAL_ACTIVE;
+		return 0;
+	}
+
+	if (info->flags & ASYNC_CALLOUT_ACTIVE) {
+		if (state->normal_termios.c_cflag & CLOCAL)
+			do_clocal = 1;
+	} else {
+		if (tty->termios->c_cflag & CLOCAL)
+			do_clocal = 1;
+	}
+	
+	/*
+	 * Block waiting for the carrier detect and the line to become
+	 * free (i.e., not in use by the callout).  While we are in
+	 * this loop, state->count is dropped by one, so that
+	 * rs_close() knows when to free things.  We restore it upon
+	 * exit, either normal or abnormal.
+	 */
+	retval = 0;
+#ifdef DO_THIS_LATER
+	add_wait_queue(&info->open_wait, &wait);
+#ifdef SERIAL_DEBUG_OPEN
+	printk("block_til_ready before block: ttys%d, count = %d\n",
+	       state->line, state->count);
+#endif
+	cli();
+	if (!tty_hung_up_p(filp)) 
+		state->count--;
+	sti();
+	info->blocked_open++;
+	while (1) {
+		cli();
+		if (!(info->flags & ASYNC_CALLOUT_ACTIVE) &&
+		    (tty->termios->c_cflag & CBAUD))
+			serial_out(info, UART_MCR,
+				   serial_inp(info, UART_MCR) |
+				   (UART_MCR_DTR | UART_MCR_RTS));
+		sti();
+		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 || (serial_in(info, UART_MSR) &
+				   UART_MSR_DCD)))
+			break;
+		if (signal_pending(current)) {
+			retval = -ERESTARTSYS;
+			break;
+		}
+#ifdef SERIAL_DEBUG_OPEN
+		printk("block_til_ready blocking: ttys%d, count = %d\n",
+		       info->line, state->count);
+#endif
+		schedule();
+	}
+	current->state = TASK_RUNNING;
+	remove_wait_queue(&info->open_wait, &wait);
+	if (!tty_hung_up_p(filp))
+		state->count++;
+	info->blocked_open--;
+#ifdef SERIAL_DEBUG_OPEN
+	printk("block_til_ready after blocking: ttys%d, count = %d\n",
+	       info->line, state->count);
+#endif
+#endif /* DO_THIS_LATER */
+	if (retval)
+		return retval;
+	info->flags |= ASYNC_NORMAL_ACTIVE;
+	return 0;
+}
+
+static int get_async_struct(int line, ser_info_t **ret_info)
+{
+	struct serial_state *sstate;
+
+	sstate = rs_table + line;
+	if (sstate->info) {
+		sstate->count++;
+		*ret_info = (ser_info_t *)sstate->info;
+		return 0;
+	}
+	else {
+		return -ENOMEM;
+	}
+}
+
+/*
+ * This routine is called whenever a serial port is opened.  It
+ * enables interrupts for a serial port, linking in its async structure into
+ * the IRQ chain.   It also performs the serial-specific
+ * initialization for the tty structure.
+ */
+static int rs_8xx_open(struct tty_struct *tty, struct file * filp)
+{
+	ser_info_t	*info;
+	int 		retval, line;
+
+	line = MINOR(tty->device) - tty->driver.minor_start;
+	if ((line < 0) || (line >= NR_PORTS))
+		return -ENODEV;
+	retval = get_async_struct(line, &info);
+	if (retval)
+		return retval;
+	if (serial_paranoia_check(info, tty->device, "rs_open"))
+		return -ENODEV;
+
+#ifdef SERIAL_DEBUG_OPEN
+	printk("rs_open %s%d, count = %d\n", tty->driver.name, info->line,
+	       info->state->count);
+#endif
+	tty->driver_data = info;
+	info->tty = tty;
+
+	/*
+	 * 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->state->count == 1) &&
+	    (info->flags & ASYNC_SPLIT_TERMIOS)) {
+		if (tty->driver.subtype == SERIAL_TYPE_NORMAL)
+			*tty->termios = info->state->normal_termios;
+		else 
+			*tty->termios = info->state->callout_termios;
+		change_speed(info);
+	}
+
+	info->session = current->session;
+	info->pgrp = current->pgrp;
+
+#ifdef SERIAL_DEBUG_OPEN
+	printk("rs_open ttys%d successful...", info->line);
+#endif
+	return 0;
+}
+
+/*
+ * /proc fs routines....
+ */
+
+static int inline line_info(char *buf, struct serial_state *state)
+{
+#ifdef notdef
+	struct async_struct *info = state->info, scr_info;
+	char	stat_buf[30], control, status;
+#endif
+	int	ret;
+
+	ret = sprintf(buf, "%d: uart:%s port:%X irq:%d",
+		      state->line,
+		      (state->smc_scc_num < SCC_NUM_BASE) ? "SMC" : "SCC",
+		      state->port, state->irq);
+
+	if (!state->port || (state->type == PORT_UNKNOWN)) {
+		ret += sprintf(buf+ret, "\n");
+		return ret;
+	}
+
+#ifdef notdef
+	/*
+	 * Figure out the current RS-232 lines
+	 */
+	if (!info) {
+		info = &scr_info;	/* This is just for serial_{in,out} */
+
+		info->magic = SERIAL_MAGIC;
+		info->port = state->port;
+		info->flags = state->flags;
+		info->quot = 0;
+		info->tty = 0;
+	}
+	cli();
+	status = serial_in(info, UART_MSR);
+	control = info ? info->MCR : serial_in(info, UART_MCR);
+	sti();
+	
+	stat_buf[0] = 0;
+	stat_buf[1] = 0;
+	if (control & UART_MCR_RTS)
+		strcat(stat_buf, "|RTS");
+	if (status & UART_MSR_CTS)
+		strcat(stat_buf, "|CTS");
+	if (control & UART_MCR_DTR)
+		strcat(stat_buf, "|DTR");
+	if (status & UART_MSR_DSR)
+		strcat(stat_buf, "|DSR");
+	if (status & UART_MSR_DCD)
+		strcat(stat_buf, "|CD");
+	if (status & UART_MSR_RI)
+		strcat(stat_buf, "|RI");
+
+	if (info->quot) {
+		ret += sprintf(buf+ret, " baud:%d",
+			       state->baud_base / info->quot);
+	}
+
+	ret += sprintf(buf+ret, " tx:%d rx:%d",
+		      state->icount.tx, state->icount.rx);
+
+	if (state->icount.frame)
+		ret += sprintf(buf+ret, " fe:%d", state->icount.frame);
+	
+	if (state->icount.parity)
+		ret += sprintf(buf+ret, " pe:%d", state->icount.parity);
+	
+	if (state->icount.brk)
+		ret += sprintf(buf+ret, " brk:%d", state->icount.brk);	
+
+	if (state->icount.overrun)
+		ret += sprintf(buf+ret, " oe:%d", state->icount.overrun);
+
+	/*
+	 * Last thing is the RS-232 status lines
+	 */
+	ret += sprintf(buf+ret, " %s\n", stat_buf+1);
+#endif
+	return ret;
+}
+
+int rs_8xx_read_proc(char *page, char **start, off_t off, int count,
+		 int *eof, void *data)
+{
+	int i, len = 0;
+	off_t	begin = 0;
+
+	len += sprintf(page, "serinfo:1.0 driver:%s\n", serial_version);
+	for (i = 0; i < NR_PORTS && len < 4000; i++) {
+		len += line_info(page + len, &rs_table[i]);
+		if (len+begin > off+count)
+			goto done;
+		if (len+begin < off) {
+			begin += len;
+			len = 0;
+		}
+	}
+	*eof = 1;
+done:
+	if (off >= len+begin)
+		return 0;
+	*start = page + (begin-off);
+	return ((count < begin+len-off) ? count : begin+len-off);
+}
+
+/*
+ * ---------------------------------------------------------------------
+ * rs_init() and friends
+ *
+ * rs_init() is called at boot-time to initialize the serial driver.
+ * ---------------------------------------------------------------------
+ */
+
+/*
+ * This routine prints out the appropriate serial driver version
+ * number, and identifies which options were configured into this
+ * driver.
+ */
+static _INLINE_ void show_serial_version(void)
+{
+ 	printk(KERN_INFO "%s version %s\n", serial_name, serial_version);
+}
+
+/*
+ * The serial driver boot-time initialization code!
+ */
+__initfunc(int rs_8xx_init(void))
+{
+	struct serial_state * state;
+	ser_info_t	*info;
+	uint		mem_addr, dp_addr;
+	int		i, j;
+	ushort		chan;
+	volatile	cbd_t		*bdp;
+	volatile	cpm8xx_t	*cp;
+	volatile	smc_t		*sp;
+	volatile	smc_uart_t	*up;
+	
+	init_bh(SERIAL_BH, do_serial_bh);
+#if 0
+	timer_table[RS_TIMER].fn = rs_8xx_timer;
+	timer_table[RS_TIMER].expires = 0;
+#endif
+
+	show_serial_version();
+
+	/* Initialize the tty_driver structure */
+	
+	memset(&serial_driver, 0, sizeof(struct tty_driver));
+	serial_driver.magic = TTY_DRIVER_MAGIC;
+	serial_driver.driver_name = "serial";
+	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_8xx_open;
+	serial_driver.close = rs_8xx_close;
+	serial_driver.write = rs_8xx_write;
+	serial_driver.put_char = rs_8xx_put_char;
+	serial_driver.write_room = rs_8xx_write_room;
+	serial_driver.chars_in_buffer = rs_8xx_chars_in_buffer;
+	serial_driver.flush_buffer = rs_8xx_flush_buffer;
+	serial_driver.ioctl = rs_8xx_ioctl;
+	serial_driver.throttle = rs_8xx_throttle;
+	serial_driver.unthrottle = rs_8xx_unthrottle;
+	serial_driver.send_xchar = rs_8xx_send_xchar;
+	serial_driver.set_termios = rs_8xx_set_termios;
+	serial_driver.stop = rs_8xx_stop;
+	serial_driver.start = rs_8xx_start;
+	serial_driver.hangup = rs_8xx_hangup;
+	serial_driver.wait_until_sent = rs_8xx_wait_until_sent;
+	serial_driver.read_proc = rs_8xx_read_proc;
+	
+	/*
+	 * The callout device is just like normal device except for
+	 * major number and the subtype code.
+	 */
+	callout_driver = serial_driver;
+	callout_driver.name = "cua";
+	callout_driver.major = TTYAUX_MAJOR;
+	callout_driver.subtype = SERIAL_TYPE_CALLOUT;
+	callout_driver.read_proc = 0;
+	callout_driver.proc_entry = 0;
+
+	if (tty_register_driver(&serial_driver))
+		panic("Couldn't register serial driver\n");
+	if (tty_register_driver(&callout_driver))
+		panic("Couldn't register callout driver\n");
+	
+	cp = cpmp;	/* Get pointer to Communication Processor */
+
+	/* Configure SMCs Tx/Rx instead of port B parallel I/O.
+	*/
+	cp->cp_pbpar |= 0x00000cc0;
+	cp->cp_pbdir &= ~0x00000cc0;
+	cp->cp_pbodr &= ~0x00000cc0;
+
+	/* Wire BRG1 to SMC1 and BRG2 to SMC2.
+	*/
+	cp->cp_simode = 0x10000000;
+
+	for (i = 0, state = rs_table; i < NR_PORTS; i++,state++) {
+		state->magic = SSTATE_MAGIC;
+		state->line = i;
+		state->type = PORT_UNKNOWN;
+		state->custom_divisor = 0;
+		state->close_delay = 5*HZ/10;
+		state->closing_wait = 30*HZ;
+		state->callout_termios = callout_driver.init_termios;
+		state->normal_termios = serial_driver.init_termios;
+		state->icount.cts = state->icount.dsr = 
+			state->icount.rng = state->icount.dcd = 0;
+		state->icount.rx = state->icount.tx = 0;
+		state->icount.frame = state->icount.parity = 0;
+		state->icount.overrun = state->icount.brk = 0;
+		printk(KERN_INFO "ttyS%02d at 0x%04x is a %s\n",
+		       i, state->port,
+		       (state->smc_scc_num < SCC_NUM_BASE) ? "SMC" : "SCC");
+#ifdef CONFIG_SERIAL_CONSOLE
+		/* If we just printed the message on the console port, and
+		 * we are about to initialize it for general use, we have
+		 * to wait a couple of character times for the CR/NL to
+		 * make it out of the transmit buffer.
+		 */
+		if (i == CONFIG_SERIAL_CONSOLE_PORT)
+			udelay(2000);
+#endif
+		info = kmalloc(sizeof(ser_info_t), GFP_KERNEL);
+		if (info) {
+			memset(info, 0, sizeof(ser_info_t));
+			info->magic = SERIAL_MAGIC;
+			info->flags = state->flags;
+			info->tqueue.routine = do_softint;
+			info->tqueue.data = info;
+			info->tqueue_hangup.routine = do_serial_hangup;
+			info->tqueue_hangup.data = info;
+			info->line = i;
+			info->state = state;
+			state->info = (struct async_struct *)info;
+
+			/* Right now, assume we are using SMCs.
+			*/
+			sp = &cp->cp_smc[state->smc_scc_num];
+
+			up = (smc_uart_t *)&cp->cp_dparam[state->port];
+
+			/* We need to allocate a transmit and receive buffer
+			 * descriptors from dual port ram, and a character
+			 * buffer area from host mem.
+			 */
+			dp_addr = mbx_cpm_dpalloc(sizeof(cbd_t) * RX_NUM_FIFO);
+
+			/* Allocate space for FIFOs in the host memory.
+			*/
+			mem_addr = mbx_cpm_hostalloc(RX_NUM_FIFO * RX_BUF_SIZE);
+
+			/* Set the physical address of the host memory
+			 * buffers in the buffer descriptors, and the
+			 * virtual address for us to work with.
+			 */
+			bdp = (cbd_t *)&cp->cp_dpmem[dp_addr];
+			up->smc_rbase = dp_addr;
+			info->rx_cur = info->rx_bd_base = (cbd_t *)bdp;
+
+			for (j=0; j<(RX_NUM_FIFO-1); j++) {
+				bdp->cbd_bufaddr = __pa(mem_addr);
+				bdp->cbd_sc = BD_SC_EMPTY | BD_SC_INTRPT;
+				mem_addr += RX_BUF_SIZE;
+				bdp++;
+			}
+			bdp->cbd_bufaddr = __pa(mem_addr);
+			bdp->cbd_sc = BD_SC_WRAP | BD_SC_EMPTY | BD_SC_INTRPT;
+
+			dp_addr = mbx_cpm_dpalloc(sizeof(cbd_t) * TX_NUM_FIFO);
+
+			/* Allocate space for FIFOs in the host memory.
+			*/
+			mem_addr = mbx_cpm_hostalloc(TX_NUM_FIFO * TX_BUF_SIZE);
+
+			/* Set the physical address of the host memory
+			 * buffers in the buffer descriptors, and the
+			 * virtual address for us to work with.
+			 */
+			bdp = (cbd_t *)&cp->cp_dpmem[dp_addr];
+			up->smc_tbase = dp_addr;
+			info->tx_cur = info->tx_bd_base = (cbd_t *)bdp;
+
+			for (j=0; j<(TX_NUM_FIFO-1); j++) {
+				bdp->cbd_bufaddr = __pa(mem_addr);
+				bdp->cbd_sc = BD_SC_INTRPT;
+				mem_addr += TX_BUF_SIZE;
+				bdp++;
+			}
+			bdp->cbd_bufaddr = __pa(mem_addr);
+			bdp->cbd_sc = (BD_SC_WRAP | BD_SC_INTRPT);
+
+			/* Set up the uart parameters in the parameter ram.
+			*/
+			up->smc_rfcr = SMC_EB;
+			up->smc_tfcr = SMC_EB;
+
+			/* Set this to 1 for now, so we get single character
+			 * interrupts.  Using idle charater time requires
+			 * some additional tuning.
+			 */
+			up->smc_mrblr = 1;	/* receive buffer length */
+			up->smc_maxidl = 0;	/* wait forever for next char */
+			up->smc_brkcr = 1;	/* number of break chars */
+
+			/* Send the CPM an initialize command.
+			*/
+			if (state->smc_scc_num == 0)
+				chan = CPM_CR_CH_SMC1;
+			else
+				chan = CPM_CR_CH_SMC2;
+			cp->cp_cpcr = mk_cr_cmd(chan,
+						CPM_CR_INIT_TRX) | CPM_CR_FLG;
+			while (cp->cp_cpcr & CPM_CR_FLG);
+
+			/* Set UART mode, 8 bit, no parity, one stop.
+			 * Enable receive and transmit.
+			 */
+			sp->smc_smcmr = smcr_mk_clen(9) |  SMCMR_SM_UART;
+
+			/* Disable all interrupts and clear all pending
+			 * events.
+			 */
+			sp->smc_smcm = 0;
+			sp->smc_smce = 0xff;
+
+			/* Install interrupt handler.
+			*/
+			cpm_install_handler(state->irq, rs_8xx_interrupt, info);
+
+			/* Set up the baud rate generator.
+			*/
+			mbx_cpm_setbrg(state->smc_scc_num, 9600);
+
+			/* If the port is the console, enable Rx and Tx.
+			*/
+#ifdef CONFIG_SERIAL_CONSOLE
+			if (i == CONFIG_SERIAL_CONSOLE_PORT)
+				sp->smc_smcmr |= SMCMR_REN | SMCMR_TEN;
+#endif
+		}
+	}
+	return 0;
+}
+
+/*
+ * The serial console driver used during boot.  Note that these names
+ * clash with those found in "serial.c", so we currently can't support
+ * the 16xxx uarts and these at the same time.  I will fix this to become
+ * an indirect function call from tty_io.c (or something).
+ */
+
+#ifdef CONFIG_SERIAL_CONSOLE
+
+/*
+ * Print a string to the serial port trying not to disturb any possible
+ * real use of the port...
+ */
+static void serial_console_write(struct console *c, const char *s,
+				unsigned count)
+{
+	struct		serial_state	*ser;
+	ser_info_t			*info;
+	unsigned			i;
+	volatile	cbd_t		*bdp, *bdbase;
+	volatile	smc_uart_t	*up;
+	volatile	u_char		*cp;
+
+	ser = rs_table + c->index;
+
+	/* If the port has been initialized for general use, we have
+	 * to use the buffer descriptors allocated there.  Otherwise,
+	 * we simply use the single buffer allocated.
+	 */
+	if ((info = (ser_info_t *)ser->info) != NULL) {
+		bdp = info->tx_cur;
+		bdbase = info->tx_bd_base;
+	}
+	else {
+		/* Pointer to UART in parameter ram.
+		*/
+		up = (smc_uart_t *)&cpmp->cp_dparam[ser->port];
+
+		/* Get the address of the host memory buffer.
+		 */
+		bdp = bdbase = (cbd_t *)&cpmp->cp_dpmem[up->smc_tbase];
+	}
+
+	/*
+	 * We need to gracefully shut down the transmitter, disable
+	 * interrupts, then send our bytes out.
+	 */
+
+	/*
+	 * Now, do each character.  This is not as bad as it looks
+	 * since this is a holding FIFO and not a transmitting FIFO.
+	 * We could add the complexity of filling the entire transmit
+	 * buffer, but we would just wait longer between accesses......
+	 */
+	for (i = 0; i < count; i++, s++) {
+
+		/* Wait for transmitter fifo to empty.
+		 * Ready indicates output is ready, and xmt is doing
+		 * that, not that it is ready for us to send.
+		 */
+		while (bdp->cbd_sc & BD_SC_READY);
+
+		/* Send the character out. */
+		cp = __va(bdp->cbd_bufaddr);
+		*cp = *s;
+		bdp->cbd_datlen = 1;
+		bdp->cbd_sc |= BD_SC_READY;
+
+		if (bdp->cbd_sc & BD_SC_WRAP)
+			bdp = bdbase;
+		else
+			bdp++;
+
+		/* if a LF, also do CR... */
+		if (*s == 10) {
+			while (bdp->cbd_sc & BD_SC_READY);
+			cp = __va(bdp->cbd_bufaddr);
+			*cp = 13;
+			bdp->cbd_datlen = 1;
+			bdp->cbd_sc |= BD_SC_READY;
+
+			if (bdp->cbd_sc & BD_SC_WRAP) {
+				bdp = bdbase;
+			}
+			else {
+				bdp++;
+			}
+		}
+	}
+
+	/*
+	 * Finally, Wait for transmitter & holding register to empty
+	 *  and restore the IER
+	 */
+	while (bdp->cbd_sc & BD_SC_READY);
+
+	if (info)
+		info->tx_cur = (cbd_t *)bdp;
+}
+
+/*
+ * Receive character from the serial port.  This only works well
+ * before the port is initialize for real use.
+ */
+static int serial_console_wait_key(struct console *co)
+{
+	struct serial_state		*ser;
+	u_char				c, *cp;
+	ser_info_t			*info;
+	volatile	cbd_t		*bdp;
+	volatile	smc_uart_t	*up;
+
+	ser = rs_table + co->index;
+
+	/* Pointer to UART in parameter ram.
+	*/
+	up = (smc_uart_t *)&cpmp->cp_dparam[ser->port];
+
+	/* Get the address of the host memory buffer.
+	 * If the port has been initialized for general use, we must
+	 * use information from the port structure.
+	 */
+	if ((info = ser->info))
+		bdp = info->rx_cur;
+	else
+		bdp = (cbd_t *)&cpmp->cp_dpmem[up->smc_rbase];
+
+	/*
+	 * We need to gracefully shut down the receiver, disable
+	 * interrupts, then read the input.
+	 */
+	while (bdp->cbd_sc & BD_SC_EMPTY);	/* Wait for a character */
+	cp = __va(bdp->cbd_bufaddr);
+
+	if (info) {
+		if (bdp->cbd_sc & BD_SC_WRAP) {
+			bdp = info->rx_bd_base;
+		}
+		else {
+			bdp++;
+		}
+		info->rx_cur = (cbd_t *)bdp;
+	}
+
+	c = *cp;
+	return((int)c);
+}
+
+static kdev_t serial_console_device(struct console *c)
+{
+	return MKDEV(TTYAUX_MAJOR, 64 + c->index);
+}
+
+/* This must always be called before the rs_8xx_init() function, otherwise
+ * it blows away the port control information.
+*/
+__initfunc(static int serial_console_setup(struct console *co, char *options))
+{
+	struct		serial_state *ser;
+	uint		mem_addr, dp_addr;
+	volatile	cbd_t		*bdp;
+	volatile	cpm8xx_t	*cp;
+	volatile	smc_t		*sp;
+	volatile	smc_uart_t	*up;
+
+	co->cflag = CREAD|CLOCAL|B9600|CS8;
+
+	ser = rs_table + co->index;
+
+	cp = cpmp;	/* Get pointer to Communication Processor */
+
+	/* Right now, assume we are using SMCs.
+	*/
+	sp = &cp->cp_smc[ser->smc_scc_num];
+
+	/* When we get here, the CPM has been reset, so we need
+	 * to configure the port.
+	 * We need to allocate a transmit and receive buffer descriptor
+	 * from dual port ram, and a character buffer area from host mem.
+	 */
+	up = (smc_uart_t *)&cp->cp_dparam[ser->port];
+	cp->cp_pbpar = 0x00c0;	/* Enable SMC1 instead of Port B I/O */
+
+	/* Allocate space for two buffer descriptors in the DP ram.
+	*/
+	dp_addr = mbx_cpm_dpalloc(sizeof(cbd_t) * 2);
+
+	/* Allocate space for two 2 byte FIFOs in the host memory.
+	*/
+	mem_addr = mbx_cpm_hostalloc(4);
+
+	/* Set the physical address of the host memory buffers in
+	 * the buffer descriptors.
+	 */
+	bdp = (cbd_t *)&cp->cp_dpmem[dp_addr];
+	bdp->cbd_bufaddr = __pa(mem_addr);
+	(bdp+1)->cbd_bufaddr = __pa(mem_addr+2);
+
+	/* For the receive, set empty and wrap.
+	 * For transmit, set wrap.
+	 */
+	bdp->cbd_sc = BD_SC_EMPTY | BD_SC_WRAP;
+	(bdp+1)->cbd_sc = BD_SC_WRAP;
+
+	/* Set up the uart parameters in the parameter ram.
+	*/
+	up->smc_rbase = dp_addr;	/* Base of receive buffer desc. */
+	up->smc_tbase = dp_addr+sizeof(cbd_t);	/* Base of xmt buffer desc. */
+	up->smc_rfcr = SMC_EB;
+	up->smc_tfcr = SMC_EB;
+
+	/* Set this to 1 for now, so we get single character interrupts.
+	*/
+	up->smc_mrblr = 1;		/* receive buffer length */
+	up->smc_maxidl = 0;		/* wait forever for next char */
+
+	/* Send the CPM an initialize command.
+	*/
+	cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SMC1, CPM_CR_INIT_TRX) | CPM_CR_FLG;
+	while (cp->cp_cpcr & CPM_CR_FLG);
+
+	/* Set UART mode, 8 bit, no parity, one stop.
+	 * Enable receive and transmit.
+	 */
+	sp->smc_smcmr = smcr_mk_clen(9) |  SMCMR_SM_UART;
+
+	/* Set up the baud rate generator.
+	*/
+	mbx_cpm_setbrg(ser->smc_scc_num, 9600);
+ 
+	/* And finally, enable Rx and Tx.
+	*/
+	sp->smc_smcmr |= SMCMR_REN | SMCMR_TEN;
+
+	return 0;
+}
+
+static struct console sercons = {
+	"ttyS",
+	serial_console_write,
+	NULL,
+	serial_console_device,
+	serial_console_wait_key,
+	NULL,
+	serial_console_setup,
+	CON_PRINTBUFFER,
+	CONFIG_SERIAL_CONSOLE_PORT,
+	0,
+	NULL
+};
+
+/*
+ *	Register console.
+ */
+__initfunc (long console_8xx_init(long kmem_start, long kmem_end))
+{
+	register_console(&sercons);
+	return kmem_start;
+}
+
+#endif
diff -u --recursive --new-file v2.1.96/linux/arch/ppc/Makefile linux/arch/ppc/Makefile
--- v2.1.96/linux/arch/ppc/Makefile	Mon Jan 12 15:18:12 1998
+++ linux/arch/ppc/Makefile	Tue Apr 14 17:33:40 1998
@@ -12,14 +12,7 @@
 # Rewritten by Cort Dougan and Paul Mackerras
 #
 
-ifdef CONFIG_CHRP
-# XXX for now
-KERNELBASE	=0x90000000
-KERNELLOAD	=0x90010000
-else
-KERNELBASE	=0xc0000000 
-KERNELLOAD	=0xc0000000 
-endif
+KERNELLOAD	=0xc0000000
 
 # PowerPC (cross) tools
 ifneq ($(shell uname -m),ppc)
@@ -32,21 +25,13 @@
 LINKFLAGS	= -T arch/ppc/vmlinux.lds -Ttext $(KERNELLOAD) -Bstatic
 CFLAGSINC	= -D__KERNEL__ -I$(TOPDIR)/include -D__powerpc__
 CFLAGS		:= $(CFLAGS) -D__powerpc__ -fsigned-char -msoft-float -pipe \
-		-fno-builtin -ffixed-r2 -Wno-uninitialized -mmultiple -mstring \
-		-DKERNELBASE=$(KERNELBASE)
+		-fno-builtin -ffixed-r2 -Wno-uninitialized -mmultiple -mstring
 CPP		= $(CC) -E $(CFLAGS)
 
-ifdef CONFIG_601
-CFLAGS := $(CFLAGS) -mcpu=601 -DCPU=601
+ifdef CONFIG_8xx
+CFLAGS := $(CFLAGS) -mcpu=860
 endif
 
-ifdef CONFIG_603
-CFLAGS := $(CFLAGS) -mcpu=603 -DCPU=603
-endif
-
-ifdef CONFIG_604
-CFLAGS := $(CFLAGS) -mcpu=604 -DCPU=604
-endif
 
 HEAD := arch/ppc/kernel/head.o
 
@@ -64,6 +49,11 @@
 MAKECOFFBOOT = $(MAKE) -C arch/$(ARCH)/coffboot
 MAKECHRPBOOT = $(MAKE) -C arch/$(ARCH)/chrpboot
 
+ifdef CONFIG_8xx
+SUBDIRS += arch/ppc/8xx_io
+DRIVERS += arch/ppc/8xx_io/8xx_io.a drivers/net/net.a
+endif
+
 checks:
 	@$(MAKE) -C arch/$(ARCH)/kernel checks
 
@@ -82,6 +72,19 @@
 prep_config:
 	rm -f .config arch/ppc/defconfig
 	ln -s prep_defconfig arch/ppc/defconfig
+
+chrp_config:
+	rm -f .config arch/ppc/defconfig
+	ln -s chrp_defconfig arch/ppc/defconfig
+
+common_config:
+	rm -f .config arch/ppc/common_defconfig
+	ln -s common_defconfig arch/ppc/defconfig
+
+mbx_config:
+	rm -f .config arch/ppc/defconfig
+	ln -s mbx_defconfig arch/ppc/defconfig
+
 
 tags:
 	etags */*.c include/{asm,linux}/*.h arch/ppc/kernel/*.{c,h}
diff -u --recursive --new-file v2.1.96/linux/arch/ppc/boot/Makefile linux/arch/ppc/boot/Makefile
--- v2.1.96/linux/arch/ppc/boot/Makefile	Mon Jan 12 15:18:12 1998
+++ linux/arch/ppc/boot/Makefile	Tue Apr 14 17:33:40 1998
@@ -25,31 +25,70 @@
 ZSZ = 0
 IOFF = 0
 ISZ = 0
+ifeq ($(CONFIG_MBX),y)
+ZLINKFLAGS = -T ../vmlinux.lds -Ttext 0x00100000
+else
 #ZLINKFLAGS = -T ../vmlinux.lds -Ttext 0x00800000
 ZLINKFLAGS = -T ../vmlinux.lds -Ttext 0x00600000
+endif
 GZIP_FLAGS = -v9
 
-OBJECTS := head.o misc.o vreset.o kbd.o ../coffboot/zlib.o # inflate.o unzip.o
-#OBJECTS := crt0.o start.o vreset.o
+OBJECTS := head.o misc.o ../coffboot/zlib.o # inflate.o unzip.o
 CFLAGS = -O2 -DSTDC_HEADERS -fno-builtin -I$(TOPDIR)/include
 OBJCOPY = $(CROSS_COMPILE)objcopy
 OBJCOPY_ARGS = -O elf32-powerpc
 
+ifeq ($(CONFIG_MBX),y)
+OBJECTS += mbxtty.o
+CFLAGS += -DCONFIG_MBX
+else
+OBJECTS += vreset.o kbd.o
+endif
+
 all:	zImage
 
+ifeq ($(CONFIG_ALL_PPC),y)
+CONFIG_PREP = y
+endif
 
 ifeq ($(CONFIG_PREP),y)
-mkprep : mkprep.c
-	$(HOSTCC) -DKERNELBASE=$(KERNELBASE) -o mkprep mkprep.c
+zvmlinux.initrd: zvmlinux
+	$(LD) $(ZLINKFLAGS) -o zvmlinux.initrd.tmp $(OBJECTS)
+	$(OBJCOPY) $(OBJCOPY_ARGS) -R .comment \
+		--add-section=initrd=ramdisk.image.gz \
+		--add-section=image=../coffboot/vmlinux.gz \
+		zvmlinux.initrd.tmp zvmlinux.initrd
+	$(CC) $(CFLAGS) -DINITRD_OFFSET=`sh offset zvmlinux.initrd initrd` \
+		-DINITRD_SIZE=`sh size zvmlinux.initrd initrd` \
+		-DZIMAGE_OFFSET=`sh offset zvmlinux.initrd image` \
+		-DZIMAGE_SIZE=`sh size zvmlinux.initrd image` \
+		-DKERNELBASE=$(KERNELBASE) -c -o misc.o misc.c
+	$(LD) $(ZLINKFLAGS) -o zvmlinux.initrd.tmp $(OBJECTS)
+	$(OBJCOPY) $(OBJCOPY_ARGS) -R .comment \
+		--add-section=initrd=ramdisk.image.gz \
+		--add-section=image=../coffboot/vmlinux.gz \
+		zvmlinux.initrd.tmp $@
+	rm zvmlinux.initrd.tmp
 
-floppy: $(TOPDIR)/vmlinux zImage
-	dd if=zImage of=/dev/fd0H1440 bs=64b
+else
+zvmlinux.initrd:
+endif
 
-znetboot : zImage
-	cp zImage /tftpboot/zImage.prep
+zImage: zvmlinux mkprep
+ifeq ($(CONFIG_PREP),y)
+	./mkprep -pbp zvmlinux zImage
+endif
+ifeq ($(CONFIG_MBX),y)
+	ln -sf zvmlinux zImage
+endif
 
-znetboot.initrd : zImage.initrd
-	cp zImage.initrd /tftpboot/zImage.prep
+zImage.initrd: zvmlinux.initrd mkprep
+ifeq ($(CONFIG_PREP),y)
+	./mkprep -pbp zvmlinux.initrd zImage.initrd
+endif
+ifeq ($(CONFIG_MBX),y)
+	ln -sf zvmlinux.initrd zImage.initrd
+endif
 
 zvmlinux: $(OBJECTS) ../coffboot/vmlinux.gz
 #
@@ -63,61 +102,39 @@
 # then with the offset rebuild the bootloader so we know where the kernel is
 #
 	$(CC) $(CFLAGS) -DINITRD_OFFSET=0 -DINITRD_SIZE=0 \
-		-DZIMAGE_OFFSET=`./offset zvmlinux image` \
-		-DZIMAGE_SIZE=`./size zvmlinux image` -DKERNELBASE=$(KERNELBASE) \
+		-DZIMAGE_OFFSET=`sh offset zvmlinux image` \
+		-DZIMAGE_SIZE=`sh size zvmlinux image` -DKERNELBASE=$(KERNELBASE) \
 		-c -o misc.o misc.c
 	$(LD) $(ZLINKFLAGS) -o zvmlinux.tmp $(OBJECTS)
 	$(OBJCOPY) $(OBJCOPY_ARGS) -R .comment --add-section=image=../coffboot/vmlinux.gz \
 		zvmlinux.tmp $@
 	rm zvmlinux.tmp
 
-zvmlinux.initrd: zvmlinux
-	$(LD) $(ZLINKFLAGS) -o zvmlinux.initrd.tmp $(OBJECTS)
-	$(OBJCOPY) $(OBJCOPY_ARGS) -R .comment \
-		--add-section=initrd=ramdisk.image.gz \
-		--add-section=image=../coffboot/vmlinux.gz \
-		zvmlinux.initrd.tmp zvmlinux.initrd
-	$(CC) $(CFLAGS) -DINITRD_OFFSET=`./offset zvmlinux.initrd initrd` \
-		-DINITRD_SIZE=`./size zvmlinux.initrd initrd` \
-		-DZIMAGE_OFFSET=`./offset zvmlinux.initrd image` \
-		-DZIMAGE_SIZE=`./size zvmlinux.initrd image` \
-		-DKERNELBASE=$(KERNELBASE) -c -o misc.o misc.c
-	$(LD) $(ZLINKFLAGS) -o zvmlinux.initrd.tmp $(OBJECTS)
-	$(OBJCOPY) $(OBJCOPY_ARGS) -R .comment \
-		--add-section=initrd=ramdisk.image.gz \
-		--add-section=image=../coffboot/vmlinux.gz \
-		zvmlinux.initrd.tmp $@
-	rm zvmlinux.initrd.tmp
-
-zImage: zvmlinux mkprep
-	./mkprep -pbp zvmlinux zImage
-
-zImage.initrd: zvmlinux.initrd mkprep
-	./mkprep -pbp zvmlinux.initrd zImage.initrd
-else
-mkprep:
-
-floppy:
-
-znetboot:
-
-znetboot.initrd:
-
-zvmlinux:
-
-zvmlinux.initrd:
-
-zImage:
-
-zImage.initrd:
+floppy: $(TOPDIR)/vmlinux zImage
+ifeq ($(CONFIG_PREP),y)
+	dd if=zImage of=/dev/fd0H1440 bs=64b
 endif
 
+mkprep : mkprep.c
+ifeq ($(CONFIG_PREP),y)
+	$(HOSTCC) -DKERNELBASE=$(KERNELBASE) -o mkprep mkprep.c
+endif
 
+znetboot : zImage
+ifeq ($(CONFIG_PREP),y)
+	cp zImage /tftpboot/zImage.prep
+endif
+ifeq ($(CONFIG_MBX),y)
+	cp zImage /tftpboot/zImage.mbx
+endif
 
-# just here to match coffboot/Makefile
-vmlinux.coff:
-
-vmlinux.coff.initrd:
+znetboot.initrd : zImage.initrd
+ifeq ($(CONFIG_PREP),y)
+	cp zImage.initrd /tftpboot/zImage.prep
+endif
+ifeq ($(CONFIG_MBX),y)
+	cp zImage.initrd /tftpboot/zImage.mbx
+endif
 
 clean:
 	rm -f vmlinux* zvmlinux* mkprep zImage*
@@ -128,3 +145,7 @@
 dep:
 	$(CPP) -M *.S *.c > .depend
 
+# just here to match coffboot/Makefile
+vmlinux.coff:
+
+vmlinux.coff.initrd:
diff -u --recursive --new-file v2.1.96/linux/arch/ppc/boot/head.S linux/arch/ppc/boot/head.S
--- v2.1.96/linux/arch/ppc/boot/head.S	Mon Jan 12 15:18:12 1998
+++ linux/arch/ppc/boot/head.S	Tue Apr 14 17:33:47 1998
@@ -1,19 +1,42 @@
 #include "../kernel/ppc_defs.h"
 #include "../kernel/ppc_asm.tmpl"
 #include <asm/processor.h>
+#include <asm/cache.h>
 
 	.text
 
 /*
  * This code is loaded by the ROM loader at some arbitrary location.
  * Move it to high memory so that it can load the kernel at 0x0000.
+ *
+ * The MBX EPPC-Bug understands ELF, so it loads us into the location
+ * specified in the header.  This is a two step process.  First, EPPC-Bug
+ * loads the file into the intermediate buffer memory location specified
+ * by the environment parameters.  When it discovers this is an ELF
+ * binary, it relocates to the link address for us.  Unfortunately, the
+ * header does not move with the file, so we have to find the
+ * intermediate load location and read the header from there.  From
+ * information provided by Motorola (thank you), we know this intermediate
+ * location can be found from the NVRAM environment.
+ * All of these addresses must be somewhat carefully chosen to make sure
+ * we don't overlap the regions.  I chose to load the kernel at 0, the
+ * compressed image loads at 0x00100000, and the MBX intermediate buffer
+ * was set to 0x00200000.  Provided the loaded kernel image never grows
+ * over one megabyte (which I am going to ensure never happens :-), these
+ * will work fine.  When we get called from EPPC-Bug, registers are:
+ *              R1 - Stack pointer at a high memory address.
+ *              R3 - Pointer to Board Information Block.
+ *              R4 - Pointer to argument string.
+ *              Interrupts masked, cache and MMU disabled.
  */
 
 	.globl	start
 start:
 	bl	start_
 start_:
-	mr	r11,r3		/* Save pointer to residual data */
+	mr	r11,r3		/* Save pointer to residual/board data */
+	
+#ifndef CONFIG_MBX
 	mfmsr	r3		/* Turn off interrupts */
 	li	r4,0
 	ori	r4,r4,MSR_EE
@@ -33,7 +56,6 @@
 	bne	00b
 	mflr	r21
 	mfctr	r22
-	bl	flush_instruction_cache
 	mtlr	r21
 	mtctr	r22
 	bctr			/* Jump to code */
@@ -70,6 +92,8 @@
 	mtlr	r3			/* Easiest way to do an absolute jump */
 	blr
 start_ldr:	
+#endif /* ndef CONFIG_MBX */
+	
 /* Clear all of BSS */
 	lis	r3,edata@h
 	ori	r3,r3,edata@l
@@ -89,12 +113,21 @@
 	li	r2,0x000F		/* Mask pointer to 16-byte boundary */
 	andc	r1,r1,r2
 /* Run loader */
+#ifdef CONFIG_MBX
+        bl      serial_init             /* Init MBX serial port */
+#define ILAP_ADDRESS    0xfa000020
+        lis     r8, ILAP_ADDRESS@h
+        lwz     r8, ILAP_ADDRESS@l(r8)
+	li	r9,end@h
+	ori	r9,r9,end@l
+	sub	r7,r8,r9
+        addis   r8, r8, 1               /* Add 64K */
+#endif	
 	mr	r3,r8			/* Load point */
 	mr	r4,r7			/* Program length */
 	mr	r5,r6			/* Checksum */
 	mr	r6,r11			/* Residual data */
 	bl	decompress_kernel
-	
 	/* changed to use r3 (as firmware does) for kernel
 	   as ptr to residual -- Cort*/
 	lis	r6,cmd_line@h
@@ -111,45 +144,25 @@
 	lis	r2,initrd_end@h
 	ori	r2,r2,initrd_end@l
 	lwz	r5,0(r2)
-		
-	li	r9,0x00c		/* Kernel code starts here */
+	
+	/* tell kernel we're prep */
+	/* 
+	 * get start address of kernel code which is stored as a coff
+	 * entry.  see boot/head.S -- Cort 
+	 */
+	li	r9,0x0
+	lwz	r9,0(r9)
 	mtlr	r9
+#ifndef CONFIG_MBX
+	li	r9,0
+	lis	r10,0xdeadc0de@h
+	ori	r10,r10,0xdeadc0de@l
+	stw	r10,0(r9)
+#endif
 	blr	
 hang:
 	b	hang	
 
-	.globl	_get_SP
-_get_SP:
-	mr	r3,r1
-	blr
-
-	.globl	_get_PVR
-_get_PVR:
-	mfspr	r3,PVR
-	blr
-
-	.globl	_get_MSR
-_get_MSR:
-	mfmsr	r3
-	blr
-
-	.globl	_put_MSR
-_put_MSR:
-	sync
-	mtmsr	r3
-	blr
-
-	.globl	_get_HID0
-_get_HID0:
-	mfspr	r3,HID0
-	blr
-	
-	.globl	_put_HID0
-_put_HID0:
-	sync
-	mtspr	HID0,r3
-	blr
-
 /*
  * Delay for a number of microseconds
  * -- Use the BUS timer (assumes 66MHz)
@@ -189,98 +202,4 @@
 	blt	2b
 3:	blr		
 	
-/*
- * This space [buffer] is used to forceably flush the data cache when
- * running in copyback mode.  This is necessary IFF the data cache could
- * contain instructions for which the instruction cache has stale data.
- * Since the instruction cache NEVER snoops the data cache, memory must
- * be made coherent with the data cache to insure that the instruction
- * cache gets a valid instruction stream.  Note that this flushing is
- * only performed when switching from system to user mode since this is
- * the only juncture [as far as the OS goes] where the data cache may
- * contain instructions, e.g. after a disk read.
- */
-#define NUM_CACHE_LINES 128*8
-#define CACHE_LINE_SIZE 32 
-#if 0
-cache_flush_buffer:
-	.space	NUM_CACHE_LINES*CACHE_LINE_SIZE	/* CAUTION! these need to match hardware */
-#else
-#define cache_flush_buffer 0x1000
-#endif
-
-
-/*
- * Flush instruction cache
- * *** I'm really paranoid here!
- */
-_GLOBAL(flush_instruction_cache)
-	mflr	r5
-	bl	flush_data_cache
-	mfspr	r3,HID0	/* Caches are controlled by this register */
-	li	r4,0
-	ori	r4,r4,(HID0_ICE|HID0_ICFI)
-	or	r3,r3,r4	/* Need to enable+invalidate to clear */
-	mtspr	HID0,r3
-	andc	r3,r3,r4
-	ori	r3,r3,HID0_ICE	/* Enable cache */
-	mtspr	HID0,r3
-	mtlr	r5
-	blr
-
-/*
- * Flush data cache
- * *** I'm really paranoid here!
- */
-_GLOBAL(flush_data_cache)
-	lis	r3,cache_flush_buffer@h
-	ori	r3,r3,cache_flush_buffer@l
-	li	r4,NUM_CACHE_LINES
-	mtctr	r4
-#if 0
-00:	dcbz	0,r3			/* Flush cache line with minimal BUS traffic */
-#else
-00:	lwz	r4,0(r3)
-#endif
-	addi	r3,r3,CACHE_LINE_SIZE	/* Next line, please */
-	bdnz	00b	
-10:	blr
-
-/*
- * Flush a particular page from the DATA cache
- * Note: this is necessary because the instruction cache does *not*
- * snoop from the data cache.
- *	void flush_page(void *page)
- */
-_GLOBAL(flush_page)
-	li	r4,0x0FFF
-	andc	r3,r3,r4		/* Get page base address */
-	li	r4,4096/CACHE_LINE_SIZE	/* Number of lines in a page */
-	mtctr	r4
-00:	dcbf	0,r3			/* Clear line */
-	icbi	0,r3
-	addi	r3,r3,CACHE_LINE_SIZE
-	bdnz	00b
-	blr
-
-/*
- * Execute a [foreign] function
- *
- * run(p1, p2, cp, ep, entry)
- *
- */
- 	.globl	run
-run:
-	mtctr	r7			/* Entry point */
-#define IS_PreP 0x50726550		/* 'PreP' */	
-	lis	r30,IS_PreP>>16
-	ori	r30,r30,IS_PreP&0xFFFF
-	mr	11,r5
-	mr	12,r6
-	mr	r28,r5
-	mr	r29,r6
-	mr	11,r5
-	mr	12,r6
-	bctr
-
 	.comm	.stack,4096*2,4
diff -u --recursive --new-file v2.1.96/linux/arch/ppc/boot/mbxtty.c linux/arch/ppc/boot/mbxtty.c
--- v2.1.96/linux/arch/ppc/boot/mbxtty.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/ppc/boot/mbxtty.c	Tue Apr 14 17:33:47 1998
@@ -0,0 +1,118 @@
+
+
+/* Minimal serial functions needed to send messages out the serial
+ * port on the MBX console.
+ *
+ * The MBX uxes SMC1 for the serial port.  We reset the port and use
+ * only the first BD that EPPC-Bug set up as a character FIFO.
+ *
+ * It's a big hack, but I don't have time right now....I want a kernel
+ * that boots.
+ */
+#include <linux/types.h>
+#include <asm/mbx.h>
+#include "../8xx_io/commproc.h"
+
+#define CPM_CPCR	((volatile ushort *)0xfa2009c0)
+#define SMC1_MODE	((volatile ushort *)0xfa200a82)
+#define SMC1_TBDF	((volatile bd_t *)0xfa202c90)
+#define SMC1_RBDF	((volatile bd_t *)0xfa202c10)
+
+static cpm8xx_t	*cpmp = (cpm8xx_t *)&(((immap_t *)MBX_IMAP_ADDR)->im_cpm);
+
+void
+serial_init(void)
+{
+	volatile smc_t		*sp;
+	volatile smc_uart_t	*up;
+	volatile cbd_t	*tbdf, *rbdf;
+	volatile cpm8xx_t	*cp;
+
+	cp = cpmp;
+	sp = (smc_t*)&(cp->cp_smc[0]);
+	up = (smc_uart_t *)&cp->cp_dparam[PROFF_SMC1];
+
+	/* Disable transmitter/receiver.
+	*/
+	sp->smc_smcm &= ~(SMCMR_REN | SMCMR_TEN);
+
+	tbdf = (cbd_t *)&cp->cp_dpmem[up->smc_tbase];
+	rbdf = (cbd_t *)&cp->cp_dpmem[up->smc_rbase];
+
+	/* Issue a stop transmit, and wait for it.
+	*/
+	cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SMC1, CPM_CR_STOP_TX) | CPM_CR_FLG;
+	while (cp->cp_cpcr & CPM_CR_FLG);
+
+	/* Make the first buffer the only buffer.
+	*/
+	tbdf->cbd_sc |= BD_SC_WRAP;
+	rbdf->cbd_sc |= BD_SC_EMPTY | BD_SC_WRAP;
+
+	/* Single character receive.
+	*/
+	up->smc_mrblr = 1;
+	up->smc_maxidl = 0;
+
+	/* Initialize Tx/Rx parameters.
+	*/
+	cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SMC1, CPM_CR_INIT_TRX) | CPM_CR_FLG;
+	while (cp->cp_cpcr & CPM_CR_FLG);
+
+	/* Enable transmitter/receiver.
+	*/
+	sp->smc_smcm |= SMCMR_REN | SMCMR_TEN;
+}
+
+void
+serial_putchar(const char c)
+{
+	volatile cbd_t		*tbdf;
+	volatile char		*buf;
+	volatile smc_uart_t	*up;
+
+	up = (smc_uart_t *)&cpmp->cp_dparam[PROFF_SMC1];
+	tbdf = (cbd_t *)&cpmp->cp_dpmem[up->smc_tbase];
+
+	/* Wait for last character to go.
+	*/
+	buf = (char *)tbdf->cbd_bufaddr;
+	while (tbdf->cbd_sc & BD_SC_READY);
+
+	*buf = c;
+	tbdf->cbd_datlen = 1;
+	tbdf->cbd_sc |= BD_SC_READY;
+}
+
+char
+serial_getc()
+{
+	volatile cbd_t		*rbdf;
+	volatile char		*buf;
+	volatile smc_uart_t	*up;
+	char			c;
+
+	up = (smc_uart_t *)&cpmp->cp_dparam[PROFF_SMC1];
+	rbdf = (cbd_t *)&cpmp->cp_dpmem[up->smc_rbase];
+
+	/* Wait for character to show up.
+	*/
+	buf = (char *)rbdf->cbd_bufaddr;
+	while (rbdf->cbd_sc & BD_SC_EMPTY);
+	c = *buf;
+	rbdf->cbd_sc |= BD_SC_EMPTY;
+
+	return(c);
+}
+
+int
+serial_tstc()
+{
+	volatile cbd_t		*rbdf;
+	volatile smc_uart_t	*up;
+
+	up = (smc_uart_t *)&cpmp->cp_dparam[PROFF_SMC1];
+	rbdf = (cbd_t *)&cpmp->cp_dpmem[up->smc_rbase];
+
+	return(!(rbdf->cbd_sc & BD_SC_EMPTY));
+}
diff -u --recursive --new-file v2.1.96/linux/arch/ppc/boot/misc.c linux/arch/ppc/boot/misc.c
--- v2.1.96/linux/arch/ppc/boot/misc.c	Mon Jan 12 15:18:13 1998
+++ linux/arch/ppc/boot/misc.c	Tue Apr 14 17:33:47 1998
@@ -4,25 +4,35 @@
  * Adapted for PowerPC by Gary Thomas
  *
  * Rewritten by Cort Dougan (cort@cs.nmt.edu)
- * Soon to be replaced by a single bootloader for chrp/prep/pmac. -- Cort
+ * One day to be replaced by a single bootloader for chrp/prep/pmac. -- Cort
  */
 
 #include "../coffboot/zlib.h"
 #include "asm/residual.h"
 #include <elf.h>
+#include <linux/config.h>
+#ifdef CONFIG_MBX
+#include <asm/mbx.h>
+bd_t	hold_board_info;
+#endif
 
 /* this is where the INITRD gets moved to for safe keeping */
-#define INITRD_DESTINATION /*0x00f00000*/ 0x01f00000
+#define INITRD_DESTINATION /*0x00f00000*/ 0x01800000
+#ifdef CONFIG_8xx
+char *avail_ram = (char *) 0x00200000;
+char *end_avail = (char *) 0x00400000;
+#else /* CONFIG_8xx */
 /* this will do for now - Cort */
 char *avail_ram = (char *) 0x00800000; /* start with 8M */
 /* assume 15M max since this is where we copy the initrd to -- Cort */
 char *end_avail = (char *) INITRD_DESTINATION; 
+#endif /* CONFIG_8xx */
 
+char cmd_line[256];
 RESIDUAL hold_residual;
 unsigned long initrd_start = 0, initrd_end = 0;
 char *zimage_start;
 int zimage_size;
-char cmd_line[256];
 
 char *vidmem = (char *)0xC00B8000;
 int lines, cols;
@@ -47,6 +57,7 @@
 	while(1); 
 }
 
+#ifndef CONFIG_MBX
 static void clear_screen()
 {
 	int i, j;
@@ -144,6 +155,39 @@
 	orig_x = x;
 	orig_y = y;
 }
+#else
+/* The MBX is just the serial port.
+*/
+tstc(void)
+{
+        return (serial_tstc());
+}
+
+getc(void)
+{
+        while (1) {
+                if (serial_tstc()) return (serial_getc());
+        }
+}
+
+void 
+putc(const char c)
+{
+        serial_putchar(c);
+}
+
+void puts(const char *s)
+{
+        char c;
+
+        while ( ( c = *s++ ) != '\0' ) {
+                serial_putchar(c);
+                if ( c == '\n' )
+                        serial_putchar('\r');
+        }
+}
+
+#endif /* CONFIG_MBX */
 
 void * memcpy(void * __dest, __const void * __src,
 			    int __n)
@@ -253,6 +297,8 @@
 	inflateEnd(&s);
 }
 
+unsigned char sanity[0x2000];
+
 unsigned long
 decompress_kernel(unsigned long load_addr, int num_words, unsigned long cksum, RESIDUAL *residual)
 {
@@ -260,31 +306,60 @@
 	extern unsigned long start;
 	char *cp, ch;
 	unsigned long i;
-	
-	
+
 	lines = 25;
 	cols = 80;
 	orig_x = 0;
 	orig_y = 24;
 	
 	
-	/* Turn off MMU.  Since we are mapped 1-1, this is OK. */
-	flush_instruction_cache();
-	_put_HID0(_get_HID0() & ~0x0000C000);
-	_put_MSR(_get_MSR() & ~0x0030);
-
+#ifndef CONFIG_8xx
 	vga_init(0xC0000000);
-	/*clear_screen();*/
+	/* copy the residual data */
+	if (residual)
+		memcpy(&hold_residual,residual,sizeof(RESIDUAL));
+#endif /* CONFIG_8xx */
+#ifdef CONFIG_MBX	
+	/* copy board data */
+	if (residual)
+		_bcopy((char *)residual, (char *)&hold_board_info,
+		       sizeof(hold_board_info));
+#endif /* CONFIG_8xx */
+	
 
-	puts("loaded at:    "); puthex(load_addr);
+	puts("loaded at:     "); puthex(load_addr);
 	puts(" "); puthex((unsigned long)(load_addr + (4*num_words))); puts("\n");
-	
-	puts("relocated to: "); puthex((unsigned long)&start);
+	puts("relocated to:  "); puthex((unsigned long)&start);
 	puts(" "); puthex((unsigned long)((unsigned long)&start + (4*num_words))); puts("\n");
-	
+
+	if ( residual )
+	{
+		puts("board data at: "); puthex((unsigned long)residual);
+		puts(" ");
+#ifdef CONFIG_MBX	
+		puthex((unsigned long)((unsigned long)residual + sizeof(bd_t)));
+#else
+		puthex((unsigned long)((unsigned long)residual + sizeof(RESIDUAL)));
+#endif	
+		puts("\n");
+		puts("relocated to:  ");
+#ifdef CONFIG_MBX
+		puthex((unsigned long)&hold_board_info);
+#else
+		puthex((unsigned long)&hold_residual);
+#endif
+		puts(" ");
+#ifdef CONFIG_MBX
+		puthex((unsigned long)((unsigned long)&hold_board_info + sizeof(bd_t)));
+#else
+		puthex((unsigned long)((unsigned long)&hold_residual + sizeof(RESIDUAL)));
+#endif	
+		puts("\n");
+	}
+
 	zimage_start = (char *)(load_addr - 0x10000 + ZIMAGE_OFFSET);
 	zimage_size = ZIMAGE_SIZE;
-	puts("zimage at:    "); puthex((unsigned long)zimage_start);
+	puts("zimage at:     "); puthex((unsigned long)zimage_start);
 	puts(" "); puthex((unsigned long)(zimage_size+zimage_start)); puts("\n");
 
 	if ( INITRD_OFFSET )
@@ -296,18 +371,19 @@
 	/* relocate initrd */
 	if ( initrd_start )
 	{
-		puts("initrd at:    "); puthex(initrd_start);
+		puts("initrd at:     "); puthex(initrd_start);
 		puts(" "); puthex(initrd_end); puts("\n");
 		
 		memcpy ((void *)INITRD_DESTINATION,(void *)initrd_start,
 			INITRD_SIZE );
 		initrd_end = INITRD_DESTINATION + INITRD_SIZE;
 		initrd_start = INITRD_DESTINATION;
-		puts("Moved initrd to: "); puthex(initrd_start);
+		puts("Moved initrd to:  "); puthex(initrd_start);
 		puts(" "); puthex(initrd_end); puts("\n");
 	}
-	
+#ifndef CONFIG_MBX
 	CRT_tstc();  /* Forces keyboard to be initialized */
+#endif	
 	puts("\nLinux/PPC load: ");
 	timer = 0;
 	cp = cmd_line;
@@ -339,9 +415,12 @@
 	if ( initrd_start > (16<<20))
 		puts("initrd_start located > 16M\n");
        
-  
 	puts("Uncompressing Linux...");
-	gunzip(0, 0x400000, zimage_start, &zimage_size);  
+
+	/* these _bcopy() calls are here so I can add breakpoints to the boot for mbx -- Cort */
+	/*_bcopy( (char *)0x100,(char *)&sanity, 0x2000-0x100);*/
+	gunzip(0, 0x400000, zimage_start, &zimage_size);
+	/*_bcopy( (char *)&sanity,(char *)0x100,0x2000-0x100);*/
 	puts("done.\n");
 	puts("Now booting the kernel\n");
 	return (unsigned long)&hold_residual;
diff -u --recursive --new-file v2.1.96/linux/arch/ppc/boot/vreset.c linux/arch/ppc/boot/vreset.c
--- v2.1.96/linux/arch/ppc/boot/vreset.c	Thu Jul 31 13:09:17 1997
+++ linux/arch/ppc/boot/vreset.c	Tue Apr 14 17:33:56 1998
@@ -434,16 +434,15 @@
 {
 	int slot;
 	struct VgaRegs *VgaTextRegs;
-
+#if 0
 	if ((_get_PVR()>>16) == PPC_601) {
 		return(old_vga_init(ISA_mem));
 	}
-
-#if 1
+#endif
+	
 	/* See if VGA already in TEXT mode - exit if so! */
 	outb(0x3CE, 0x06);
 	if ((inb(0x3CF) & 0x01) == 0){puts("VGA already in text mode\n"); return;}
-#endif
 
 	/* If no VGA responding in text mode, then we have some work to do...
 	 */
@@ -522,11 +521,6 @@
 	return (1);  /* 'CRT' I/O supported */
 }
 
-static int
-NOP(int x)
-{
-}
-
 /*
  * Write to VGA Attribute registers.  
  */
@@ -852,166 +846,5 @@
 		puts(" Vendor ID: "); 
 		puthex(PCIVendor(i)); puts("\n");
 #endif
-
-	}
-}
-
-/*
- * OLD vreset.c
- *
- * Initialize the VGA control registers to 80x25 text mode.
- *
- * Adapted from a program by:
- *                                      Steve Sellgren
- *                                      San Francisco Indigo Company
- *                                      sfindigo!sellgren@uunet.uu.net
- */
- 
-unsigned char CRTC[24] = {
-    0x5F, 0x4F, 0x50, 0x82, 0x55, 0x81, 0xBF, 0x1F, 
-    0x00, 0x4F, 0x0D, 0x0E, 0x00, 0x00, 0x00, 0x00, /*0x07, 0x80, */
-    0x9C, 0xAE, 0x8F, 0x28, 0x1F, 0x96, 0xB9, 0xA3};
-unsigned char SEQ[5] = {0x3, 0x0, 0x3, 0x0, 0x2};
-unsigned char GC[9] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0xE, 0x0, 0xFF};
-
-#if 0
-static const unsigned char color_LUT[] =
-   {
-	0x00, 0x00, 0x00, /* 0 - black */
-	0x00, 0x00, 0x2A, /* 1 - blue */
-	0x00, 0x2A, 0x00, /* 2 - green */
-	0x00, 0x2A, 0x2A, /* 3 - cyan */
-	0x2A, 0x00, 0x00, /* 4 - red */
-	0x2A, 0x00, 0x2A, /* 5 - magenta */
-	0x2A, 0x2A, 0x00, /* 6 - brown */
-	0x2A, 0x2A, 0x2A, /* 7 - white */
-	0x00, 0x00, 0x15, /* 8 - gray */
-	0x00, 0x00, 0x3F, /* 9 - light blue */
-	0x00, 0x2A, 0x15, /* 10 - light green */
-	0x00, 0x2A, 0x3F, /* 11 - light cyan */
-	0x2A, 0x00, 0x15, /* 12 - light red */
-	0x2A, 0x00, 0x3F, /* 13 - light magenta */
-	0x2A, 0x2A, 0x15, /* 14 - yellow */
-	0x2A, 0x2A, 0x3F, /* 15 - bright white */
-   };
-#endif
- 
-old_vga_init(unsigned char *ISA_mem)
-{
-	int i, j;
-	int value;
-	unsigned char *font_page = (unsigned char *) &ISA_mem[0xA0000];
-
-	/* See if VGA already in TEXT mode - exit if so! */
-	outb(0x3CE, 0x06);
-	if ((inb(0x3CF) & 0x01) == 0) return;
-    
-	/* From the S3 manual */
-	outb(0x46E8, 0x10);  /* Put into setup mode */
-	outb(0x3C3, 0x10);
-	outb(0x102, 0x01);   /* Enable registers */
-	outb(0x46E8, 0x08);  /* Enable video */
-	outb(0x3C3, 0x08);
-	outb(0x4AE8, 0x00);
-
-#if 0
-	outb(0x42E8, 0x80);  /* Reset graphics engine? */
-#endif
-
-	outb(0x3D4, 0x38);  /* Unlock all registers */
-	outb(0x3D5, 0x48);
-	outb(0x3D4, 0x39);
-	outb(0x3D5, 0xA5);
-	outb(0x3D4, 0x40);
-	outb(0x3D5, inb(0x3D5)|0x01);
-	outb(0x3D4, 0x33);
-	outb(0x3D5, inb(0x3D5)&~0x52);
-	outb(0x3D4, 0x35);
-	outb(0x3D5, inb(0x3D5)&~0x30);
-	outb(0x3D4, 0x3A);
-	outb(0x3D5, 0x00);
-	outb(0x3D4, 0x53);
-	outb(0x3D5, 0x00);
-	outb(0x3D4, 0x31);
-	outb(0x3D5, inb(0x3D5)&~0x4B);
-	outb(0x3D4, 0x58);
-	outb(0x3D5, 0);
-
-	outb(0x3D4, 0x54);
-	outb(0x3D5, 0x38);
-	outb(0x3D4, 0x60);
-	outb(0x3D5, 0x07);
-	outb(0x3D4, 0x61);
-	outb(0x3D5, 0x80);
-	outb(0x3D4, 0x62);
-	outb(0x3D5, 0xA1);
-	outb(0x3D4, 0x69);  /* High order bits for cursor address */
-	outb(0x3D5, 0);
-  
-	outb(0x3D4, 0x32);
-	outb(0x3D5, inb(0x3D5)&~0x10);
-
-	outb(0x3C2, 0x67);
-
-#if 0
-	/* Initialize DAC */
-	outb(0x3C6,0xFF);
-	inb(0x3C7);
-	outb(0x3C8,0x00);
-	inb(0x3C7);
-	for (i=0; i<sizeof(color_LUT); i++) {
-		outb(0x3C9, color_LUT[i]);
-	}
-	for (i; i<768; i += 3) {
-		outb(0x3C9, 0x3F); /* White? */
-		outb(0x3C9, 0x3F); /* White? */
-		outb(0x3C9, 0x3F); /* White? */
-	}
-
-	/* Load font */
-	NOP(inb(0x3DA));  /* Reset Address/Data FlipFlop for Attribute ctlr */
-	outb(0x3C0,0x30); outb(0x3C0, 0x01); /* graphics mode */
-	outw(0x3C4, 0x0001);    /* reset sequencer */
-	outw(0x3C4, 0x0204);    /* write to plane 2 */
-	outw(0x3C4, 0x0407);    /* enable plane graphics */
-	outw(0x3C4, 0x0003);    /* reset sequencer */
-	outw(0x3CE, 0x0402);    /* read plane 2 */
-	outw(0x3CE, 0x0500);    /* write mode 0, read mode 0 */
-	outw(0x3CE, 0x0600);    /* set graphics */
-	for (i = 0;  i < sizeof(font);  i += 16) {
-		for (j = 0;  j < 16;  j++) {
-			font_page[(2*i)+j] = font[i+j];
-		}
-	}
-#else
-	outw(0x3C4, 0x0120);                /* disable video              */
-	setTextCLUT(2);                     /* load color lookup table    */
-	loadFont(ISA_mem);                  /* load font                  */
-#endif
-
-	for (i = 0; i < 24; i++) {
-		outb(0x3D4, i);
-		outb(0x3D5, CRTC[i]);
-	}
-	for (i = 0; i < 5; i++) {
-		outb(0x3C4, i);
-		outb(0x3C5, SEQ[i]);
-	}
-	for (i = 0; i < 9; i++) {
-		outb(0x3CE, i);
-		outb(0x3CF, GC[i]);
-	}
-	value = inb(0x3DA);         /* reset flip-flop */
-	for (i = 0; i < 16; i++) {
-		outb(0x3C0, i);
-		outb(0x3C0, AC[i]);
-	}
-	for (i = 16; i < 21; i++) {
-		outb(0x3C0, i | 0x20);
-		outb(0x3C0, AC[i]);
 	}
-	clearVideoMemory();
-	outw(0x3C4, 0x0100);                /* re-enable video            */
-	outb(0x3C2, 0x23);
-	return (1); /* Keyboard should work */
 }
diff -u --recursive --new-file v2.1.96/linux/arch/ppc/chrp_defconfig linux/arch/ppc/chrp_defconfig
--- v2.1.96/linux/arch/ppc/chrp_defconfig	Wed Dec 31 16:00:00 1969
+++ linux/arch/ppc/chrp_defconfig	Tue Apr 14 17:33:56 1998
@@ -0,0 +1,321 @@
+#
+# Automatically generated by make menuconfig: don't edit
+#
+
+#
+# Platform support
+#
+CONFIG_PPC=y
+CONFIG_6xx=y
+# CONFIG_8xx is not set
+# CONFIG_PMAC is not set
+# CONFIG_PREP is not set
+CONFIG_CHRP=y
+# CONFIG_ALL_PPC is not set
+# CONFIG_MBX is not set
+CONFIG_MACH_SPECIFIC=y
+
+#
+# General setup
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_MODULES=y
+# CONFIG_MODVERSIONS is not set
+CONFIG_KERNELD=y
+CONFIG_PCI=y
+CONFIG_PCI_OLD_PROC=y
+CONFIG_NET=y
+CONFIG_SYSCTL=y
+CONFIG_SYSVIPC=y
+# CONFIG_BSD_PROCESS_ACCT is not set
+CONFIG_BINFMT_ELF=y
+CONFIG_KERNEL_ELF=y
+# CONFIG_BINFMT_MISC is not set
+# CONFIG_BINFMT_JAVA is not set
+# CONFIG_PARPORT is not set
+# CONFIG_ABSTRACT_CONSOLE is not set
+CONFIG_PMAC_CONSOLE=y
+CONFIG_MAC_KEYBOARD=y
+# CONFIG_MAC_FLOPPY is not set
+CONFIG_MACMOUSE=y
+CONFIG_PROC_DEVICETREE=y
+# CONFIG_XMON is not set
+CONFIG_CONTROL_VIDEO=y
+CONFIG_PLATINUM_VIDEO=y
+CONFIG_VALKYRIE_VIDEO=y
+CONFIG_ATY_VIDEO=y
+CONFIG_IMSTT_VIDEO=y
+CONFIG_CHIPS_VIDEO=y
+
+#
+# Plug and Play support
+#
+# CONFIG_PNP is not set
+
+#
+# Floppy, IDE, and other block devices
+#
+CONFIG_BLK_DEV_FD=y
+CONFIG_BLK_DEV_IDE=y
+# CONFIG_BLK_DEV_HD_IDE is not set
+CONFIG_BLK_DEV_IDEDISK=y
+CONFIG_BLK_DEV_IDECD=y
+# CONFIG_BLK_DEV_IDETAPE is not set
+# CONFIG_BLK_DEV_IDEFLOPPY is not set
+# CONFIG_BLK_DEV_IDESCSI is not set
+# CONFIG_BLK_DEV_CMD640 is not set
+# CONFIG_BLK_DEV_RZ1000 is not set
+# CONFIG_BLK_DEV_IDEPCI is not set
+# CONFIG_BLK_DEV_SL82C105 is not set
+# CONFIG_IDE_CHIPSETS is not set
+CONFIG_BLK_DEV_LOOP=m
+# CONFIG_BLK_DEV_NBD is not set
+# CONFIG_BLK_DEV_MD is not set
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_INITRD=y
+# CONFIG_BLK_DEV_XD is not set
+CONFIG_PARIDE_PARPORT=y
+# CONFIG_PARIDE is not set
+# CONFIG_BLK_DEV_HD is not set
+
+#
+# NEW devices (io_request, all ALPHA and dangerous)
+#
+# CONFIG_IO_REQUEST is not set
+
+#
+# Networking options
+#
+# CONFIG_PACKET is not set
+# CONFIG_NETLINK is not set
+# CONFIG_FIREWALL is not set
+CONFIG_NET_ALIAS=y
+# CONFIG_FILTER is not set
+CONFIG_UNIX=y
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+# CONFIG_IP_ADVANCED_ROUTER is not set
+# CONFIG_IP_PNP is not set
+# CONFIG_IP_ACCT is not set
+# CONFIG_IP_ROUTER is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_IP_MROUTE is not set
+CONFIG_IP_ALIAS=y
+# CONFIG_SYN_COOKIES is not set
+CONFIG_INET_RARP=y
+CONFIG_IP_NOSR=y
+CONFIG_SKB_LARGE=y
+# CONFIG_IPV6 is not set
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_LLC is not set
+# CONFIG_WAN_ROUTER is not set
+# CONFIG_NET_FASTROUTE is not set
+# CONFIG_NET_HW_FLOWCONTROL is not set
+# CONFIG_CPU_IS_SLOW is not set
+# CONFIG_NET_SCHED is not set
+# CONFIG_NET_PROFILE is not set
+
+#
+# SCSI support
+#
+CONFIG_SCSI=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_CHR_DEV_ST=y
+CONFIG_BLK_DEV_SR=y
+CONFIG_BLK_DEV_SR_VENDOR=y
+CONFIG_CHR_DEV_SG=y
+# CONFIG_SCSI_MULTI_LUN is not set
+CONFIG_SCSI_CONSTANTS=y
+# CONFIG_SCSI_LOGGING is not set
+
+#
+# SCSI low-level drivers
+#
+# CONFIG_SCSI_7000FASST is not set
+# CONFIG_SCSI_AHA152X is not set
+# CONFIG_SCSI_AHA1542 is not set
+# CONFIG_SCSI_AHA1740 is not set
+CONFIG_SCSI_AIC7XXX=m
+# CONFIG_AIC7XXX_TAGGED_QUEUEING is not set
+# CONFIG_OVERRIDE_CMDS is not set
+# CONFIG_AIC7XXX_PAGE_ENABLE is not set
+CONFIG_AIC7XXX_PROC_STATS=y
+CONFIG_AIC7XXX_RESET_DELAY=15
+# CONFIG_SCSI_ADVANSYS is not set
+# CONFIG_SCSI_IN2000 is not set
+# CONFIG_SCSI_AM53C974 is not set
+# CONFIG_SCSI_BUSLOGIC is not set
+# CONFIG_SCSI_DTC3280 is not set
+# CONFIG_SCSI_EATA_DMA is not set
+# CONFIG_SCSI_EATA_PIO is not set
+# CONFIG_SCSI_EATA is not set
+# CONFIG_SCSI_FUTURE_DOMAIN is not set
+# CONFIG_SCSI_GDTH is not set
+# CONFIG_SCSI_GENERIC_NCR5380 is not set
+# CONFIG_SCSI_NCR53C406A is not set
+# CONFIG_SCSI_NCR53C7xx is not set
+# CONFIG_SCSI_NCR53C8XX is not set
+# CONFIG_SCSI_PAS16 is not set
+# CONFIG_SCSI_PCI2000 is not set
+# CONFIG_SCSI_PCI2220I is not set
+# CONFIG_SCSI_PSI240I is not set
+# CONFIG_SCSI_QLOGIC_FAS is not set
+# CONFIG_SCSI_QLOGIC_ISP is not set
+# CONFIG_SCSI_SEAGATE is not set
+# CONFIG_SCSI_DC390T is not set
+# CONFIG_SCSI_T128 is not set
+# CONFIG_SCSI_U14_34F is not set
+# CONFIG_SCSI_ULTRASTOR is not set
+# CONFIG_SCSI_DEBUG is not set
+CONFIG_SCSI_MESH=y
+CONFIG_SCSI_MESH_SYNC_RATE=10
+CONFIG_SCSI_MAC53C94=y
+
+#
+# Network device support
+#
+CONFIG_NETDEVICES=y
+# CONFIG_ARCNET is not set
+# CONFIG_DUMMY is not set
+# CONFIG_EQUALIZER is not set
+CONFIG_NET_ETHERNET=y
+# CONFIG_NET_VENDOR_3COM is not set
+# CONFIG_LANCE is not set
+# CONFIG_NET_VENDOR_SMC is not set
+# CONFIG_NET_VENDOR_RACAL is not set
+# CONFIG_RTL8139 is not set
+# CONFIG_YELLOWFIN is not set
+# CONFIG_NET_ISA is not set
+CONFIG_NET_EISA=y
+# CONFIG_PCNET32 is not set
+# CONFIG_AC3200 is not set
+# CONFIG_APRICOT is not set
+# CONFIG_CS89x0 is not set
+CONFIG_DE4X5=y
+# CONFIG_DEC_ELCP is not set
+# CONFIG_DGRS is not set
+# CONFIG_EEXPRESS_PRO100 is not set
+# CONFIG_TLAN is not set
+# CONFIG_ES3210 is not set
+# CONFIG_ZNET is not set
+# CONFIG_NET_POCKET is not set
+# CONFIG_FDDI is not set
+# CONFIG_DLCI is not set
+# CONFIG_PPP is not set
+# CONFIG_SLIP is not set
+# CONFIG_NET_RADIO is not set
+# CONFIG_TR is not set
+# CONFIG_SHAPER is not set
+
+#
+# Amateur Radio support
+#
+# CONFIG_HAMRADIO is not set
+
+#
+# ISDN subsystem
+#
+# CONFIG_ISDN is not set
+
+#
+# CD-ROM drivers (not for SCSI or IDE/ATAPI drives)
+#
+# CONFIG_CD_NO_IDESCSI is not set
+CONFIG_CDROM=y
+
+#
+# Filesystems
+#
+# CONFIG_QUOTA is not set
+# CONFIG_MINIX_FS is not set
+CONFIG_EXT2_FS=y
+CONFIG_ISO9660_FS=y
+# CONFIG_JOLIET is not set
+CONFIG_FAT_FS=m
+CONFIG_MSDOS_FS=m
+# CONFIG_UMSDOS_FS is not set
+CONFIG_VFAT_FS=m
+CONFIG_PROC_FS=y
+CONFIG_NFS_FS=y
+CONFIG_NFSD=y
+CONFIG_SUNRPC=y
+CONFIG_LOCKD=y
+# CONFIG_CODA_FS is not set
+# CONFIG_SMB_FS is not set
+# CONFIG_HPFS_FS is not set
+# CONFIG_NTFS_FS is not set
+# CONFIG_SYSV_FS is not set
+# CONFIG_AFFS_FS is not set
+CONFIG_HFS_FS=y
+# CONFIG_ROMFS_FS is not set
+CONFIG_AUTOFS_FS=y
+# CONFIG_UFS_FS is not set
+# CONFIG_ADFS_FS is not set
+CONFIG_MAC_PARTITION=y
+CONFIG_NLS=y
+
+#
+# Native Language Support
+#
+# CONFIG_NLS_CODEPAGE_437 is not set
+# CONFIG_NLS_CODEPAGE_737 is not set
+# CONFIG_NLS_CODEPAGE_775 is not set
+# CONFIG_NLS_CODEPAGE_850 is not set
+# CONFIG_NLS_CODEPAGE_852 is not set
+# CONFIG_NLS_CODEPAGE_855 is not set
+# CONFIG_NLS_CODEPAGE_857 is not set
+# CONFIG_NLS_CODEPAGE_860 is not set
+# CONFIG_NLS_CODEPAGE_861 is not set
+# CONFIG_NLS_CODEPAGE_862 is not set
+# CONFIG_NLS_CODEPAGE_863 is not set
+# CONFIG_NLS_CODEPAGE_864 is not set
+# CONFIG_NLS_CODEPAGE_865 is not set
+# CONFIG_NLS_CODEPAGE_866 is not set
+# CONFIG_NLS_CODEPAGE_869 is not set
+# CONFIG_NLS_CODEPAGE_874 is not set
+# CONFIG_NLS_ISO8859_1 is not set
+# CONFIG_NLS_ISO8859_2 is not set
+# CONFIG_NLS_ISO8859_3 is not set
+# CONFIG_NLS_ISO8859_4 is not set
+# CONFIG_NLS_ISO8859_5 is not set
+# CONFIG_NLS_ISO8859_6 is not set
+# CONFIG_NLS_ISO8859_7 is not set
+# CONFIG_NLS_ISO8859_8 is not set
+# CONFIG_NLS_ISO8859_9 is not set
+# CONFIG_NLS_KOI8_R is not set
+
+#
+# Character devices
+#
+CONFIG_VT=y
+CONFIG_VT_CONSOLE=y
+# CONFIG_SOFTCURSOR is not set
+CONFIG_SERIAL=y
+# CONFIG_SERIAL_CONSOLE is not set
+# CONFIG_SERIAL_EXTENDED is not set
+# CONFIG_SERIAL_NONSTANDARD is not set
+# CONFIG_MOUSE is not set
+# CONFIG_UMISC is not set
+# CONFIG_QIC02_TAPE is not set
+# CONFIG_APM is not set
+# CONFIG_WATCHDOG is not set
+# CONFIG_RTC is not set
+# CONFIG_VIDEO_DEV is not set
+CONFIG_NVRAM=y
+# CONFIG_JOYSTICK is not set
+# CONFIG_MISC_RADIO is not set
+
+#
+# Ftape, the floppy tape device driver
+#
+# CONFIG_FTAPE is not set
+
+#
+# Sound
+#
+# CONFIG_SOUND is not set
diff -u --recursive --new-file v2.1.96/linux/arch/ppc/chrpboot/main.c linux/arch/ppc/chrpboot/main.c
--- v2.1.96/linux/arch/ppc/chrpboot/main.c	Mon Jan 12 15:18:13 1998
+++ linux/arch/ppc/chrpboot/main.c	Tue Apr 14 17:33:57 1998
@@ -16,11 +16,11 @@
 #define get_16be(x)	(*(unsigned short *)(x))
 #define get_32be(x)	(*(unsigned *)(x))
 
-#define RAM_START	0x90000000
-#define RAM_END		0x90800000	/* only 8M mapped with BATs */
+#define RAM_START	0x00000000
+#define RAM_END		0x00800000	/* only 8M mapped with BATs */
 
-#define RAM_FREE	0x90540000	/* after image of chrpboot */
-#define PROG_START	0x90010000
+#define RAM_FREE	0x00540000	/* after image of chrpboot */
+#define PROG_START	0x00010000
 
 char *avail_ram;
 char *end_avail;
@@ -40,7 +40,7 @@
     unsigned initrd_start, initrd_size;
     
     printf("chrpboot starting\n\r");
-    setup_bats();
+    /* setup_bats(); */
 
     if (initrd_len) {
 	initrd_size = initrd_len;
diff -u --recursive --new-file v2.1.96/linux/arch/ppc/chrpboot/misc.S linux/arch/ppc/chrpboot/misc.S
--- v2.1.96/linux/arch/ppc/chrpboot/misc.S	Mon Jan 12 15:18:13 1998
+++ linux/arch/ppc/chrpboot/misc.S	Tue Apr 14 17:33:57 1998
@@ -23,10 +23,10 @@
 	b	5f
 4:	ori	4,4,0xff		/* set up BAT registers for 604 */
 	li	5,2
-	mtdbatu	0,4
-	mtdbatl	0,5
-5:	mtibatu	0,4
-	mtibatl	0,5
+	mtdbatu	3,4
+	mtdbatl	3,5
+5:	mtibatu	3,4
+	mtibatl	3,5
 	isync
 	blr
 
diff -u --recursive --new-file v2.1.96/linux/arch/ppc/coffboot/main.c linux/arch/ppc/coffboot/main.c
--- v2.1.96/linux/arch/ppc/coffboot/main.c	Sat Aug 16 09:51:08 1997
+++ linux/arch/ppc/coffboot/main.c	Tue Apr 14 17:33:57 1998
@@ -18,9 +18,10 @@
 #define get_32be(x)	(*(unsigned *)(x))
 
 #define RAM_START	0xc0000000
-#define RAM_END		0xc0800000	/* only 8M mapped with BATs */
+#define PROG_START	RAM_START
+#define RAM_END		(RAM_START + 0x800000)	/* only 8M mapped with BATs */
 
-#define RAM_FREE	0xc0540000	/* after image of coffboot */
+#define RAM_FREE	(RAM_START + 0x540000)	/* after image of coffboot */
 
 char *avail_ram;
 char *end_avail;
@@ -47,7 +48,7 @@
 	printf("error getting load-base\n");
 	exit();
     }
-    setup_bats();
+    setup_bats(RAM_START);
 
     eh = (struct external_filehdr *) loadbase;
     ns = get_16be(eh->f_nscns);
@@ -82,7 +83,7 @@
 	
     im = (unsigned char *)(loadbase + get_32be(isect->s_scnptr));
     len = get_32be(isect->s_size);
-    dst = (void *) RAM_START;
+    dst = (void *) PROG_START;
 
     if (im[0] == 0x1f && im[1] == 0x8b) {
 	void *cp = (void *) RAM_FREE;
@@ -98,7 +99,7 @@
 
     flush_cache(dst, len);
 
-    sa = *(unsigned *)dst + RAM_START;
+    sa = *(unsigned *)dst + PROG_START;
     printf("start address = 0x%x\n", sa);
 
 #if 0
diff -u --recursive --new-file v2.1.96/linux/arch/ppc/coffboot/misc.S linux/arch/ppc/coffboot/misc.S
--- v2.1.96/linux/arch/ppc/coffboot/misc.S	Sat Aug 16 09:51:08 1997
+++ linux/arch/ppc/coffboot/misc.S	Tue Apr 14 17:33:57 1998
@@ -9,24 +9,25 @@
 	.text
 
 /*
- * Use the BAT0 registers to map the 1st 8MB of RAM to 0xc0000000.
+ * Use the BAT0 registers to map the 1st 8MB of RAM to
+ * the address given as the 1st argument.
  */
 	.globl	setup_bats
 setup_bats:
+	mr	4,3
 	mfpvr	3
 	rlwinm	3,3,16,16,31		/* r3 = 1 for 601, 4 for 604 */
 	cmpi	0,3,1
-	lis	4,0xc000
 	bne	4f
 	ori	4,4,4			/* set up BAT registers for 601 */
 	li	5,0x7f
 	b	5f
 4:	ori	4,4,0xff		/* set up BAT registers for 604 */
 	li	5,2
-	mtdbatu	0,4
-	mtdbatl	0,5
-5:	mtibatu	0,4
-	mtibatl	0,5
+	mtdbatu	3,4
+	mtdbatl	3,5
+5:	mtibatu	3,4
+	mtibatl	3,5
 	isync
 	blr
 
diff -u --recursive --new-file v2.1.96/linux/arch/ppc/common_defconfig linux/arch/ppc/common_defconfig
--- v2.1.96/linux/arch/ppc/common_defconfig	Wed Dec 31 16:00:00 1969
+++ linux/arch/ppc/common_defconfig	Tue Apr 14 17:33:57 1998
@@ -0,0 +1,331 @@
+#
+# Automatically generated by make menuconfig: don't edit
+#
+
+#
+# Platform support
+#
+CONFIG_PPC=y
+CONFIG_6xx=y
+# CONFIG_8xx is not set
+# CONFIG_PMAC is not set
+# CONFIG_PREP is not set
+# CONFIG_CHRP is not set
+CONFIG_ALL_PPC=y
+# CONFIG_MBX is not set
+
+#
+# General setup
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_MODULES=y
+CONFIG_MODVERSIONS=y
+CONFIG_KERNELD=y
+CONFIG_PCI=y
+CONFIG_PCI_OLD_PROC=y
+CONFIG_NET=y
+CONFIG_SYSCTL=y
+CONFIG_SYSVIPC=y
+# CONFIG_BSD_PROCESS_ACCT is not set
+CONFIG_BINFMT_ELF=y
+CONFIG_KERNEL_ELF=y
+# CONFIG_BINFMT_MISC is not set
+# CONFIG_BINFMT_JAVA is not set
+# CONFIG_PARPORT is not set
+CONFIG_ABSTRACT_CONSOLE=y
+CONFIG_FB=y
+CONFIG_VGA_CONSOLE=y
+CONFIG_FB_COMPAT_XPMAC=y
+CONFIG_MAC_KEYBOARD=y
+CONFIG_MAC_FLOPPY=y
+CONFIG_MACMOUSE=y
+CONFIG_PROC_DEVICETREE=y
+# CONFIG_XMON is not set
+
+#
+# Plug and Play support
+#
+# CONFIG_PNP is not set
+
+#
+# Floppy, IDE, and other block devices
+#
+CONFIG_BLK_DEV_FD=y
+CONFIG_BLK_DEV_IDE=y
+# CONFIG_BLK_DEV_HD_IDE is not set
+CONFIG_BLK_DEV_IDEDISK=y
+CONFIG_BLK_DEV_IDECD=y
+# CONFIG_BLK_DEV_IDETAPE is not set
+# CONFIG_BLK_DEV_IDEFLOPPY is not set
+# CONFIG_BLK_DEV_IDESCSI is not set
+# CONFIG_BLK_DEV_CMD640 is not set
+# CONFIG_BLK_DEV_RZ1000 is not set
+# CONFIG_BLK_DEV_IDEPCI is not set
+# CONFIG_IDE_CHIPSETS is not set
+CONFIG_BLK_DEV_LOOP=y
+# CONFIG_BLK_DEV_NBD is not set
+# CONFIG_BLK_DEV_MD is not set
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_INITRD=y
+# CONFIG_BLK_DEV_XD is not set
+CONFIG_PARIDE_PARPORT=y
+# CONFIG_PARIDE is not set
+# CONFIG_BLK_DEV_HD is not set
+
+#
+# NEW devices (io_request, all ALPHA and dangerous)
+#
+# CONFIG_IO_REQUEST is not set
+
+#
+# Networking options
+#
+# CONFIG_PACKET is not set
+# CONFIG_NETLINK is not set
+# CONFIG_FIREWALL is not set
+# CONFIG_NET_ALIAS is not set
+# CONFIG_FILTER is not set
+CONFIG_UNIX=y
+CONFIG_INET=y
+# CONFIG_IP_MULTICAST is not set
+# CONFIG_IP_ADVANCED_ROUTER is not set
+# CONFIG_IP_PNP is not set
+CONFIG_IP_ACCT=y
+# CONFIG_IP_ROUTER is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_IP_ALIAS is not set
+CONFIG_SYN_COOKIES=y
+CONFIG_INET_RARP=y
+# CONFIG_IP_NOSR is not set
+CONFIG_SKB_LARGE=y
+# CONFIG_IPV6 is not set
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_LLC is not set
+# CONFIG_WAN_ROUTER is not set
+# CONFIG_NET_FASTROUTE is not set
+# CONFIG_NET_HW_FLOWCONTROL is not set
+# CONFIG_CPU_IS_SLOW is not set
+# CONFIG_NET_SCHED is not set
+# CONFIG_NET_PROFILE is not set
+
+#
+# SCSI support
+#
+CONFIG_SCSI=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_CHR_DEV_ST=y
+CONFIG_BLK_DEV_SR=y
+CONFIG_BLK_DEV_SR_VENDOR=y
+# CONFIG_CHR_DEV_SG is not set
+# CONFIG_SCSI_MULTI_LUN is not set
+# CONFIG_SCSI_CONSTANTS is not set
+# CONFIG_SCSI_LOGGING is not set
+
+#
+# SCSI low-level drivers
+#
+# CONFIG_SCSI_7000FASST is not set
+# CONFIG_SCSI_AHA152X is not set
+# CONFIG_SCSI_AHA1542 is not set
+# CONFIG_SCSI_AHA1740 is not set
+# CONFIG_SCSI_AIC7XXX is not set
+# CONFIG_SCSI_ADVANSYS is not set
+# CONFIG_SCSI_IN2000 is not set
+# CONFIG_SCSI_AM53C974 is not set
+# CONFIG_SCSI_BUSLOGIC is not set
+# CONFIG_SCSI_DTC3280 is not set
+# CONFIG_SCSI_EATA_DMA is not set
+# CONFIG_SCSI_EATA_PIO is not set
+# CONFIG_SCSI_EATA is not set
+# CONFIG_SCSI_FUTURE_DOMAIN is not set
+# CONFIG_SCSI_GDTH is not set
+# CONFIG_SCSI_GENERIC_NCR5380 is not set
+# CONFIG_SCSI_NCR53C406A is not set
+# CONFIG_SCSI_NCR53C7xx is not set
+CONFIG_SCSI_NCR53C8XX=y
+# CONFIG_SCSI_NCR53C8XX_NVRAM_DETECT is not set
+CONFIG_SCSI_NCR53C8XX_TAGGED_QUEUE=y
+CONFIG_SCSI_NCR53C8XX_IOMAPPED=y
+CONFIG_SCSI_NCR53C8XX_MAX_TAGS=4
+CONFIG_SCSI_NCR53C8XX_SYNC=5
+# CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT is not set
+# CONFIG_SCSI_PAS16 is not set
+# CONFIG_SCSI_PCI2000 is not set
+# CONFIG_SCSI_PCI2220I is not set
+# CONFIG_SCSI_PSI240I is not set
+# CONFIG_SCSI_QLOGIC_FAS is not set
+# CONFIG_SCSI_QLOGIC_ISP is not set
+# CONFIG_SCSI_SEAGATE is not set
+# CONFIG_SCSI_DC390T is not set
+# CONFIG_SCSI_T128 is not set
+# CONFIG_SCSI_U14_34F is not set
+# CONFIG_SCSI_ULTRASTOR is not set
+CONFIG_SCSI_MESH=y
+CONFIG_SCSI_MESH_SYNC_RATE=5
+CONFIG_SCSI_MAC53C94=y
+
+#
+# Network device support
+#
+CONFIG_NETDEVICES=y
+# CONFIG_ARCNET is not set
+# CONFIG_DUMMY is not set
+# CONFIG_EQUALIZER is not set
+CONFIG_NET_ETHERNET=y
+# CONFIG_NET_VENDOR_3COM is not set
+CONFIG_LANCE=y
+# CONFIG_NET_VENDOR_SMC is not set
+# CONFIG_NET_VENDOR_RACAL is not set
+# CONFIG_RTL8139 is not set
+# CONFIG_YELLOWFIN is not set
+# CONFIG_NET_ISA is not set
+CONFIG_NET_EISA=y
+CONFIG_PCNET32=y
+# CONFIG_AC3200 is not set
+# CONFIG_APRICOT is not set
+# CONFIG_CS89x0 is not set
+CONFIG_DE4X5=y
+# CONFIG_DEC_ELCP is not set
+# CONFIG_DGRS is not set
+# CONFIG_EEXPRESS_PRO100 is not set
+# CONFIG_TLAN is not set
+# CONFIG_ES3210 is not set
+# CONFIG_ZNET is not set
+# CONFIG_NET_POCKET is not set
+# CONFIG_FDDI is not set
+# CONFIG_DLCI is not set
+CONFIG_PPP=y
+# CONFIG_SLIP is not set
+# CONFIG_NET_RADIO is not set
+# CONFIG_TR is not set
+# CONFIG_SHAPER is not set
+
+#
+# Amateur Radio support
+#
+# CONFIG_HAMRADIO is not set
+
+#
+# ISDN subsystem
+#
+# CONFIG_ISDN is not set
+
+#
+# CD-ROM drivers (not for SCSI or IDE/ATAPI drives)
+#
+# CONFIG_CD_NO_IDESCSI is not set
+CONFIG_CDROM=y
+
+#
+# Filesystems
+#
+# CONFIG_QUOTA is not set
+# CONFIG_MINIX_FS is not set
+CONFIG_EXT2_FS=y
+CONFIG_ISO9660_FS=y
+CONFIG_JOLIET=y
+CONFIG_FAT_FS=y
+CONFIG_MSDOS_FS=y
+# CONFIG_UMSDOS_FS is not set
+CONFIG_VFAT_FS=y
+CONFIG_PROC_FS=y
+CONFIG_NFS_FS=y
+CONFIG_NFSD=y
+CONFIG_SUNRPC=y
+CONFIG_LOCKD=y
+# CONFIG_CODA_FS is not set
+# CONFIG_SMB_FS is not set
+# CONFIG_HPFS_FS is not set
+# CONFIG_NTFS_FS is not set
+# CONFIG_SYSV_FS is not set
+# CONFIG_AFFS_FS is not set
+CONFIG_HFS_FS=y
+# CONFIG_ROMFS_FS is not set
+# CONFIG_AUTOFS_FS is not set
+# CONFIG_UFS_FS is not set
+# CONFIG_ADFS_FS is not set
+CONFIG_MAC_PARTITION=y
+CONFIG_NLS=y
+
+#
+# Native Language Support
+#
+# CONFIG_NLS_CODEPAGE_437 is not set
+# CONFIG_NLS_CODEPAGE_737 is not set
+# CONFIG_NLS_CODEPAGE_775 is not set
+# CONFIG_NLS_CODEPAGE_850 is not set
+# CONFIG_NLS_CODEPAGE_852 is not set
+# CONFIG_NLS_CODEPAGE_855 is not set
+# CONFIG_NLS_CODEPAGE_857 is not set
+# CONFIG_NLS_CODEPAGE_860 is not set
+# CONFIG_NLS_CODEPAGE_861 is not set
+# CONFIG_NLS_CODEPAGE_862 is not set
+# CONFIG_NLS_CODEPAGE_863 is not set
+# CONFIG_NLS_CODEPAGE_864 is not set
+# CONFIG_NLS_CODEPAGE_865 is not set
+# CONFIG_NLS_CODEPAGE_866 is not set
+# CONFIG_NLS_CODEPAGE_869 is not set
+# CONFIG_NLS_CODEPAGE_874 is not set
+# CONFIG_NLS_ISO8859_1 is not set
+# CONFIG_NLS_ISO8859_2 is not set
+# CONFIG_NLS_ISO8859_3 is not set
+# CONFIG_NLS_ISO8859_4 is not set
+# CONFIG_NLS_ISO8859_5 is not set
+# CONFIG_NLS_ISO8859_6 is not set
+# CONFIG_NLS_ISO8859_7 is not set
+# CONFIG_NLS_ISO8859_8 is not set
+# CONFIG_NLS_ISO8859_9 is not set
+# CONFIG_NLS_KOI8_R is not set
+
+#
+# Frame buffer devices
+#
+CONFIG_FB_OPEN_FIRMWARE=y
+# CONFIG_FB_S3TRIO is not set
+# CONFIG_FB_ATY is not set
+# CONFIG_FB_VIRTUAL is not set
+# CONFIG_FBCON_ADVANCED is not set
+CONFIG_FBCON_MFB=y
+CONFIG_FBCON_CFB8=y
+
+#
+# Character devices
+#
+CONFIG_VT=y
+CONFIG_VT_CONSOLE=y
+# CONFIG_SOFTCURSOR is not set
+CONFIG_SERIAL=y
+# CONFIG_SERIAL_CONSOLE is not set
+# CONFIG_SERIAL_EXTENDED is not set
+# CONFIG_SERIAL_NONSTANDARD is not set
+CONFIG_MOUSE=y
+# CONFIG_ATIXL_BUSMOUSE is not set
+# CONFIG_BUSMOUSE is not set
+# CONFIG_MS_BUSMOUSE is not set
+CONFIG_PSMOUSE=y
+# CONFIG_82C710_MOUSE is not set
+# CONFIG_PC110_PAD is not set
+# CONFIG_UMISC is not set
+# CONFIG_QIC02_TAPE is not set
+# CONFIG_APM is not set
+# CONFIG_WATCHDOG is not set
+# CONFIG_RTC is not set
+# CONFIG_VIDEO_DEV is not set
+# CONFIG_NVRAM is not set
+# CONFIG_JOYSTICK is not set
+# CONFIG_MISC_RADIO is not set
+
+#
+# Ftape, the floppy tape device driver
+#
+# CONFIG_FTAPE is not set
+
+#
+# Sound
+#
+# CONFIG_SOUND is not set
diff -u --recursive --new-file v2.1.96/linux/arch/ppc/config.in linux/arch/ppc/config.in
--- v2.1.96/linux/arch/ppc/config.in	Wed Apr  1 20:11:48 1998
+++ linux/arch/ppc/config.in	Tue Apr 14 17:33:57 1998
@@ -1,30 +1,37 @@
-# $Id: config.in,v 1.36 1997/12/29 21:36:52 geert Exp $
+# $Id: config.in,v 1.47 1998/04/10 10:19:04 geert Exp $
 # For a description of the syntax of this configuration file,
 # see the Configure script.
 #
 mainmenu_name "Linux/PowerPC Kernel Configuration"
 
+
+
 mainmenu_option next_comment
 comment 'Platform support'
 define_bool CONFIG_PPC y
-if [ "`uname`" != "Linux" -o "`uname -m`" != "ppc" ]; then
-  define_bool CONFIG_CROSSCOMPILE y
-else
-  define_bool CONFIG_NATIVE y
-fi
-
-define_bool CONFIG_MACH_SPECIFIC y
-bool 'Build PowerMac Kernel (not PReP or CHRP)?' CONFIG_PMAC
-bool 'Build PReP Kernel (not PowerMac or CHRP)?' CONFIG_PREP
-bool 'Build CHRP Kernel (not PReP or PowerMac)?' CONFIG_CHRP
+#if [ "`uname`" != "Linux" -o "`uname -m`" != "ppc" ]; then
+#  define_bool CONFIG_CROSSCOMPILE y
+#else
+#  define_bool CONFIG_NATIVE y
+#fi
 
 choice 'Processor type' \
-	"Common		CONFIG_COMMON \
-	 601		CONFIG_601	\
-	 603		CONFIG_603	\
-	 604		CONFIG_604" Common
+	"6xx/7xx	CONFIG_6xx \
+	 860/821	CONFIG_8xx" 6xx/7xx
+
+choice 'Machine Type' \
+	"PowerMac	CONFIG_PMAC \
+	 PReP		CONFIG_PREP \
+	 CHRP		CONFIG_CHRP \
+	 PowerMac/PReP/CHRP	CONFIG_ALL_PPC \
+  	 APUS           CONFIG_APUS \
+	 MBX		CONFIG_MBX" PReP
 endmenu
 
+if [ "$CONFIG_ALL_PPC" != "y" ]; then
+  define_bool CONFIG_MACH_SPECIFIC y
+fi
+
 mainmenu_option next_comment
 comment 'General setup'
 
@@ -35,7 +42,11 @@
   bool 'Kernel module loader' CONFIG_KMOD
 fi
 
-define_bool CONFIG_PCI y
+if [ "$CONFIG_APUS" = "y" ]; then
+  define_bool CONFIG_PCI n
+else
+  define_bool CONFIG_PCI y
+fi
 if [ "$CONFIG_PREP" = "y" ]; then
   bool 'PCI bridge optimization' CONFIG_PCI_OPTIMIZE
 fi
@@ -51,6 +62,14 @@
 tristate 'Kernel support for MISC binaries' CONFIG_BINFMT_MISC
 tristate 'Kernel support for JAVA binaries (obsolete)' CONFIG_BINFMT_JAVA
 
+tristate 'Parallel port support' CONFIG_PARPORT
+if [ "$CONFIG_PARPORT" != "n" ]; then
+  dep_tristate '   PC-style hardware' CONFIG_PARPORT_PC $CONFIG_PARPORT
+  if [ "$CONFIG_PARPORT_PC" != "n" ]; then
+    bool '   Support foreign hardware' CONFIG_PARPORT_OTHER
+  fi
+fi
+
 if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
   bool 'New unified console driver (EXPERIMENTAL)' CONFIG_ABSTRACT_CONSOLE
 fi
@@ -68,6 +87,7 @@
 # if compiling specifically for prep or chrp, or supporting all arch's
   if [ "$CONFIG_ABSTRACT_CONSOLE" = "y" ]; then
     bool 'Support for frame buffer devices' CONFIG_FB
+    bool 'Support for VGA devices' CONFIG_VGA_CONSOLE
     bool 'Backward compatibility mode for Xpmac' CONFIG_FB_COMPAT_XPMAC
   else
     bool 'Support for PowerMac console' CONFIG_PMAC_CONSOLE
@@ -79,13 +99,10 @@
   bool 'Support for PowerMac mouse (EXPERIMENTAL)' CONFIG_MACMOUSE
 fi
 bool 'Support for Open Firmware device tree in /proc' CONFIG_PROC_DEVICETREE
+bool 'Include kgdb kernel debugger' CONFIG_KGDB
 bool 'Include xmon kernel debugger' CONFIG_XMON
 
-if [ "$CONFIG_ABSTRACT_CONSOLE" = "y" ]; then
-  if [ "$CONFIG_FB" != "y" ]; then
-    define_bool CONFIG_VGA_CONSOLE y
-  fi
-else
+if [ "$CONFIG_ABSTRACT_CONSOLE" != "y" ]; then
   if [ "$CONFIG_PMAC_CONSOLE" = "y" ]; then
     bool 'Support for Apple "control" display' CONFIG_CONTROL_VIDEO
     bool 'Support for Apple "platinum" display' CONFIG_PLATINUM_VIDEO
diff -u --recursive --new-file v2.1.96/linux/arch/ppc/defconfig linux/arch/ppc/defconfig
--- v2.1.96/linux/arch/ppc/defconfig	Tue Mar 17 22:18:14 1998
+++ linux/arch/ppc/defconfig	Tue Apr 14 17:33:57 1998
@@ -6,22 +6,21 @@
 # Platform support
 #
 CONFIG_PPC=y
-CONFIG_NATIVE=y
-CONFIG_MACH_SPECIFIC=y
+# CONFIG_6xx is not set
+CONFIG_8xx=y
 # CONFIG_PMAC is not set
-CONFIG_PREP=y
+# CONFIG_PREP is not set
 # CONFIG_CHRP is not set
-CONFIG_COMMON=y
+# CONFIG_ALL_PPC is not set
+CONFIG_MBX=y
+CONFIG_MACH_SPECIFIC=y
 
 #
 # General setup
 #
-CONFIG_EXPERIMENTAL=y
-CONFIG_MODULES=y
-CONFIG_MODVERSIONS=y
-CONFIG_KMOD=y
+# CONFIG_EXPERIMENTAL is not set
+# CONFIG_MODULES is not set
 CONFIG_PCI=y
-# CONFIG_PCI_OPTIMIZE is not set
 CONFIG_PCI_OLD_PROC=y
 CONFIG_NET=y
 # CONFIG_SYSCTL is not set
@@ -31,6 +30,7 @@
 CONFIG_KERNEL_ELF=y
 # CONFIG_BINFMT_MISC is not set
 # CONFIG_BINFMT_JAVA is not set
+# CONFIG_PARPORT is not set
 # CONFIG_PMAC_CONSOLE is not set
 # CONFIG_MAC_KEYBOARD is not set
 # CONFIG_MAC_FLOPPY is not set
@@ -46,25 +46,17 @@
 #
 # Floppy, IDE, and other block devices
 #
-CONFIG_BLK_DEV_FD=y
-CONFIG_BLK_DEV_IDE=y
-# CONFIG_BLK_DEV_HD_IDE is not set
-CONFIG_BLK_DEV_IDEDISK=y
-CONFIG_BLK_DEV_IDECD=y
-# CONFIG_BLK_DEV_IDETAPE is not set
-# CONFIG_BLK_DEV_IDEFLOPPY is not set
-# CONFIG_BLK_DEV_IDESCSI is not set
-# CONFIG_BLK_DEV_CMD640 is not set
-# CONFIG_BLK_DEV_RZ1000 is not set
-# CONFIG_BLK_DEV_TRITON is not set
-# CONFIG_IDE_CHIPSETS is not set
+# CONFIG_BLK_DEV_FD is not set
+# CONFIG_BLK_DEV_IDE is not set
+# CONFIG_BLK_DEV_HD_ONLY is not set
 # CONFIG_BLK_DEV_LOOP is not set
 # CONFIG_BLK_DEV_NBD is not set
 # CONFIG_BLK_DEV_MD is not set
 CONFIG_BLK_DEV_RAM=y
 CONFIG_BLK_DEV_INITRD=y
 # CONFIG_BLK_DEV_XD is not set
-# CONFIG_BLK_DEV_EZ is not set
+CONFIG_PARIDE_PARPORT=y
+# CONFIG_PARIDE is not set
 # CONFIG_BLK_DEV_HD is not set
 
 #
@@ -79,83 +71,30 @@
 # CONFIG_NETLINK is not set
 # CONFIG_FIREWALL is not set
 # CONFIG_NET_ALIAS is not set
+# CONFIG_FILTER is not set
 CONFIG_UNIX=y
 CONFIG_INET=y
 # CONFIG_IP_MULTICAST is not set
 # CONFIG_IP_ADVANCED_ROUTER is not set
-# CONFIG_IP_PNP is not set
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_BOOTP=y
+# CONFIG_IP_PNP_RARP is not set
 # CONFIG_IP_ACCT is not set
-# CONFIG_IP_MASQUERADE is not set
 # CONFIG_IP_ROUTER is not set
 # CONFIG_NET_IPIP is not set
 # CONFIG_NET_IPGRE is not set
 # CONFIG_IP_ALIAS is not set
 # CONFIG_SYN_COOKIES is not set
-CONFIG_INET_RARP=y
-# CONFIG_IP_NOSR is not set
-CONFIG_SKB_LARGE=y
-# CONFIG_IPV6 is not set
+# CONFIG_INET_RARP is not set
+CONFIG_IP_NOSR=y
+# CONFIG_SKB_LARGE is not set
 # CONFIG_IPX is not set
 # CONFIG_ATALK is not set
-# CONFIG_AX25 is not set
-# CONFIG_X25 is not set
-# CONFIG_LAPB is not set
-# CONFIG_BRIDGE is not set
-# CONFIG_LLC is not set
-# CONFIG_WAN_ROUTER is not set
-# CONFIG_CPU_IS_SLOW is not set
-# CONFIG_NET_SCHED is not set
 
 #
 # SCSI support
 #
-CONFIG_SCSI=y
-CONFIG_BLK_DEV_SD=y
-CONFIG_CHR_DEV_ST=y
-CONFIG_BLK_DEV_SR=y
-CONFIG_BLK_DEV_SR_VENDOR=y
-# CONFIG_CHR_DEV_SG is not set
-# CONFIG_SCSI_MULTI_LUN is not set
-# CONFIG_SCSI_CONSTANTS is not set
-
-#
-# SCSI low-level drivers
-#
-# CONFIG_SCSI_7000FASST is not set
-# CONFIG_SCSI_AHA152X is not set
-# CONFIG_SCSI_AHA1542 is not set
-# CONFIG_SCSI_AHA1740 is not set
-# CONFIG_SCSI_AIC7XXX is not set
-# CONFIG_SCSI_ADVANSYS is not set
-# CONFIG_SCSI_IN2000 is not set
-# CONFIG_SCSI_AM53C974 is not set
-# CONFIG_SCSI_BUSLOGIC is not set
-# CONFIG_SCSI_DTC3280 is not set
-# CONFIG_SCSI_EATA_DMA is not set
-# CONFIG_SCSI_EATA_PIO is not set
-# CONFIG_SCSI_EATA is not set
-# CONFIG_SCSI_FUTURE_DOMAIN is not set
-# CONFIG_SCSI_GENERIC_NCR5380 is not set
-# CONFIG_SCSI_NCR53C406A is not set
-# CONFIG_SCSI_NCR53C7xx is not set
-CONFIG_SCSI_NCR53C8XX=y
-# CONFIG_SCSI_NCR53C8XX_NVRAM_DETECT is not set
-CONFIG_SCSI_NCR53C8XX_TAGGED_QUEUE=y
-CONFIG_SCSI_NCR53C8XX_IOMAPPED=y
-CONFIG_SCSI_NCR53C8XX_MAX_TAGS=4
-CONFIG_SCSI_NCR53C8XX_SYNC=5
-# CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT is not set
-# CONFIG_SCSI_PPA is not set
-# CONFIG_SCSI_PAS16 is not set
-# CONFIG_SCSI_QLOGIC_FAS is not set
-# CONFIG_SCSI_QLOGIC_ISP is not set
-# CONFIG_SCSI_SEAGATE is not set
-# CONFIG_SCSI_DC390T is not set
-# CONFIG_SCSI_T128 is not set
-# CONFIG_SCSI_U14_34F is not set
-# CONFIG_SCSI_ULTRASTOR is not set
-# CONFIG_SCSI_MESH is not set
-# CONFIG_SCSI_MAC53C94 is not set
+# CONFIG_SCSI is not set
 
 #
 # Network device support
@@ -164,34 +103,28 @@
 # CONFIG_ARCNET is not set
 # CONFIG_DUMMY is not set
 # CONFIG_EQUALIZER is not set
-# CONFIG_ETHERTAP is not set
 CONFIG_NET_ETHERNET=y
 # CONFIG_NET_VENDOR_3COM is not set
-CONFIG_LANCE=y
+# CONFIG_LANCE is not set
 # CONFIG_NET_VENDOR_SMC is not set
 # CONFIG_NET_VENDOR_RACAL is not set
 # CONFIG_NET_ISA is not set
-CONFIG_NET_EISA=y
-CONFIG_PCNET32=y
-# CONFIG_AC3200 is not set
-# CONFIG_APRICOT is not set
-# CONFIG_CS89x0 is not set
-CONFIG_DE4X5=y
-# CONFIG_DEC_ELCP is not set
-# CONFIG_DGRS is not set
-# CONFIG_EEXPRESS_PRO100 is not set
-# CONFIG_TLAN is not set
-# CONFIG_ES3210 is not set
-# CONFIG_ZNET is not set
+# CONFIG_NET_EISA is not set
 # CONFIG_NET_POCKET is not set
 # CONFIG_FDDI is not set
 # CONFIG_DLCI is not set
-# CONFIG_PLIP is not set
-CONFIG_PPP=m
-# CONFIG_NET_RADIO is not set
+# CONFIG_PPP is not set
 # CONFIG_SLIP is not set
+# CONFIG_NET_RADIO is not set
 # CONFIG_TR is not set
-# CONFIG_SHAPER is not set
+# CONFIG_WAN_DRIVERS is not set
+# CONFIG_LAPBETHER is not set
+# CONFIG_X25_ASY is not set
+
+#
+# Amateur Radio support
+#
+# CONFIG_HAMRADIO is not set
 
 #
 # ISDN subsystem
@@ -202,6 +135,7 @@
 # CD-ROM drivers (not for SCSI or IDE/ATAPI drives)
 #
 # CONFIG_CD_NO_IDESCSI is not set
+# CONFIG_CDROM is not set
 
 #
 # Filesystems
@@ -209,63 +143,52 @@
 # CONFIG_QUOTA is not set
 # CONFIG_MINIX_FS is not set
 CONFIG_EXT2_FS=y
-CONFIG_ISO9660_FS=y
-# CONFIG_JOLIET is not set
+# CONFIG_ISO9660_FS is not set
 # CONFIG_FAT_FS is not set
 # CONFIG_MSDOS_FS is not set
 # CONFIG_UMSDOS_FS is not set
 # CONFIG_VFAT_FS is not set
-CONFIG_PROC_FS=y
+# CONFIG_PROC_FS is not set
 CONFIG_NFS_FS=y
+CONFIG_ROOT_NFS=y
 # CONFIG_NFSD is not set
 CONFIG_SUNRPC=y
 CONFIG_LOCKD=y
+# CONFIG_CODA_FS is not set
 # CONFIG_SMB_FS is not set
 # CONFIG_HPFS_FS is not set
+# CONFIG_NTFS_FS is not set
 # CONFIG_SYSV_FS is not set
 # CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
 # CONFIG_ROMFS_FS is not set
 # CONFIG_AUTOFS_FS is not set
 # CONFIG_UFS_FS is not set
 # CONFIG_MAC_PARTITION is not set
-
-#
-# Native Language Support
-#
 # CONFIG_NLS is not set
 
 #
 # Character devices
 #
-CONFIG_VT=y
-CONFIG_VT_CONSOLE=y
-# CONFIG_SOFTCURSOR is not set
+# CONFIG_VT is not set
 CONFIG_SERIAL=y
-CONFIG_SERIAL_EXTENDED=y
-# CONFIG_SERIAL_MANY_PORTS is not set
-# CONFIG_SERIAL_SHARE_IRQ is not set
-# CONFIG_SERIAL_MULTIPORT is not set
-# CONFIG_HUB6 is not set
 CONFIG_SERIAL_CONSOLE=y
+# CONFIG_SERIAL_EXTENDED is not set
 # CONFIG_SERIAL_NONSTANDARD is not set
-# CONFIG_PRINTER is not set
-CONFIG_MOUSE=y
-# CONFIG_ATIXL_BUSMOUSE is not set
-# CONFIG_BUSMOUSE is not set
-# CONFIG_MS_BUSMOUSE is not set
-CONFIG_PSMOUSE=y
-# CONFIG_82C710_MOUSE is not set
-# CONFIG_PC110_PAD is not set
-# CONFIG_UMISC is not set
+# CONFIG_MOUSE is not set
 # CONFIG_QIC02_TAPE is not set
-# CONFIG_FTAPE is not set
 # CONFIG_APM is not set
 # CONFIG_WATCHDOG is not set
 # CONFIG_RTC is not set
 # CONFIG_VIDEO_DEV is not set
-# CONFIG_VIDEO_BT848 is not set
 # CONFIG_NVRAM is not set
 # CONFIG_JOYSTICK is not set
+# CONFIG_MISC_RADIO is not set
+
+#
+# Ftape, the floppy tape device driver
+#
+# CONFIG_FTAPE is not set
 
 #
 # Sound
diff -u --recursive --new-file v2.1.96/linux/arch/ppc/kernel/Makefile linux/arch/ppc/kernel/Makefile
--- v2.1.96/linux/arch/ppc/kernel/Makefile	Mon Jan 12 15:18:13 1998
+++ linux/arch/ppc/kernel/Makefile	Tue Apr 14 17:33:57 1998
@@ -11,14 +11,32 @@
 	$(CC) $(CFLAGS) -D__ASSEMBLY__ -c $< -o $*.o
 
 O_TARGET := kernel.o
-O_OBJS := misc.o traps.o process.o signal.o syscalls.o \
-	  align.o ptrace.o irq.o openpic.o bitops.o ppc_htab.o idle.o \
-	  time.o prep_time.o pmac_time.o chrp_time.o \
-	  setup.o prep_setup.o pmac_setup.o pmac_support.o chrp_setup.o \
-	  pci.o prep_pci.o pmac_pci.o chrp_pci.o \
-	  residual.o prom.o
 OX_OBJS := ppc_ksyms.o
 
+
+O_OBJS := traps.o irq.o idle.o time.o process.o signal.o syscalls.o misc.o \
+	  bitops.o ppc_htab.o setup.o ptrace.o align.o
+
+ifdef CONFIG_PCI
+O_OBJS += pci.o
+endif
+ifdef CONFIG_KGDB
+O_OBJS += ppc-stub.o
+endif
+
+ifeq ($(CONFIG_MBX),y)
+O_OBJS += mbx_setup.o mbx_pci.o softemu8xx.o
+else
+ifeq ($(CONFIG_APUS),y)
+O_OBJS += prom.o openpic.o
+else
+O_OBJS += prep_time.o pmac_time.o chrp_time.o \
+	  prep_setup.o pmac_setup.o pmac_support.o chrp_setup.o \
+	  prep_pci.o pmac_pci.o chrp_pci.o \
+	  residual.o prom.o openpic.o
+endif
+endif
+
 ifdef SMP
 O_OBJS += smp.o
 endif
@@ -38,10 +56,10 @@
 	rm mk_defs.s
 
 find_name : find_name.c
-	$(HOSTCC) -DKERNELBASE=$(KERNELBASE) -o find_name find_name.c
+	$(HOSTCC) -o find_name find_name.c
 
 checks: checks.c
-	$(HOSTCC) -DKERNELBASE=$(KERNELBASE) -fno-builtin -I$(TOPDIR)/include -D__KERNEL__ -o checks checks.c
+	$(HOSTCC) -fno-builtin -I$(TOPDIR)/include -D__KERNEL__ -o checks checks.c
 	./checks
 
 include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v2.1.96/linux/arch/ppc/kernel/align.c linux/arch/ppc/kernel/align.c
--- v2.1.96/linux/arch/ppc/kernel/align.c	Thu Jul 31 13:09:17 1997
+++ linux/arch/ppc/kernel/align.c	Tue Apr 14 17:33:57 1998
@@ -194,9 +194,13 @@
 			return -EFAULT;	/* bad address */
 	}
 
+#ifdef __SMP__
+	if ((flags & F) && (regs->msr & MSR_FP) )
+		smp_giveup_fpu(current);
+#else	
 	if ((flags & F) && last_task_used_math == current)
 		giveup_fpu();
-
+#endif
 	if (flags & M)
 		return 0;		/* too hard for now */
 
@@ -255,12 +259,22 @@
 	 * the kernel with -msoft-float so it doesn't use the
 	 * fp regs for copying 8-byte objects. */
 	case LD+F+S:
+#ifdef __SMP__
+		if (regs->msr & MSR_FP )
+			smp_giveup_fpu(current);
+#else	
 		giveup_fpu();
+#endif		
 		cvt_fd(&data.f, &current->tss.fpr[reg]);
 		/* current->tss.fpr[reg] = data.f; */
 		break;
 	case ST+F+S:
+#ifdef __SMP__
+		if (regs->msr & MSR_FP )
+			smp_giveup_fpu(current);
+#else	
 		giveup_fpu();
+#endif		
 		cvt_df(&current->tss.fpr[reg], &data.f);
 		/* data.f = current->tss.fpr[reg]; */
 		break;
diff -u --recursive --new-file v2.1.96/linux/arch/ppc/kernel/chrp_pci.c linux/arch/ppc/kernel/chrp_pci.c
--- v2.1.96/linux/arch/ppc/kernel/chrp_pci.c	Mon Jan 12 15:18:13 1998
+++ linux/arch/ppc/kernel/chrp_pci.c	Tue Apr 14 17:33:57 1998
@@ -4,13 +4,13 @@
 
 #include <linux/kernel.h>
 #include <linux/pci.h>
-#include <linux/bios32.h>
 #include <linux/delay.h>
 #include <linux/string.h>
 #include <linux/init.h>
 #include <linux/openpic.h>
 
 #include <asm/io.h>
+#include <asm/pgtable.h>
 #include <asm/irq.h>
 #include <asm/hydra.h>
 #include <asm/prom.h>
@@ -22,8 +22,12 @@
 
 volatile struct Hydra *Hydra = NULL;
 
-
 #if 1
+/*
+ * The VLSI Golden Gate II has only 512K of PCI configuration space, so we
+ * limit the bus number to 3 bits
+ */
+
 int chrp_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn,
 				  unsigned char offset, unsigned char *val)
 {
@@ -32,11 +36,6 @@
 	return PCIBIOS_DEVICE_NOT_FOUND;
     }
     *val = in_8((unsigned char *)pci_config_addr(bus, dev_fn, offset));
-    if (offset == PCI_INTERRUPT_LINE) {
-	/* PCI interrupts are controlled by the OpenPIC */
-	if (*val)
-	    *val = openpic_to_irq(*val);
-    }
     return PCIBIOS_SUCCESSFUL;
 }
 
@@ -228,10 +227,11 @@
     unsigned char t8;
     unsigned short t16;
     unsigned int t32;
-    if (pcibios_find_device(PCI_VENDOR_ID_WINBOND,
-			    PCI_DEVICE_ID_WINBOND_83C553, 0, &bus, &dev)
-	== PCIBIOS_SUCCESSFUL) {
-	dev++;
+    struct pci_dev *pdev;
+    if ((pdev = pci_find_device(PCI_VENDOR_ID_WINBOND,
+				PCI_DEVICE_ID_WINBOND_83C553, NULL))) {
+	bus = pdev->bus->number;
+	dev = pdev->devfn + 1;
 	chrp_pcibios_read_config_dword(bus, dev, PCI_VENDOR_ID, &t32);
 	if (t32 == (PCI_DEVICE_ID_WINBOND_82C105<<16) + PCI_VENDOR_ID_WINBOND) {
 #if 0
diff -u --recursive --new-file v2.1.96/linux/arch/ppc/kernel/chrp_setup.c linux/arch/ppc/kernel/chrp_setup.c
--- v2.1.96/linux/arch/ppc/kernel/chrp_setup.c	Mon Jan 12 15:18:13 1998
+++ linux/arch/ppc/kernel/chrp_setup.c	Tue Apr 14 17:33:57 1998
@@ -60,21 +60,6 @@
 extern int rd_image_start;	/* starting block # of image */
 #endif
 
-
-int chrp_ide_irq = 0;
-
-void chrp_ide_init_hwif_ports (ide_ioreg_t *p, ide_ioreg_t base, int *irq)
-{
-	ide_ioreg_t port = base;
-	int i = 8;
-
-	while (i--)
-		*p++ = port++;
-	*p++ = base + 0x206;
-	if (irq != NULL)
-		*irq = chrp_ide_irq;
-}
-
 static const char *gg2_memtypes[4] = {
     "FPM", "SDRAM", "EDO", "BEDO"
 };
@@ -89,6 +74,34 @@
     "Disabled", "Write-Through", "Copy-Back", "Transparent Mode"
 };
 
+#if 0
+#ifdef CONFIG_BLK_DEV_IDE
+int chrp_ide_ports_known;
+ide_ioreg_t chrp_ide_regbase[MAX_HWIFS];
+ide_ioreg_t chrp_idedma_regbase; /* one for both channels */
+unsigned int chrp_ide_irq;
+
+void chrp_ide_probe(void)
+{
+}
+
+void chrp_ide_init_hwif_ports(ide_ioreg_t *p, ide_ioreg_t base, int *irq)
+{
+	int i;
+
+	*p = 0;
+	if (base == 0)
+		return;
+	for (i = 0; i < 8; ++i)
+		*p++ = base + i * 0x10;
+	*p = base + 0x160;
+	if (irq != NULL) {
+		*irq = chrp_ide_irq;
+	}
+}
+#endif /* CONFIG_BLK_DEV_IDE */
+#endif
+
 int
 chrp_get_cpuinfo(char *buffer)
 {
@@ -139,12 +152,63 @@
 	}
 	/* L2 cache */
 	t = in_le32((unsigned *)(GG2_PCI_CONFIG_BASE+GG2_PCI_CC_CTRL));
-	len += sprintf(buffer+len, "l2\t\t: %s %s (%s)\n",
+	len += sprintf(buffer+len, "board l2\t: %s %s (%s)\n",
 		       gg2_cachesizes[(t>>7) & 3], gg2_cachetypes[(t>>2) & 3],
 		       gg2_cachemodes[t & 3]);
 	return len;
 }
 
+    /*
+     *  Fixes for the National Semiconductor PC78308VUL SuperI/O
+     *
+     *  Some versions of Open Firmware incorrectly initialize the IRQ settings
+     *  for keyboard and mouse
+     */
+
+__initfunc(static inline void sio_write(u8 val, u8 index))
+{
+    outb(index, 0x15c);
+    outb(val, 0x15d);
+}
+
+__initfunc(static inline u8 sio_read(u8 index))
+{
+    outb(index, 0x15c);
+    return inb(0x15d);
+}
+
+__initfunc(static void sio_init(void))
+{
+    u8 irq, type;
+
+    /* select logical device 0 (KBC/Keyboard) */
+    sio_write(0, 0x07);
+    irq = sio_read(0x70);
+    type = sio_read(0x71);
+    printk("sio: Keyboard irq %d, type %d: ", irq, type);
+    if (irq == 1 && type == 3)
+	printk("OK\n");
+    else {
+	printk("remapping to irq 1, type 3\n");
+	sio_write(1, 0x70);
+	sio_write(3, 0x71);
+    }
+
+    /* select logical device 1 (KBC/Mouse) */
+    sio_write(1, 0x07);
+    irq = sio_read(0x70);
+    type = sio_read(0x71);
+    printk("sio: Mouse irq %d, type %d: ", irq, type);
+    if (irq == 12 && type == 3)
+	printk("OK\n");
+    else {
+	printk("remapping to irq 12, type 3\n");
+	sio_write(12, 0x70);
+	sio_write(3, 0x71);
+    }
+}
+
+
 __initfunc(void
 chrp_setup_arch(unsigned long * memory_start_p, unsigned long * memory_end_p))
 {
@@ -191,7 +255,12 @@
 	hydra_init();		/* Mac I/O */
 	w83c553f_init();	/* PCI-ISA bridge and IDE */
 
-#ifdef CONFIG_ABSTRACT_CONSOLE
+	/*
+	 *  Fix the Super I/O configuration
+	 */
+	sio_init();
+
+#ifdef CONFIG_FB
 	/* Frame buffer device based console */
 	conswitchp = &fb_con;
 #endif
diff -u --recursive --new-file v2.1.96/linux/arch/ppc/kernel/chrp_time.c linux/arch/ppc/kernel/chrp_time.c
--- v2.1.96/linux/arch/ppc/kernel/chrp_time.c	Mon Jan 12 15:18:13 1998
+++ linux/arch/ppc/kernel/chrp_time.c	Tue Apr 14 17:33:57 1998
@@ -38,7 +38,7 @@
 	rtcs = find_compatible_devices("rtc", "pnpPNP,b00");
 	if (rtcs == NULL || rtcs->addrs == NULL)
 		return;
-	base = ((int *)rtcs->addrs)[2];
+	base = rtcs->addrs[0].address;
 	nvram_as1 = 0;
 	nvram_as0 = base;
 	nvram_data = base + 1;
@@ -69,7 +69,7 @@
 	unsigned char save_control, save_freq_select;
 	struct rtc_time tm;
 
-	to_tm(nowtime + 10*60*60, &tm);	/* XXX for now */
+	to_tm(nowtime, &tm);
 
 	save_control = chrp_cmos_clock_read(RTC_CONTROL); /* tell the clock it's being set */
 
@@ -146,7 +146,7 @@
 	  }
 	if ((year += 1900) < 1970)
 		year += 100;
-	return mktime(year, mon, day, hour, min, sec) - 10*60*60 /* XXX for now */;
+	return mktime(year, mon, day, hour, min, sec);
 }
 
 
@@ -154,6 +154,9 @@
 {
 	struct device_node *cpu;
 	int freq, *fp, divisor;
+
+	if (via_calibrate_decr())
+		return;
 
 	/*
 	 * The cpu node should have a timebase-frequency property
diff -u --recursive --new-file v2.1.96/linux/arch/ppc/kernel/head.S linux/arch/ppc/kernel/head.S
--- v2.1.96/linux/arch/ppc/kernel/head.S	Mon Jan 12 15:18:13 1998
+++ linux/arch/ppc/kernel/head.S	Tue Apr 14 17:33:57 1998
@@ -9,6 +9,8 @@
  *  Low-level exception handlers and MMU support
  *  rewritten by Paul Mackerras.
  *    Copyright (C) 1996 Paul Mackerras.
+ *  MPC8xx modifications Copyright (C) 1997 Dan Malek (dmalek@jlc.net).
+ *  Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk).
  *
  *  This file contains the low-level support and setup for the
  *  PowerPC platform, including trap and interrupt dispatch.
@@ -29,12 +31,14 @@
 #include <linux/sys.h>
 #include <linux/errno.h>
 #include <linux/config.h>
+#ifdef CONFIG_8xx
+#include <asm/mmu.h>
+#include <asm/pgtable.h>
+#include <asm/cache.h>
+#endif
 
 #ifdef CONFIG_APUS
-/* At CYBERBASEp we'll find the following sum:
- * -KERNELBASE+CyberStormMemoryBase
- */
-#define CYBERBASEp (0xfff00000)
+#include <asm/amigappc.h>
 #endif
 
 /* optimization for 603 to load the tlb directly from the linux table */
@@ -78,6 +82,8 @@
 	isync
 
 /* This instruction is not implemented on the PPC 603 or 601 */
+#ifndef CONFIG_8xx
+/* This instruction is not implemented on the PPC 603 or 601 */
 #define tlbia \
 	li	r4,128; \
 	mtctr	r4; \
@@ -85,6 +91,7 @@
 0:	tlbie	r4; \
 	addi	r4,r4,0x1000; \
 	bdnz	0b
+#endif
 
 #define LOAD_BAT(n, offset, reg, RA, RB) \
 	lwz	RA,offset+0(reg); \
@@ -96,6 +103,20 @@
 	mtspr	DBAT##n##U,RA;	\
 	mtspr	DBAT##n##L,RB
 
+#ifndef CONFIG_APUS
+#define tophys(rd,rs,rt)	addis	rd,rs,-KERNELBASE@h
+#define tovirt(rd,rs,rt)	addis	rd,rs,KERNELBASE@h
+#else
+#define tophys(rd,rs,rt)	 \
+	lis	rt,CYBERBASEp@h; \
+	lwz	rt,0(rt);	 \
+	add	rd,rs,rt
+#define tovirt(rd,rs,rt)	 \
+	lis	rt,CYBERBASEp@h; \
+	lwz	rt,0(rt);	 \
+	sub	rd,rs,rt
+#endif
+	
 	.text
 	.globl	_stext
 _stext:
@@ -130,12 +151,44 @@
  *
  * This just gets a minimal mmu environment setup so we can call
  * start_here() to do the real work.
- * -- Cort	
+ * -- Cort
+ *
+ * MPC8xx
+ * This port was done on an MBX board with an 860.  Right now I only
+ * support an ELF compressed (zImage) boot from EPPC-Bug because the
+ * code there loads up some registers before calling us:
+ *   r3: ptr to board info data
+ *   r4: initrd_start or if no initrd then 0
+ *   r5: initrd_end - unused if r4 is 0
+ *   r6: Start of command line string
+ *   r7: End of command line string
+ *
+ * I decided to use conditional compilation instead of checking PVR and
+ * adding more processor specific branches around code I don't need.
+ * Since this is an embedded processor, I also appreciate any memory
+ * savings I can get.
+ *
+ * The MPC8xx does not have any BATs, but it supports large page sizes.
+ * We first initialize the MMU to support 8M byte pages, then load one
+ * entry into each of the instruction and data TLBs to map the first
+ * 8M 1:1.  I also mapped an additional I/O space 1:1 so we can get to
+ * the "internal" processor registers before MMU_init is called.
+ *
+ * The TLB code currently contains a major hack.  Since I use the condition
+ * code register, I have to save and restore it.  I am out of registers, so
+ * I just store it in memory location 0 (the TLB handlers are not reentrant).
+ * To avoid making any decisions, I need to use the "segment" valid bit
+ * in the first level table, but that would require many changes to the
+ * Linux page directory/table functions that I don't want to do right now.
+ *
+ * I used to use SPRG2 for a temporary register in the TLB handler, but it
+ * has since been put to other uses.  I now use a hack to save a register
+ * and the CCR at memory location 0.....Someday I'll fix this.....
+ *	-- Dan
  */
 	
 	.globl	__start
 __start:
-
 /*
  * We have to do any OF calls before we map ourselves to KERNELBASE,
  * because OF may have I/O devices mapped in in that area
@@ -145,12 +198,16 @@
 	mr	r30,r4
 	mr	r29,r5
 	mr	r28,r6
-	mr	r29,r7
+	mr	r27,r7
+#ifndef CONFIG_8xx
+#ifndef CONFIG_APUS
 	bl	prom_init
+#endif
 
 /*
  * Use the first pair of BAT registers to map the 1st 16MB
- * of RAM to KERNELBASE.
+ * of RAM to KERNELBASE.  From this point on we can't safely
+ * call OF any more.
  */
 	mfspr	r9,PVR
 	rlwinm	r9,r9,16,16,31		/* r9 = 1 for 601, 4 for 604 */
@@ -173,17 +230,134 @@
 	addis	r8,r8,KERNELBASE@h
 	addi	r8,r8,2
 #endif
-	mtspr	DBAT0U,r11
+5:	mtspr	DBAT0U,r11
 	mtspr	DBAT0L,r8
-5:	mtspr	IBAT0U,r11
+	mtspr	IBAT0U,r11
 	mtspr	IBAT0L,r8
 	isync
+
+/*
+ * We need to run with _start at physical address 0.
+ * On CHRP, we are loaded at 0x10000 since OF on CHRP uses
+ * the exception vectors at 0 (and therefore this copy
+ * overwrites OF's exception vectors with our own).
+ * If the MMU is already turned on, we copy stuff to KERNELBASE,
+ * otherwise we copy it to 0.
+ */
+	bl	reloc_offset
+	mr	r26,r3
+	addis	r4,r3,KERNELBASE@h	/* current address of _start */
+	cmpwi	0,r4,0			/* are we already running at 0? */
+	beq	2f			/* assume it's OK if so */
+	li	r3,0
+	mfmsr	r0
+	andi.	r0,r0,MSR_DR		/* MMU enabled? */
+	beq	7f
+	lis	r3,KERNELBASE@h		/* if so, are we */
+	cmpw	0,r4,r3			/* already running at KERNELBASE? */
+	beq	2f
+	rlwinm	r4,r4,0,8,31		/* translate source address */
+	add	r4,r4,r3		/* to region mapped with BATs */
+7:	addis	r9,r26,klimit@ha	/* fetch klimit */
+	lwz	r25,klimit@l(r9)
+	addis	r25,r25,-KERNELBASE@h
+	li	r6,0			/* Destination */
+#ifdef CONFIG_APUS
+	lis	r9,0x6170
+	ori	r9,r9,0x7573
+	cmpw	0,r9,r31
+	bne	8f
+	lis	r6,0xfff0		/* Copy to 0xfff00000 on APUS */
+8:
+#endif
+	li	r5,0x4000		/* # bytes of memory to copy */
+	bl	copy_and_flush		/* copy the first 0x4000 bytes */
+#ifdef CONFIG_APUS
+	cmpw	0,r9,r31		/* That's all we need on APUS. */
+	beq	2f
+#endif
+	addi	r0,r3,4f@l		/* jump to the address of 4f */
+	mtctr	r0			/* in copy and do the rest. */
+	bctr				/* jump to the copy */
+4:	mr	r5,r25
+	bl	copy_and_flush		/* copy the rest */
+2:
 /*
  * we now have the 1st 16M of ram mapped with the bats.
  * prep needs the mmu to be turned on here, but pmac already has it on.
  * this shouldn't bother the pmac since it just gets turned on again
  * as we jump to our code at KERNELBASE. -- Cort
  */
+	
+#else /* CONFIG_8xx */
+	tlbia			/* Invalidate all TLB entries */
+	li	r8, 0
+	mtspr	MI_CTR, r8	/* Set instruction control to zero */
+	lis	r8, MD_RESETVAL@h
+	mtspr	MD_CTR, r8	/* Set data TLB control */
+
+	/* Now map the lower 8 Meg into the TLBs.  For this quick hack,
+	 * we can load the instruction and data TLB registers with the
+	 * same values.
+	 */
+	lis	r8, KERNELBASE@h	/* Create vaddr for TLB */
+	ori	r8, r8, MI_EVALID	/* Mark it valid */
+	mtspr	MI_EPN, r8
+	mtspr	MD_EPN, r8
+	li	r8, MI_PS8MEG		/* Set 8M byte page */
+	ori	r8, r8, MI_SVALID	/* Make it valid */
+	mtspr	MI_TWC, r8
+	mtspr	MD_TWC, r8
+	li	r8, MI_BOOTINIT		/* Create RPN for address 0 */
+	mtspr	MI_RPN, r8		/* Store TLB entry */
+	mtspr	MD_RPN, r8
+	lis	r8, MI_Kp@h		/* Set the protection mode */
+	mtspr	MI_AP, r8
+	mtspr	MD_AP, r8
+#ifdef CONFIG_MBX
+	/* Map another 8 MByte at 0xfa000000 to get the processor
+	 * internal registers (among other things).
+	 */
+	lis	r8, 0xfa000000@h	/* Create vaddr for TLB */
+	ori	r8, r8, MD_EVALID	/* Mark it valid */
+	mtspr	MD_EPN, r8
+	li	r8, MD_PS8MEG		/* Set 8M byte page */
+	ori	r8, r8, MD_SVALID	/* Make it valid */
+	mtspr	MD_TWC, r8
+	lis	r8, 0xfa000000@h	/* Create paddr for TLB */
+	ori	r8, r8, MI_BOOTINIT
+	mtspr	MD_RPN, r8
+#endif
+
+	/* Since the cache is enabled according to the information we
+	 * just loaded into the TLB, invalidate and enable the caches here.
+	 * We should probably check/set other modes....later.
+	 */
+	lis	r8, IDC_INVALL@h
+	mtspr	IC_CST, r8
+	mtspr	DC_CST, r8
+	lis	r8, IDC_ENABLE@h
+	mtspr	IC_CST, r8
+#ifdef notdef
+	mtspr	DC_CST, r8
+#else
+	/* I still have a bug somewhere because the Ethernet driver
+	 * does not want to work with copyback enabled.  For now,
+	 * at least enable write through.
+	 */
+#if 0	
+	lis	r8, DC_SFWT@h
+	mtspr	DC_CST, r8
+	lis	r8, IDC_ENABLE@h
+	mtspr	DC_CST, r8
+#endif	
+#endif
+
+/* We now have the lower 8 Meg mapped into TLB entries, and the caches
+ * ready to work.
+ */
+#endif /* CONFIG_8xx */
+
 	mfmsr	r0
 	ori	r0,r0,MSR_DR|MSR_IR
 	mtspr	SRR1,r0
@@ -202,48 +376,6 @@
 #define STACK_UNDERHEAD	64
 	
 /*
- * Macros for storing registers into and loading registers from
- * exception frames.
- */
-#define SAVE_GPR(n, base)	stw	n,GPR0+4*(n)(base)
-#define SAVE_2GPRS(n, base)	SAVE_GPR(n, base); SAVE_GPR(n+1, base)
-#define SAVE_4GPRS(n, base)	SAVE_2GPRS(n, base); SAVE_2GPRS(n+2, base)
-#define SAVE_8GPRS(n, base)	SAVE_4GPRS(n, base); SAVE_4GPRS(n+4, base)
-#define SAVE_10GPRS(n, base)	SAVE_8GPRS(n, base); SAVE_2GPRS(n+8, base)
-#define REST_GPR(n, base)	lwz	n,GPR0+4*(n)(base)
-#define REST_2GPRS(n, base)	REST_GPR(n, base); REST_GPR(n+1, base)
-#define REST_4GPRS(n, base)	REST_2GPRS(n, base); REST_2GPRS(n+2, base)
-#define REST_8GPRS(n, base)	REST_4GPRS(n, base); REST_4GPRS(n+4, base)
-#define REST_10GPRS(n, base)	REST_8GPRS(n, base); REST_2GPRS(n+8, base)
-
-#define SAVE_FPR(n, base)	stfd	n,TSS_FPR0+8*(n)(base)
-#define SAVE_2FPRS(n, base)	SAVE_FPR(n, base); SAVE_FPR(n+1, base)
-#define SAVE_4FPRS(n, base)	SAVE_2FPRS(n, base); SAVE_2FPRS(n+2, base)
-#define SAVE_8FPRS(n, base)	SAVE_4FPRS(n, base); SAVE_4FPRS(n+4, base)
-#define SAVE_16FPRS(n, base)	SAVE_8FPRS(n, base); SAVE_8FPRS(n+8, base)
-#define SAVE_32FPRS(n, base)	SAVE_16FPRS(n, base); SAVE_16FPRS(n+16, base)
-#define REST_FPR(n, base)	lfd	n,TSS_FPR0+8*(n)(base)
-#define REST_2FPRS(n, base)	REST_FPR(n, base); REST_FPR(n+1, base)
-#define REST_4FPRS(n, base)	REST_2FPRS(n, base); REST_2FPRS(n+2, base)
-#define REST_8FPRS(n, base)	REST_4FPRS(n, base); REST_4FPRS(n+4, base)
-#define REST_16FPRS(n, base)	REST_8FPRS(n, base); REST_8FPRS(n+8, base)
-#define REST_32FPRS(n, base)	REST_16FPRS(n, base); REST_16FPRS(n+16, base)
-
-#ifndef CONFIG_APUS
-#define tophys(rd,rs,rt)	addis	rd,rs,-KERNELBASE@h
-#define tovirt(rd,rs,rt)	addis	rd,rs,KERNELBASE@h
-#else
-#define tophys(rd,rs,rt)	 \
-	lis	rt,CYBERBASEp@h; \
-	lwz	rt,0(rt);	 \
-	add	rd,rs,rt
-#define tovirt(rd,rs,rt)	 \
-	lis	rt,CYBERBASEp@h; \
-	lwz	rt,0(rt);	 \
-	sub	rd,rs,rt
-#endif
-	
-/*
  * Exception entry code.  This code runs with address translation
  * turned off, i.e. using physical addresses.
  * We assume sprg3 has the physical address of the current
@@ -303,11 +435,15 @@
 /* Machine check */
 	STD_EXCEPTION(0x200, MachineCheck, MachineCheckException)
 
-/* Data access exception */
+/* Data access exception.
+ * This is "never generated" by the MPC8xx.  We jump to it for other
+ * translation errors.
+ */
 	. = 0x300
 DataAccess:
 	EXCEPTION_PROLOG
 	mfspr	r20,DSISR
+#ifndef CONFIG_8xx
 	andis.	r0,r20,0xa470		/* weird error? */
 	bne	1f			/* if not, try to put a PTE */
 	mfspr	r3,DAR			/* into the hash table */
@@ -315,6 +451,7 @@
 	rlwimi	r4,r20,32-23,29,29	/* DSISR_STORE -> _PAGE_RW */
 	mfspr	r5,SPRG3		/* phys addr of TSS */
 	bl	hash_page
+#endif
 1:	stw	r20,_DSISR(r21)
 	mr	r5,r20
 	mfspr	r4,DAR
@@ -326,10 +463,14 @@
 	.long	do_page_fault
 	.long	int_return
 
-/* Instruction access exception */
+/* Instruction access exception.
+ * This is "never generated" by the MPC8xx.  We jump to it for other
+ * translation errors.
+ */
 	. = 0x400
 InstructionAccess:
 	EXCEPTION_PROLOG
+#ifndef CONFIG_8xx
 	andis.	r0,r23,0x4000		/* no pte found? */
 	beq	1f			/* if so, try to put a PTE */
 	mr	r3,r22			/* into the hash table */
@@ -337,6 +478,7 @@
 	mr	r20,r23			/* SRR1 has reason bits */
 	mfspr	r5,SPRG3		/* phys addr of TSS */
 	bl	hash_page
+#endif
 1:	addi	r3,r1,STACK_FRAME_OVERHEAD
 	mr	r4,r22
 	mr	r5,r23
@@ -347,7 +489,38 @@
 	.long	int_return
 
 /* External interrupt */
-	STD_EXCEPTION(0x500, HardwareInterrupt, do_IRQ)
+	. = 0x500;
+HardwareInterrupt:
+	EXCEPTION_PROLOG;
+#ifdef CONFIG_APUS
+	mfmsr	20
+	xori	r20,r20,MSR_DR
+	sync
+	mtmsr	r20
+	sync
+
+	lis	r3,APUS_IPL_EMU@h
+
+	li	r20,(IPLEMU_SETRESET|IPLEMU_DISABLEINT)
+	stb	r20,APUS_IPL_EMU@l(r3)
+	sync
+
+	lbz	r3,APUS_IPL_EMU@l(r3)
+
+	mfmsr	r20
+	xori	r20,r20,MSR_DR
+	sync
+	mtmsr	r20
+	sync
+
+	stw	r3,(_CCR+4)(r21);
+#endif
+	addi	r3,r1,STACK_FRAME_OVERHEAD;
+	li	r20,MSR_KERNEL;
+	bl	transfer_to_handler;
+	.long	do_IRQ;
+	.long	int_return
+	
 
 /* Alignment exception */
 	. = 0x600
@@ -375,6 +548,7 @@
 	.long	ProgramCheckException
 	.long	int_return
 
+#ifndef CONFIG_8xx
 /* Floating-point unavailable */
 	. = 0x800
 FPUnavailable:
@@ -384,6 +558,11 @@
 	bl	transfer_to_handler	/* if from kernel, take a trap */
 	.long	KernelFP
 	.long	int_return
+#else
+/* No FPU on MPC8xx.  This exception is not supposed to happen.
+*/
+	STD_EXCEPTION(0x800, FPUnavailable, UnknownException)
+#endif
 
 	STD_EXCEPTION(0x900, Decrementer, timer_interrupt)
 	STD_EXCEPTION(0xa00, Trap_0a, UnknownException)
@@ -406,6 +585,7 @@
 	STD_EXCEPTION(0xe00, Trap_0e, UnknownException)
 	STD_EXCEPTION(0xf00, Trap_0f, UnknownException)
 
+#ifndef CONFIG_8xx
 /*
  * Handle TLB miss for instruction on 603/603e.
  * Note: we get an alternate set of r0 - r3 to use automatically.
@@ -502,7 +682,14 @@
 	sync			/* Some chip revs have problems here... */
 	mtmsr	r0
 	b	InstructionAccess
+#else
+/* On the MPC8xx, this is a software emulation interrupt.  It occurs
+ * for all unimplemented and illegal instructions.
+ */
+	STD_EXCEPTION(0x1000, SoftEmu, SoftwareEmulation)
+#endif
 
+#ifndef CONFIG_8xx
 /*
  * Handle TLB miss for DATA Load operation on 603/603e
  */
@@ -598,12 +785,78 @@
 	sync			/* Some chip revs have problems here... */
 	mtmsr	r0
 	b	DataAccess
+#else
+/*
+ * For the MPC8xx, this is a software tablewalk to load the instruction
+ * TLB.  It is modelled after the example in the Motorola manual.  The task
+ * switch loads the M_TWB register with the pointer to the first level table.
+ * If we discover there is no second level table (the value is zero), the
+ * plan was to load that into the TLB, which causes another fault into the
+ * TLB Error interrupt where we can handle such problems.  However, that did
+ * not work, so if we discover there is no second level table, we restore
+ * registers and branch to the error exception.  We have to use the MD_xxx
+ * registers for the tablewalk because the equivalent MI_xxx registers
+ * only perform the attribute functions.
+ */
+InstructionTLBMiss:
+	mtspr	M_TW, r20	/* Save a couple of working registers */
+	mfcr	r20
+	stw	r20, 0(r0)
+	stw	r21, 4(r0)
+	mfspr	r20, SRR0	/* Get effective address of fault */
+	mtspr	MD_EPN, r20	/* Have to use MD_EPN for walk, MI_EPN can't */
+	mfspr	r20, M_TWB	/* Get level 1 table entry address */
+	lwz	r21, 0(r20)	/* Get the level 1 entry */
+	rlwinm.	r20, r21,0,0,20	/* Extract page descriptor page address */
+	beq	2f		/* If zero, don't try to find a pte */
+
+	/* We have a pte table, so load the MI_TWC with the attributes
+	 * for this page, which has only bit 31 set.
+	 */
+	tophys(r21,r21,0)
+	ori	r21,r21,1		/* Set valid bit */
+	mtspr	MI_TWC, r21	/* Set page attributes */
+	mtspr	MD_TWC, r21	/* Load pte table base address */
+	mfspr	r21, MD_TWC	/* ....and get the pte address */
+	lwz	r21, 0(r21)	/* Get the pte */
+
+	/* Set four subpage valid bits (24, 25, 26, and 27).
+	 * Since we currently run MI_CTR.PPCS = 0, the manual says,
+	 *	"If the page size is larger than 4k byte, then all the
+	 *	 4 bits should have the same value."
+	 * I don't really know what to do if the page size is 4k Bytes,
+	 * but I know setting them all to 0 does not work, and setting them
+	 * all to 1 does, so that is the way it is right now.
+	 * BTW, these four bits map to the software only bits in the
+	 * linux page table.  I used to turn them all of, but now just
+	 * set them all for the hardware.
+	li	r20, 0x00f0
+	andc	r20, r21, r20
+	ori	r20, r20, 0x0080
+	 */
+	ori	r20, r21, 0x00f0
+
+	mtspr	MI_RPN, r20	/* Update TLB entry */
 
+	mfspr	r20, M_TW	/* Restore registers */
+	lwz	r21, 0(r0)
+	mtcr	r21
+	lwz	r21, 4(r0)
+	rfi
+
+2:	mfspr	r20, M_TW	/* Restore registers */
+	lwz	r21, 0(r0)
+	mtcr	r21
+	lwz	r21, 4(r0)
+	b	InstructionAccess
+#endif /* CONFIG_8xx */
+	
 /*
  * Handle TLB miss for DATA Store on 603/603e
  */
 	. = 0x1200
 DataStoreTLBMiss:
+#ifndef CONFIG_8xx
 #ifdef NO_RELOAD_HTAB
 /*
  * r0:	stored ctr
@@ -671,27 +924,164 @@
 	ori	r3,r3,0x40	/* Set secondary hash */
 	b	00b			/* Try lookup again */
 #endif /* NO_RELOAD_HTAB */
-	
+#else /* CONFIG_8xx */	
+	mtspr	M_TW, r20	/* Save a couple of working registers */
+	mfcr	r20
+	stw	r20, 0(r0)
+	stw	r21, 4(r0)
+	mfspr	r20, M_TWB	/* Get level 1 table entry address */
+	lwz	r21, 0(r20)	/* Get the level 1 entry */
+	rlwinm.	r20, r21,0,0,20	/* Extract page descriptor page address */
+	beq	2f		/* If zero, don't try to find a pte */
+
+	/* We have a pte table, so load fetch the pte from the table.
+	 */
+	tophys(r21, r21, 0)
+	ori	r21, r21, 1	/* Set valid bit in physical L2 page */
+	mtspr	MD_TWC, r21	/* Load pte table base address */
+	mfspr	r21, MD_TWC	/* ....and get the pte address */
+	lwz	r21, 0(r21)	/* Get the pte */
+
+	/* Set four subpage valid bits (24, 25, 26, and 27).
+	 * Since we currently run MD_CTR.PPCS = 0, the manual says,
+	 *	"If the page size is larger than 4k byte, then all the
+	 *	 4 bits should have the same value."
+	 * I don't really know what to do if the page size is 4k Bytes,
+	 * but I know setting them all to 0 does not work, and setting them
+	 * all to 1 does, so that is the way it is right now.
+	 * BTW, these four bits map to the software only bits in the
+	 * linux page table.  I used to turn them all of, but now just
+	 * set them all for the hardware.
+	li	r20, 0x00f0
+	andc	r20, r21, r20
+	ori	r20, r20, 0x0080
+	 */
+	ori	r20, r21, 0x00f0
+
+	mtspr	MD_RPN, r20	/* Update TLB entry */
+
+	mfspr	r20, M_TW	/* Restore registers */
+	lwz	r21, 0(r0)
+	mtcr	r21
+	lwz	r21, 4(r0)
+	rfi
 
+2:	mfspr	r20, M_TW	/* Restore registers */
+	lwz	r21, 0(r0)
+	mtcr	r21
+	lwz	r21, 4(r0)
+	b	DataAccess
+#endif /* CONFIG_8xx */
+
+#ifndef CONFIG_8xx
 /* Instruction address breakpoint exception (on 603/604) */
 	STD_EXCEPTION(0x1300, Trap_13, InstructionBreakpoint)
+#else
+
+/* This is an instruction TLB error on the MPC8xx.  This could be due
+ * to many reasons, such as executing guarded memory or illegal instruction
+ * addresses.  There is nothing to do but handle a big time error fault.
+ */
+	. = 0x1300
+InstructionTLBError:
+	b	InstructionAccess
+#endif
 
 /* System management exception (603?) */
+#ifndef CONFIG_8xx
 	STD_EXCEPTION(0x1400, Trap_14, UnknownException)
+#else
+
+/* This is the data TLB error on the MPC8xx.  This could be due to
+ * many reasons, including a dirty update to a pte.  We can catch that
+ * one here, but anything else is an error.  First, we track down the
+ * Linux pte.  If it is valid, write access is allowed, but the
+ * page dirty bit is not set, we will set it and reload the TLB.  For
+ * any other case, we bail out to a higher level function that can
+ * handle it.
+ */
+	. = 0x1400
+DataTLBError:
+	mtspr	M_TW, r20	/* Save a couple of working registers */
+	mfcr	r20
+	stw	r20, 0(r0)
+	stw	r21, 4(r0)
+
+	/* First, make sure this was a store operation.
+	*/
+	mfspr	r20, DSISR
+	andis.	r21, r20, 0x0200	/* If set, indicates store op */
+	beq	2f
+
+	mfspr	r20, M_TWB	/* Get level 1 table entry address */
+	lwz	r21, 0(r20)	/* Get the level 1 entry */
+	rlwinm.	r20, r21,0,0,20	/* Extract page descriptor page address */
+	beq	2f		/* If zero, bail */
+
+	/* We have a pte table, so fetch the pte from the table.
+	 */
+	tophys(r21, r21, 0)
+	ori	r21, r21, 1		/* Set valid bit in physical L2 page */
+	mtspr	MD_TWC, r21		/* Load pte table base address */
+	mfspr	r21, MD_TWC		/* ....and get the pte address */
+	lwz	r21, 0(r21)		/* Get the pte */
+
+	andi.	r20, r21, _PAGE_RW	/* Is it writeable? */
+	beq	2f			/* Bail out if not */
+
+	ori	r21, r21, _PAGE_DIRTY	/* Update changed bit */
+	mfspr	r20, MD_TWC		/* Get pte address again */
+	stw	r21, 0(r20)		/* and update pte in table */
+
+	/* Set four subpage valid bits (24, 25, 26, and 27).
+	 * Since we currently run MD_CTR.PPCS = 0, the manual says,
+	 *	"If the page size is larger than 4k byte, then all the
+	 *	 4 bits should have the same value."
+	 * I don't really know what to do if the page size is 4k Bytes,
+	 * but I know setting them all to 0 does not work, and setting them
+	 * all to 1 does, so that is the way it is right now.
+	 * BTW, these four bits map to the software only bits in the
+	 * linux page table.  I used to turn them all of, but now just
+	 * set them all for the hardware.
+	li	r20, 0x00f0
+	andc	r20, r21, r20
+	ori	r20, r20, 0x0080
+	 */
+	ori	r20, r21, 0x00f0
+
+	mtspr	MD_RPN, r20	/* Update TLB entry */
+
+	mfspr	r20, M_TW	/* Restore registers */
+	lwz	r21, 0(r0)
+	mtcr	r21
+	lwz	r21, 4(r0)
+	rfi
+2:
+	mfspr	r20, M_TW	/* Restore registers */
+	lwz	r21, 0(r0)
+	mtcr	r21
+	lwz	r21, 4(r0)
+	b	DataAccess
+#endif /* CONFIG_8xx */
 
 	STD_EXCEPTION(0x1500, Trap_15, UnknownException)
 	STD_EXCEPTION(0x1600, Trap_16, UnknownException)
-	STD_EXCEPTION(0x1700, Trap_17, UnknownException)
+	STD_EXCEPTION(0x1700, Trap_17, TAUException)
 	STD_EXCEPTION(0x1800, Trap_18, UnknownException)
 	STD_EXCEPTION(0x1900, Trap_19, UnknownException)
 	STD_EXCEPTION(0x1a00, Trap_1a, UnknownException)
 	STD_EXCEPTION(0x1b00, Trap_1b, UnknownException)
+/* On the MPC8xx, these next four traps are used for development
+ * support of breakpoints and such.  Someday I will get around to
+ * using them.
+ */
 	STD_EXCEPTION(0x1c00, Trap_1c, UnknownException)
 	STD_EXCEPTION(0x1d00, Trap_1d, UnknownException)
 	STD_EXCEPTION(0x1e00, Trap_1e, UnknownException)
 	STD_EXCEPTION(0x1f00, Trap_1f, UnknownException)
 
-/* Run mode exception */
+#ifndef CONFIG_8xx
+	/* Run mode exception */
 	STD_EXCEPTION(0x2000, RunMode, RunModeException)
 
 	STD_EXCEPTION(0x2100, Trap_21, UnknownException)
@@ -711,6 +1101,9 @@
 	STD_EXCEPTION(0x2f00, Trap_2f, UnknownException)
 
 	. = 0x3000
+#else
+	. = 0x2000
+#endif
 
 /*
  * This code finishes saving the registers to the exception frame
@@ -720,6 +1113,8 @@
 	.globl	transfer_to_handler
 transfer_to_handler:
 	stw	r22,_NIP(r21)
+	lis	r22,MSR_POW@h
+	andc	r23,r23,r22
 	stw	r23,_MSR(r21)
 	SAVE_GPR(7, r21)
 	SAVE_4GPRS(8, r21)
@@ -768,6 +1163,7 @@
 	SYNC
 	rfi
 
+#ifndef CONFIG_8xx
 /*
  * Continuation of the floating-point unavailable handler.
  */
@@ -790,9 +1186,18 @@
 	ori	r5,r5,MSR_FP
 	SYNC
 	mtmsr	r5			/* enable use of fpu now */
+#ifndef __SMP__
 	SYNC
 	cmpi	0,r4,0
 	beq	1f
+#else
+/*
+ * All the saving of last_task_used_math is handled
+ * by a switch_to() call to smp_giveup_fpu() in SMP so 
+ * last_task_used_math is not used. -- Cort
+ */
+	b	1f
+#endif	
 	add	r4,r4,r6
 	addi	r4,r4,TSS	        /* want TSS of last_task_used_math */
 	SAVE_32FPRS(0, r4)
@@ -810,9 +1215,11 @@
 	lfd	fr0,TSS_FPSCR-4(r5)
 	mtfsf	0xff,fr0
 	REST_32FPRS(0, r5)
+#ifndef __SMP__	
 	subi	r4,r5,TSS
 	sub	r4,r4,r6
 	stw	r4,last_task_used_math@l(r3)
+#endif /* __SMP__ */	
 	/* restore registers and return */
 	lwz	r3,_CCR(r21)
 	lwz	r4,_LINK(r21)
@@ -859,16 +1266,6 @@
 
 	.globl	hash_page
 hash_page:
-#ifdef __SMP__
-	lis	r6,hash_table_lock@h
-	ori	r6,r6,hash_table_lock@l
-	tophys(r6,r6,r2)
-1011:	lwarx	r0,0,r6
-	stwcx.	r6,0,r6
-	bne-	1011b
-	cmpi	0,r0,0
-	bne	1011b
-#endif /* __SMP__ */	
 	/* Get PTE (linux-style) and check access */
 	lwz	r5,PG_TABLES(r5)		
 	tophys(r5,r5,r2)		/* convert to phys addr */
@@ -1018,7 +1415,6 @@
 	lwz	r3,0(r2)
 	addi	r3,r3,1
 	stw	r3,0(r2)
-	SYNC
 
 	/* Return from the exception */
 	lwz	r3,_CCR(r21)
@@ -1027,19 +1423,14 @@
 	mtcrf	0xff,r3
 	mtlr	r4
 	mtctr	r5
-#ifdef __SMP__
-	lis	r5,hash_table_lock@h
-	ori	r5,r5,hash_table_lock@l
-	tophys(r5,r5,r6)
-	li	r6,0
-	stw	r6,0(r5)
-#endif /* __SMP__ */	
 	REST_GPR(0, r21)
 	REST_2GPRS(1, r21)
 	REST_4GPRS(3, r21)
 	/* we haven't used xer */
+	SYNC
 	mtspr	SRR1,r23
 	mtspr	SRR0,r22
+	SYNC
 	REST_GPR(20, r21)
 	REST_2GPRS(22, r21)
 	lwz	r21,GPR21(r21)
@@ -1047,16 +1438,37 @@
 	rfi
 	
 hash_page_out:
-#ifdef __SMP__
-	lis	r5,hash_table_lock@h
-	ori	r5,r5,hash_table_lock@l
-	tophys(r5,r5,r6)
-	li	r6,0
-	stw	r6,0(r5)
-#endif /* __SMP__ */	
 	blr
 next_slot:
 	.long	0
+#endif /* CONFIG_8xx */
+	
+#ifndef CONFIG_APUS
+/*
+ * Copy routine used to copy the kernel to start at physical address 0
+ * and flush and invalidate the caches as needed.
+ * r3 = dest addr, r4 = source addr, r5 = copy limit, r6 = start offset
+ * on exit, r3, r4, r5 are unchanged, r6 is updated to be >= r5.
+ */
+copy_and_flush:
+	addi	r5,r5,-4
+	addi	r6,r6,-4
+4:	li	r0,8
+	mtctr	r0
+3:	addi	r6,r6,4			/* copy a cache line */
+	lwzx	r0,r6,r4
+	stwx	r0,r6,r3
+	bdnz	3b
+	dcbst	r6,r3			/* write it to memory */
+	sync
+	icbi	r6,r3			/* flush the icache line */
+	cmplw	0,r6,r5
+	blt	4b
+	isync
+	addi	r5,r5,4
+	addi	r6,r6,4
+	blr
+#endif
 
 #ifdef CONFIG_APUS
 	/* On APUS the first 0x4000 bytes of the kernel	will be mapped
@@ -1072,6 +1484,7 @@
  */
 
 start_here:
+#ifndef CONFIG_8xx
 	/*
 	 * Enable caches and 604-specific features if necessary.
 	 */
@@ -1108,6 +1521,7 @@
 	ori	r11,r11,HID0_BTCD
 5:	mtspr	HID0,r11		/* superscalar exec & br history tbl */
 4:
+#endif /* CONFIG_8xx */
 	/* ptr to current */
 	lis	r2,init_task_union@h
 	ori	r2,r2,init_task_union@l
@@ -1140,6 +1554,9 @@
 	mr	r6,r28
 	mr	r7,r27
 	bl	identify_machine
+#ifdef CONFIG_MBX
+	bl	set_mbx_memory
+#endif
 	bl	MMU_init
 
 /*
@@ -1147,8 +1564,19 @@
  * for SDR1 (hash table pointer) and the segment registers
  * and change to using our exception vectors.
  */
+#ifndef CONFIG_8xx
 	lis	r6,_SDR1@ha
 	lwz	r6,_SDR1@l(r6)
+#else
+	/* The right way to do this would be to track it down through
+	 * init's TSS like the context switch code does, but this is
+	 * easier......until someone changes init's static structures.
+	 */
+	lis	r6, swapper_pg_dir@h
+	tophys(r6,r6,0)
+	ori	r6, r6, swapper_pg_dir@l
+	mtspr	M_TWB, r6
+#endif
 	lis	r4,2f@h
 	ori	r4,r4,2f@l
 	tophys(r4,r4,r3)
@@ -1158,8 +1586,10 @@
 	rfi
 /* Load up the kernel context */
 2:
+
 	SYNC			/* Force all PTE updates to finish */
 	tlbia			/* Clear all TLB entries */
+#ifndef CONFIG_8xx
 	mtspr	SDR1,r6
 	li	r0,16		/* load up segment register values */
 	mtctr	r0		/* for context 0 */
@@ -1179,7 +1609,8 @@
 	LOAD_BAT(1,16,r3,r4,r5)
 	LOAD_BAT(2,32,r3,r4,r5)
 	LOAD_BAT(3,48,r3,r4,r5)
-
+#endif /* CONFIG_8xx */
+	
 /* Set up for using our exception vectors */
 	/* ptr to phys current tss */
 	tophys(r4,r2,r4)
@@ -1188,36 +1619,6 @@
 	li	r3,0
 	mtspr	SPRG2,r3	/* 0 => r1 has kernel sp */
 
-/* On CHRP copy exception vectors down to 0 */
-	lis	r5,_stext@ha
-	addi	r5,r5,_stext@l
-	addis	r5,r5,-KERNELBASE@h
-	cmpwi	0,r5,0
-	beq	77f		/* vectors are already at 0 */
-	li	r3,0x1000
-	mtctr	r3
-	li	r4,-4
-	addi	r5,r5,-4
-74:	lwzu	r0,4(r5)
-	stwu	r0,4(r4)
-	bdnz	74b
-	/* need to flush/invalidate caches too */
-	li	r3,0x4000/CACHE_LINE_SIZE
-	li	r4,0
-	mtctr	r3
-73:	dcbst	0,r4
-	addi	r4,r4,CACHE_LINE_SIZE
-	bdnz	73b
-	sync
-	li	r4,0
-	mtctr	r3
-72:	icbi	0,r4
-	addi	r4,r4,CACHE_LINE_SIZE
-	bdnz	72b
-	sync
-	isync
-77:
-
 /* Now turn on the MMU for real! */
 	li	r4,MSR_KERNEL
 	lis	r3,start_kernel@h
@@ -1227,29 +1628,6 @@
 	rfi			/* enable MMU and jump to start_kernel */
 
 
-	.globl reset_SDR1
-reset_SDR1:
-	lis	r6,_SDR1@ha
-	lwz	r6,_SDR1@l(r6)
-	mfmsr	r5
-	li	r4,0
-	ori	r4,r4,MSR_EE|MSR_IR|MSR_DR
-	andc	r3,r5,r4
-	lis	r4,2f@h
-	ori	r4,r4,2f@l
-	tophys(r4,r4,r5)
-	mtspr	SRR0,r4
-	mtspr	SRR1,r3
-	rfi
-2:	/* load new SDR1 */
-	tlbia
-	mtspr	SDR1,r6
-	/* turn the mmu back on */
-	mflr	r3
-	mtspr	SRR0,r3
-	mtspr	SRR1,r5
-	rfi
-
 /*
  * FP unavailable trap from kernel - print a message, but let
  * the task use FP in the kernel until it returns to user mode.
@@ -1272,10 +1650,19 @@
  * and save its floating-point registers in its thread_struct.
  * Enables the FPU for use in the kernel on return.
  */
+/* smp_giveup_fpu() takes an arg to tell it where to save the fpu
+ * regs since last_task_used_math can't be trusted (many many race
+ * conditions). -- Cort
+ */
+	.globl	smp_giveup_fpu
+smp_giveup_fpu:	
+	mr	r4,r3
+	b	12f
 	.globl	giveup_fpu
 giveup_fpu:
 	lis	r3,last_task_used_math@ha
 	lwz	r4,last_task_used_math@l(r3)
+12:		
 	mfmsr	r5
 	ori	r5,r5,MSR_FP
 	SYNC
@@ -1284,8 +1671,10 @@
 	cmpi	0,r4,0
 	beqlr-				/* if no previous owner, done */
 	addi	r4,r4,TSS	        /* want TSS of last_task_used_math */
+#ifndef __SMP__
 	li	r5,0
 	stw	r5,last_task_used_math@l(r3)
+#endif /* __SMP__ */	
 	SAVE_32FPRS(0, r4)
 	mffs	fr0
 	stfd	fr0,TSS_FPSCR-4(r4)
@@ -1445,6 +1834,18 @@
  *
  * The code which creates the new task context is in 'copy_thread'
  * in arch/ppc/kernel/process.c
+ *
+ * The MPC8xx has something that currently happens "automagically."
+ * Unshared user space address translations are subject to ASID (context)
+ * match.  During each task switch, the ASID is incremented.  We can
+ * guarantee (I hope :-) that no entries currently match this ASID
+ * because every task will cause at least a TLB entry to be loaded for
+ * the first instruction and data access, plus the kernel running will
+ * have displaced several more TLBs.  The MMU contains 32 entries for
+ * each TLB, and there are 16 contexts, so we just need to make sure
+ * two pages get replaced for every context switch, which currently
+ * happens.  There are other TLB management techniques that I will
+ * eventually implement, but this is the easiest for now.  -- Dan
  */	
 _GLOBAL(_switch)
 	stwu	r1,-INT_FRAME_SIZE-STACK_UNDERHEAD(r1)
@@ -1476,6 +1877,7 @@
 	SYNC
 	lwz	r1,KSP(r4)	/* Load new stack pointer */
 	addi	r2,r4,-TSS	/* Update current */
+#ifndef CONFIG_8xx
 	/* Set up segment registers for new task */
 	rlwinm	r5,r5,4,8,27	/* VSID = context << 4 */
 	addis	r5,r5,0x6000	/* Set Ks, Ku bits */
@@ -1486,9 +1888,32 @@
 	addi	r5,r5,1		/* next VSID */
 	addis	r3,r3,0x1000	/* address of next segment */
 	bdnz	3b
+#else
+/* On the MPC8xx, we place the physical address of the new task
+ * page directory loaded into the MMU base register, and set the
+ * ASID compare register with the new "context".
+ */
+	mtspr	M_CASID, r5		/* Update context */
+	lwz	r5,MM-TSS(r4)		/* Get virtual address of mm */
+	lwz	r5,PGD(r5)		/* get new->mm->pgd */	
+	tophys(r5, r5, 0)		/* convert to phys addr */
+	mtspr	M_TWB, r5		/* Update MMU base address */
+#endif
 	SYNC
 
 /* FALL THROUGH into int_return */
+#ifdef __SMP__
+	/* drop scheduler_lock since we weren't called by schedule() */
+	lwz	r5,TSS_SMP_FORK_RET(r4)
+	cmpi	0,r5,0
+	beq+	int_return
+	li	r3,0
+	lis	r5,scheduler_lock@ha
+	stw	r3,TSS_SMP_FORK_RET(r4)
+	stw	r3,scheduler_lock@l+4(r5)	/* owner_pc */
+	stw	r3,scheduler_lock@l+8(r5)	/* owner_cpu */
+	stw	r3,scheduler_lock@l(r5)		/* lock */
+#endif /* __SMP__ */
 
 /*
  * Trap exit.
@@ -1566,6 +1991,18 @@
 	SYNC
 	rfi
 
+#if 0/*def __SMP__*/
+	.globl	ret_from_smpfork
+ret_from_smpfork:
+	/* drop scheduler_lock since schedule() called us */
+	lis	r4,scheduler_lock@ha
+	li	r5,0
+	stw	r5,scheduler_lock@l+4(r4)	/* owner_pc */
+	stw	r5,scheduler_lock@l+8(r4)	/* owner_cpu */
+	stw	r5,scheduler_lock@l(r4)		/* lock */
+	b	int_return
+#endif /* __SMP__ */
+	
 /*
  * Fake an interrupt from kernel mode.
  * This is used when enable_irq loses an interrupt.
@@ -1686,6 +2123,7 @@
  * Flush entries from the hash table with VSIDs in the range
  * given.
  */
+#ifndef CONFIG_8xx	
 _GLOBAL(flush_hash_segments)
 #ifdef NO_RELOAD_HTAB
 /*
@@ -1700,15 +2138,6 @@
 	rlwnm.	r0,r9,r0,0,0
 	bne	99f
 #endif /* NO_RELOAD_HTAB */
-#ifdef __SMP__
-	lis	r6,hash_table_lock@h
-	ori	r6,r6,hash_table_lock@l
-1011:	lwarx	r0,0,r6
-	stwcx.	r6,0,r6
-	bne-	1011b
-	cmpi	0,r0,0
-	bne	1011b
-#endif /* __SMP__ */	
 	rlwinm	r3,r3,7,1,24		/* put VSID lower limit in position */
 	oris	r3,r3,0x8000		/* set V bit */
 	rlwinm	r4,r4,7,1,24		/* put VSID upper limit in position */
@@ -1730,12 +2159,6 @@
 	stw	r0,0(r5)		/* invalidate entry */
 2:	bdnz	1b			/* continue with loop */
 	sync
-#ifdef __SMP__
-	lis	r5,hash_table_lock@h
-	ori	r5,r5,hash_table_lock@l
-	li	r6,0
-	stw	r6,0(r5)
-#endif /* __SMP__ */	
 99:	tlbia
 	isync
 	blr
@@ -1753,15 +2176,6 @@
 	rlwnm.	r0,r9,r0,0,0
 	bne	99f
 #endif /* NO_RELOAD_HTAB */		
-#ifdef __SMP__
-	lis	r6,hash_table_lock@h
-	ori	r6,r6,hash_table_lock@l
-1011:	lwarx	r0,0,r6
-	stwcx.	r6,0,r6
-	bne-	1011b
-	cmpi	0,r0,0
-	bne	1011b
-#endif /* __SMP__ */	
 	rlwinm	r3,r3,11,1,20		/* put context into vsid */
 	rlwimi	r3,r4,11,21,24		/* put top 4 bits of va into vsid */
 	oris	r3,r3,0x8000		/* set V (valid) bit */
@@ -1794,22 +2208,17 @@
 3:	li	r0,0
 	stw	r0,0(r7)		/* invalidate entry */
 4:	sync
-#ifdef __SMP__
-	lis	r5,hash_table_lock@h
-	ori	r5,r5,hash_table_lock@l
-	li	r6,0
-	stw	r6,0(r5)
-#endif /* __SMP__ */	
 99:	tlbie	r4			/* in hw tlb too */
 	isync
 	blr
-
+#endif /* CONFIG_8xx */
 /*
  * This routine is just here to keep GCC happy - sigh...
  */	
 _GLOBAL(__main)
 	blr
 
+#ifndef CONFIG_8xx		
 /*
  * On CHRP, the Run-Time Abstraction Services (RTAS) have to be
  * called with the MMU off.
@@ -1819,9 +2228,9 @@
 	stwu	r1,-16(r1)
 	mflr	r0
 	stw	r0,20(r1)
-	addis	r3,r3,-KERNELBASE@h
 	lis	r4,rtas_data@ha
 	lwz	r4,rtas_data@l(r4)
+	addis	r4,r4,-KERNELBASE@h
 	lis	r6,1f@ha	/* physical return address for rtas */
 	addi	r6,r6,1f@l
 	addis	r6,r6,-KERNELBASE@h
@@ -1829,14 +2238,15 @@
 	addis	r7,r7,-KERNELBASE@h
 	lis	r8,rtas_entry@ha
 	lwz	r8,rtas_entry@l(r8)
+	addis	r5,r8,-KERNELBASE@h
 	mfmsr	r9
 	stw	r9,8(r1)
-	li	r0,0
 	ori	r0,r0,MSR_EE|MSR_SE|MSR_BE
 	andc	r0,r9,r0
 	andi.	r9,r9,MSR_ME|MSR_RI
 	sync			/* disable interrupts so SRR0/1 */
 	mtmsr	r0		/* don't get trashed */
+	li	r6,0	
 	mtlr	r6
 	mtspr	SPRG2,r7
 	mtspr	SRR0,r8
@@ -1850,23 +2260,26 @@
 	mtspr	SRR0,r8
 	mtspr	SRR1,r9
 	rfi			/* return to caller */
+#endif /* CONFIG_8xx */
 
+#ifdef CONFIG_8xx
+/* This is called during an exec when new page tables are created.
+ * It maps to the SET_PAGE_DIR macro.  I guess I should make it an
+ * inline function.
+ */
+_GLOBAL(set_page_dir)
+	addis	r3,r3,-KERNELBASE@h	/* convert to phys addr */
+	mtspr	M_TWB, r3		/* Update MMU base address */
+	blr
+#endif
 	
-	.globl amhere
-amhere:	.long 0
-	
+		
 #ifdef __SMP__
 /*
  * Secondary processor begins executing here.
  */
 	.globl	secondary_entry
 secondary_entry:
-	lis	r0,amhere@h
-	ori	r0,r0,amhere@l
-	addis	r0,r0,-KERNELBASE@h
-	stw	r0,0(r0)
-	sync
-	isync	
 	/* just like __start() with a few changes -- Cort */
 	mfspr	r9,PVR
 	rlwinm	r9,r9,16,16,31		/* r9 = 1 for 601, 4 for 604 */
@@ -1938,16 +2351,6 @@
 	ori	r11,r11,HID0_BTCD
 5:	mtspr	HID0,r11		/* superscalar exec & br history tbl */
 4:
-	/* get ptr to current */
-	lis	r2,current_set@h
-	ori	r2,r2,current_set@l
-	/* assume we're second processor for now */
-	lwz	r2,4(r2)
-	/* stack */
-	addi	r1,r2,TASK_UNION_SIZE
-	li	r0,0
-	stwu	r0,-STACK_FRAME_OVERHEAD(r1)
-		
 /*
  * init_MMU on the first processor has setup the variables
  * for us - all we need to do is load them -- Cort 
@@ -1969,6 +2372,18 @@
 	rfi
 /* Load up the kernel context */
 2:
+	/* get ptr to current */
+	lis	r2,current_set@h
+	ori	r2,r2,current_set@l
+	/* assume we're second processor for now */
+	tophys(r2,r2,r10)
+	lwz	r2,4(r2)
+	/* stack */
+	addi	r1,r2,TASK_UNION_SIZE
+	li	r0,0
+	tophys(r3,r1,r10)
+	stwu	r0,-STACK_FRAME_OVERHEAD(r3)
+	
 	SYNC			/* Force all PTE updates to finish */
 	tlbia			/* Clear all TLB entries */
 	mtspr	SDR1,r6
@@ -2025,6 +2440,31 @@
 /* should never return */
 	.long 0
 #endif /* __SMP__ */
+
+#ifdef CONFIG_MBX
+/* Jump into the system reset for the MBX rom.
+ * We first disable the MMU, and then jump to the ROM reset address.
+ *
+ * This does not work, don't bother trying.  There is no place in
+ * the ROM we can jump to cause a reset.  We will have to program
+ * a watchdog of some type that we don't service to cause a processor
+ * reset.
+ */
+	.globl	MBX_gorom
+MBX_gorom:
+	li	r3,MSR_KERNEL & ~(MSR_IR|MSR_DR)
+	lis	r4,2f@h
+	addis	r4,r4,-KERNELBASE@h
+	ori	r4,r4,2f@l
+	mtspr	SRR0,r4
+	mtspr	SRR1,r3
+	rfi
+2:
+	lis	r4, 0xfe000000@h
+	addi	r4, r4, 0xfe000000@l
+	mtlr	r4
+	blr
+#endif
 	
 /*
  * We put a few things here that have to be page-aligned.
diff -u --recursive --new-file v2.1.96/linux/arch/ppc/kernel/idle.c linux/arch/ppc/kernel/idle.c
--- v2.1.96/linux/arch/ppc/kernel/idle.c	Mon Feb 23 18:12:02 1998
+++ linux/arch/ppc/kernel/idle.c	Tue Apr 14 17:33:57 1998
@@ -1,5 +1,5 @@
 /*
- * $Id: idle.c,v 1.13 1998/01/06 06:44:55 cort Exp $
+ * $Id: idle.c,v 1.35 1998/04/07 20:24:23 cort Exp $
  *
  * Idle daemon for PowerPC.  Idle daemon will handle any action
  * that needs to be taken when the system becomes idle.
@@ -31,41 +31,44 @@
 #include <asm/smp_lock.h>
 #include <asm/processor.h>
 #include <asm/mmu.h>
+#include <asm/cache.h>
+#ifdef CONFIG_PMAC
+#include <asm/mediabay.h>
+#endif
 
-int zero_paged(void *unused);
-void inline power_save(void);
+void zero_paged(void);
+void power_save(void);
 void inline htab_reclaim(void);
 
+unsigned long htab_reclaim_on = 0;
+unsigned long zero_paged_on = 0;
+
 int idled(void *unused)
 {
 	int ret = -EPERM;
 
-	/*
-	 * want one per cpu since it would be nice to have all
-	 * processors who aren't doing anything
-	 * zero-ing pages since this daemon is lock-free
-	 * -- Cort
-	 */
-	/* kernel_thread(zero_paged, NULL, 0); */
-
-#ifdef __SMP__	
-printk("SMP %d: in idle.  current = %s/%d\n",
-       current->processor,current->comm,current->pid);
-#endif /* __SMP__ */
 	for (;;)
 	{
+		__sti();
+		
 		/* endless loop with no priority at all */
 		current->priority = -100;
 		current->counter = -100;
+		
+		check_pgt_cache();
 
-		/* endless idle loop with no priority at all */
-		/* htab_reclaim(); */
-		schedule();
+		if ( !need_resched && zero_paged_on ) zero_paged();
+		if ( !need_resched && htab_reclaim_on ) htab_reclaim();
+
+		/*
+		 * Only processor 1 may sleep now since processor 2 would
+		 * never wake up.  Need to add timer code for processor 2
+		 * then it can sleep. -- Cort
+		 */
 #ifndef __SMP__
-		/* can't do this on smp since second processor
-		   will never wake up -- Cort */
-		/* power_save(); */
-#endif /* __SMP__  */
+		if ( !need_resched ) power_save();
+#endif /* __SMP__ */
+		schedule();
 	}
 	ret = 0;
 	return ret;
@@ -76,13 +79,16 @@
  * Mark 'zombie' pte's in the hash table as invalid.
  * This improves performance for the hash table reload code
  * a bit since we don't consider unused pages as valid.
- * I haven't done any rigorous performance analysis yet
- * so it's still experimental and turned off here.
  *  -- Cort
  */
+PTE *reclaim_ptr = 0;
 void inline htab_reclaim(void)
 {
+#ifndef CONFIG_8xx		
+#if 0	
 	PTE *ptr, *start;
+	static int dir = 1;
+#endif	
 	struct task_struct *p;
 	unsigned long valid = 0;
 	extern PTE *Hash, *Hash_end;
@@ -91,28 +97,33 @@
 	/* if we don't have a htab */
 	if ( Hash_size == 0 )
 		return;
-	/*lock_dcache();*/
-	
+	lock_dcache(1);
+
+#if 0	
 	/* find a random place in the htab to start each time */
-	start = &Hash[jiffies%(Hash_size/sizeof(ptr))];
-	for ( ptr = start; ptr < Hash_end ; ptr++)
+	start = &Hash[jiffies%(Hash_size/sizeof(PTE))];
+	/* go a different direction each time */
+	dir *= -1;
+        for ( ptr = start;
+	      !need_resched && (ptr != Hash_end) && (ptr != Hash);
+	      ptr += dir)
 	{
-		if ( ptr == start )
-			return;
-		if ( ptr == Hash_end )
-			ptr = Hash;
-		valid = 0;
-		if (!ptr->v)
+#else
+	if ( !reclaim_ptr ) reclaim_ptr = Hash;
+	while ( !need_resched )
+	{
+		reclaim_ptr++;
+		if ( reclaim_ptr == Hash_end ) reclaim_ptr = Hash;
+#endif	  
+		if (!reclaim_ptr->v)
 			continue;
+		valid = 0;
 		for_each_task(p)
 		{
 			if ( need_resched )
-			{
-				/*unlock_dcache();*/
-				return;
-			}
+				goto out;
 			/* if this vsid/context is in use */
-			if ( (ptr->vsid >> 4) == p->mm->context )
+			if ( (reclaim_ptr->vsid >> 4) == p->mm->context )
 			{
 				valid = 1;
 				break;
@@ -121,19 +132,28 @@
 		if ( valid )
 			continue;
 		/* this pte isn't used */
-		ptr->v = 0;
+		reclaim_ptr->v = 0;
 	}
-	/*unlock_dcache();*/
+out:
+	if ( need_resched ) printk("need_resched: %x\n", need_resched);
+	unlock_dcache();
+#endif /* CONFIG_8xx */
 }
-
+	
 /*
  * Syscall entry into the idle task. -- Cort
  */
 asmlinkage int sys_idle(void)
 {
+	extern int media_bay_task(void *);
 	if(current->pid != 0)
 		return -EPERM;
 
+#ifdef CONFIG_PMAC
+	if (media_bay_present)
+		kernel_thread(media_bay_task, NULL, 0);
+#endif
+
 	idled(NULL);
 	return 0; /* should never execute this but it makes gcc happy -- Cort */
 }
@@ -157,10 +177,8 @@
 unsigned long bytecount = 0;	/* pointer into the currently being zero'd page */
 unsigned long zerocount = 0;	/* # currently pre-zero'd pages */
 unsigned long zerototal = 0;	/* # pages zero'd over time -- for ooh's and ahhh's */
-unsigned long pageptr = 0;	/* current page being zero'd */
 unsigned long zeropage_hits = 0;/* # zero'd pages request that we've done */
 unsigned long zeropage_calls = 0;/* # zero'd pages request that've been made */
-static struct wait_queue * page_zerod_wait = NULL;
 #define PAGE_THRESHOLD 96       /* how many pages to keep pre-zero'd */
 
 /*
@@ -189,7 +207,6 @@
 		 */
 		atomic_inc((atomic_t *)&zeropage_hits);
 		atomic_dec((atomic_t *)&zerocount);
-		wake_up(&page_zerod_wait);
 		need_resched = 1;
 		
 		/* zero out the pointer to next in the page */
@@ -201,35 +218,18 @@
 
 /*
  * Experimental stuff to zero out pages in the idle task
- * to speed up get_free_pages() -- Cort
- * Zero's out pages until we need to resched or
- * we've reached the limit of zero'd pages.
+ * to speed up get_free_pages(). Zero's out pages until
+ * we've reached the limit of zero'd pages.  We handle
+ * reschedule()'s in here so when we return we know we've
+ * zero'd all we need to for now.
  */
-int zero_paged(void *unused)
+void zero_paged(void)
 {
-	extern pte_t *get_pte( struct mm_struct *mm, unsigned long address );
-	pgd_t *dir;
-	pmd_t *pmd;
+	unsigned long pageptr = 0;	/* current page being zero'd */
 	pte_t *pte;
-
-	sprintf(current->comm, "zero_paged (idle)");
-	/* current->blocked = ~0UL; */
 	
-#ifdef __SMP__
-	printk("Started zero_paged (cpu %d)\n", hard_smp_processor_id());
-#else
-	printk("Started zero_paged\n");
-#endif /* __SMP__ */
-	
-	__sti();
-	while ( 1 )
+	while ( zerocount <= PAGE_THRESHOLD )
 	{
-		/* don't want to be pre-empted by swapper or power_save */
-		current->priority = -98;
-		current->counter = -98;
-		/* we don't want to run until we have something to do */
-		while ( zerocount >= PAGE_THRESHOLD )
-			sleep_on(&page_zerod_wait);
 		/*
 		 * Mark a page as reserved so we can mess with it
 		 * If we're interrupted we keep this page and our place in it
@@ -237,7 +237,7 @@
 		 */
 		pageptr = __get_free_pages(GFP_ATOMIC, 0);
 		if ( !pageptr )
-			goto retry;
+			return;
 		
 		if ( need_resched )
 			schedule();
@@ -245,20 +245,15 @@
 		/*
 		 * Make the page no cache so we don't blow our cache with 0's
 		 */
-		dir = pgd_offset( init_task.mm, pageptr );
-		if (dir)
+		pte = find_pte(init_task.mm, pageptr);
+		if ( !pte )
 		{
-			pmd = pmd_offset(dir, pageptr & PAGE_MASK);
-			if (pmd && pmd_present(*pmd))
-			{
-				pte = pte_offset(pmd, pageptr & PAGE_MASK);
-				if (pte && pte_present(*pte))
-				{			
-					pte_uncache(*pte);
-					flush_tlb_page(find_vma(init_task.mm,pageptr),pageptr);
-				}
-			}
+			printk("pte NULL in zero_paged()\n");
+			return;
 		}
+		
+		pte_uncache(*pte);
+		flush_tlb_page(find_vma(init_task.mm,pageptr),pageptr);
 	
 		/*
 		 * Important here to not take time away from real processes.
@@ -308,35 +303,34 @@
 		 */
 		atomic_inc((atomic_t *)&zerocount);
 		atomic_inc((atomic_t *)&zerototal);
-retry:	
-		schedule();
 	}
 }
 
-void inline power_save(void)
+int powersave_mode = HID0_DOZE;
+
+void power_save(void)
 {
 	unsigned long msr, hid0;
 
-	/* no powersaving modes on the 601 */
-	if(  (_get_PVR()>>16) == 1 )
+	/* only sleep on the 603-family/750 processors */
+	switch (_get_PVR() >> 16) {
+	case 3:			/* 603 */
+	case 6:			/* 603e */
+	case 7:			/* 603ev */
+	case 8:			/* 750 */
+		break;
+	default:
 		return;
+	}
 	
-	__sti();
-	asm volatile(
-		/* clear powersaving modes and set nap mode */
-		"mfspr %3,1008 \n\t"
-		"andc  %3,%3,%4 \n\t"
-		"or    %3,%3,%5 \n\t"
-		"mtspr 1008,%3 \n\t"
-		/* enter the mode */
-		"mfmsr %0 \n\t"
-		"oris  %0,%0,%2 \n\t"
-		"sync \n\t"
-		"mtmsr %0 \n\t"
-		"isync \n\t"
-		: "=&r" (msr)
-		: "0" (msr), "i" (MSR_POW>>16),
-		"r" (hid0),
-		"r" (HID0_DOZE|HID0_NAP|HID0_SLEEP),
-		"r" (HID0_NAP));
+	save_flags(msr);
+	cli();
+	if (!need_resched) {
+		asm("mfspr %0,1008" : "=r" (hid0) :);
+		hid0 &= ~(HID0_NAP | HID0_SLEEP | HID0_DOZE);
+		hid0 |= powersave_mode | HID0_DPM;
+		asm("mtspr 1008,%0" : : "r" (hid0));
+		msr |= MSR_POW;
+	}
+	restore_flags(msr);
 }
diff -u --recursive --new-file v2.1.96/linux/arch/ppc/kernel/irq.c linux/arch/ppc/kernel/irq.c
--- v2.1.96/linux/arch/ppc/kernel/irq.c	Thu Feb 12 20:56:04 1998
+++ linux/arch/ppc/kernel/irq.c	Tue Apr 14 17:33:57 1998
@@ -14,6 +14,14 @@
  * instead of just grabbing them. Thus setups with different IRQ numbers
  * shouldn't result in any weird surprises, and installing new handlers
  * should be easier.
+ *
+ * The MPC8xx has an interrupt mask in the SIU.  If a bit is set, the
+ * interrupt is _enabled_.  As expected, IRQ0 is bit 0 in the 32-bit
+ * mask register (of which only 16 are defined), hence the weird shifting
+ * and compliment of the cached_irq_mask.  I want to be able to stuff
+ * this right into the SIU SMASK register.
+ * Many of the prep/chrp functions are conditional compiled on CONFIG_8xx
+ * to reduce code space and undefined function references.
  */
 
 
@@ -30,30 +38,41 @@
 #include <linux/malloc.h>
 #include <linux/openpic.h>
 #include <linux/pci.h>
-#include <linux/bios32.h>
 #include <linux/openpic.h>
 
 #include <asm/hydra.h>
 #include <asm/system.h>
 #include <asm/io.h>
+#include <asm/pgtable.h>
 #include <asm/irq.h>
 #include <asm/bitops.h>
 #include <asm/gg2.h>
+#include <asm/cache.h>
+#ifdef CONFIG_8xx
+#include <asm/8xx_immap.h>
+#include <asm/mbx.h>
+#endif
 
 #undef SHOW_IRQ
 
 unsigned lost_interrupts = 0;
 unsigned int local_irq_count[NR_CPUS];
-static struct irqaction irq_action[NR_IRQS];
+static struct irqaction *irq_action[NR_IRQS];
 static int spurious_interrupts = 0;
+#ifndef CONFIG_8xx
 static unsigned int cached_irq_mask = 0xffffffff;
+#else
+static unsigned int cached_irq_mask = 0xffffffff;
+#endif
 static void no_action(int cpl, void *dev_id, struct pt_regs *regs) { }
-spinlock_t irq_controller_lock;
+/*spinlock_t irq_controller_lock = SPIN_LOCK_UNLOCKED;*/
 #ifdef __SMP__
 atomic_t __ppc_bh_counter = ATOMIC_INIT(0);
 #else
 int __ppc_bh_counter = 0;
 #endif
+static volatile unsigned char *gg2_int_ack_special;
+extern volatile unsigned long ipi_count;
 
 #define cached_21	(((char *)(&cached_irq_mask))[3])
 #define cached_A1	(((char *)(&cached_irq_mask))[2])
@@ -61,9 +80,19 @@
 /*
  * These are set to the appropriate functions by init_IRQ()
  */
+#ifndef CONFIG_8xx
 void (*mask_and_ack_irq)(int irq_nr);
 void (*mask_irq)(unsigned int irq_nr);
 void (*unmask_irq)(unsigned int irq_nr);
+#else /* CONFIG_8xx */
+/* init_IRQ() happens too late for the MBX because we initialize the
+ * CPM early and it calls request_irq() before we have these function
+ * pointers initialized.
+ */
+#define mask_and_ack_irq(irq)	mbx_mask_irq(irq)
+#define mask_irq(irq) mbx_mask_irq(irq)
+#define unmask_irq(irq) mbx_unmask_irq(irq)
+#endif /* CONFIG_8xx */
 
 
 /* prep */
@@ -79,9 +108,46 @@
 #define PMAC_IRQ_MASK	(~ld_le32(IRQ_ENABLE))
 
 
+
+/* nasty hack for shared irq's since we need to do kmalloc calls but
+ * can't very very early in the boot when we need to do a request irq.
+ * this needs to be removed.
+ * -- Cort
+ */
+static char cache_bitmask = 0;
+static struct irqaction malloc_cache[4];
+extern int mem_init_done;
+
+void *irq_kmalloc(size_t size, int pri)
+{
+	unsigned int i;
+	if ( mem_init_done )
+		return kmalloc(size,pri);
+	for ( i = 0; i <= 3 ; i++ )
+		if ( ! ( cache_bitmask & (1<<i) ) )
+		{
+			cache_bitmask |= (1<<i);
+			return (void *)(&malloc_cache[i]);
+		}
+	return 0;
+}
+
+void irq_kfree(void *ptr)
+{
+	unsigned int i;
+	for ( i = 0 ; i <= 3 ; i++ )
+		if ( ptr == &malloc_cache[i] )
+		{
+			cache_bitmask &= ~(1<<i);
+			return;
+		}
+	kfree(ptr);
+}
+
+#ifndef CONFIG_8xx	
 void i8259_mask_and_ack_irq(int irq_nr)
 {
-	spin_lock(&irq_controller_lock);
+  /*	spin_lock(&irq_controller_lock);*/
 	cached_irq_mask |= 1 << irq_nr;
 	if (irq_nr > 7) {
 		inb(0xA1);	/* DUMMY */
@@ -96,20 +162,22 @@
 		outb(0x60|irq_nr,0x20); /* specific eoi */
 		  
 	}
-	spin_unlock(&irq_controller_lock);
+	/*	spin_unlock(&irq_controller_lock);*/
 }
 
 void pmac_mask_and_ack_irq(int irq_nr)
 {
 	unsigned long bit = 1UL << irq_nr;
 
-	spin_lock(&irq_controller_lock);
+	/*	spin_lock(&irq_controller_lock);*/
 	cached_irq_mask |= bit;
 	lost_interrupts &= ~bit;
 	out_le32(IRQ_ACK, bit);
 	out_le32(IRQ_ENABLE, ~cached_irq_mask);
 	out_le32(IRQ_ACK, bit);
-	spin_unlock(&irq_controller_lock);
+	/*	spin_unlock(&irq_controller_lock);*/
+	/*if ( irq_controller_lock.lock )
+  panic("irq controller lock still held in mask and ack\n");*/
 }
 
 void chrp_mask_and_ack_irq(int irq_nr)
@@ -188,44 +256,96 @@
 	else
 		openpic_enable_irq(irq_to_openpic(irq_nr));
 }
+#else /* CONFIG_8xx */
+static void mbx_mask_irq(unsigned int irq_nr)
+{
+	cached_irq_mask &= ~(1 << (31-irq_nr));
+	((immap_t *)MBX_IMAP_ADDR)->im_siu_conf.sc_simask =
+							cached_irq_mask;
+}
+
+static void mbx_unmask_irq(unsigned int irq_nr)
+{
+	cached_irq_mask |= (1 << (31-irq_nr));
+	((immap_t *)MBX_IMAP_ADDR)->im_siu_conf.sc_simask =
+							cached_irq_mask;
+}
+#endif /* CONFIG_8xx */
 
 void disable_irq(unsigned int irq_nr)
 {
-	unsigned long flags;
+	/*unsigned long flags;*/
 
-	spin_lock_irqsave(&irq_controller_lock, flags);
+	/*	spin_lock_irqsave(&irq_controller_lock, flags);*/
 	mask_irq(irq_nr);
-	spin_unlock_irqrestore(&irq_controller_lock, flags);
+	/*	spin_unlock_irqrestore(&irq_controller_lock, flags);*/
 	synchronize_irq();
 }
 
 void enable_irq(unsigned int irq_nr)
 {
-	unsigned long flags;
+	/*unsigned long flags;*/
 
-	spin_lock_irqsave(&irq_controller_lock, flags);
+	/*	spin_lock_irqsave(&irq_controller_lock, flags);*/
 	unmask_irq(irq_nr);
-	spin_unlock_irqrestore(&irq_controller_lock, flags);
+	/*	spin_unlock_irqrestore(&irq_controller_lock, flags);*/
 }
 
 int get_irq_list(char *buf)
 {
-	int i, len = 0;
+	int i, len = 0, j;
 	struct irqaction * action;
 
+	len += sprintf(buf+len, "           ");
+	for (j=0; j<smp_num_cpus; j++)
+		len += sprintf(buf+len, "CPU%d       ",j);
+	*(char *)(buf+len++) = '\n';
+
 	for (i = 0 ; i < NR_IRQS ; i++) {
-		action = irq_action + i;
+		action = irq_action[i];
 		if (!action || !action->handler)
 			continue;
-		len += sprintf(buf+len, "%2d: %10u   %s",
-			i, kstat.interrupts[i], action->name);
+		len += sprintf(buf+len, "%3d: ", i);		
+#ifdef __SMP__
+		for (j = 0; j < smp_num_cpus; j++)
+			len += sprintf(buf+len, "%10u ",
+				kstat.irqs[cpu_logical_map(j)][i]);
+#else		
+		len += sprintf(buf+len, "%10u ", kstat_irqs(i));
+#endif /* __SMP__ */
+		switch( _machine )
+		{
+		case _MACH_prep:
+			len += sprintf(buf+len, "    82c59 ");
+			break;
+		case _MACH_Pmac:
+			len += sprintf(buf+len, " PMAC-PIC ");
+			break;
+		case _MACH_chrp:
+			if ( is_8259_irq(i) )
+				len += sprintf(buf+len, "    82c59 ");
+			else
+				len += sprintf(buf+len, "  OpenPIC ");
+			break;
+		}
+
+		len += sprintf(buf+len, "   %s",action->name);
 		for (action=action->next; action; action = action->next) {
 			len += sprintf(buf+len, ", %s", action->name);
 		}
 		len += sprintf(buf+len, "\n");
 	}
-	len += sprintf(buf+len, "99: %10u   spurious or short\n",
-		       spurious_interrupts);
+#ifdef __SMP__
+	/* should this be per processor send/receive? */
+	len += sprintf(buf+len, "IPI: %10lu\n", ipi_count);
+	for ( i = 0 ; i <= smp_num_cpus-1; i++ )
+		len += sprintf(buf+len,"          ");
+	len += sprintf(buf+len, "    interprocessor messages received\n");
+#endif		
+	len += sprintf(buf+len, "BAD: %10u",spurious_interrupts);
+	for ( i = 0 ; i <= smp_num_cpus-1; i++ )
+		len += sprintf(buf+len,"          ");
+	len += sprintf(buf+len, "    spurious or short\n");
 	return len;
 }
 
@@ -394,20 +514,31 @@
 	int status;
 	int openpic_eoi_done = 0;
 
+	/* save the HID0 in case dcache was off - see idle.c
+	 * this hack should leave for a better solution -- Cort */
+	unsigned dcache_locked;
+	
+	dcache_locked = unlock_dcache();	
+	hardirq_enter(cpu);
+#ifndef CONFIG_8xx		  
 #ifdef __SMP__
 	if ( cpu != 0 )
-		panic("cpu %d received interrupt", cpu);
-#endif /* __SMP__ */		
-
-	hardirq_enter(cpu);
+	{
+		if ( !lost_interrupts )
+		{
+			extern smp_message_recv(void);
+			goto out;
+			
+			ipi_count++;
+			smp_message_recv();
+			goto out;
+		}
+		/* could be here due to a do_fake_interrupt call but we don't
+		   mess with the controller from the second cpu -- Cort */
+		goto out;
+	}
+#endif /* __SMP__ */			
 
-	/*
-	 * I'll put this ugly mess of code into a function
-	 * such as get_pending_irq() or some such clear thing
-	 * so we don't have a switch in the irq code and
-	 * the chrp code is merged a bit with the prep.
-	 * -- Cort
-	 */
 	switch ( _machine )
 	{
 	case _MACH_Pmac:
@@ -425,7 +556,7 @@
 			 * 
 			 * This should go in the above mask/ack code soon. -- Cort
 			 */
-			irq = *(volatile unsigned char *)GG2_INT_ACK_SPECIAL;
+			irq = *gg2_int_ack_special;
 			/*
 			 * Acknowledge as soon as possible to allow i8259
 			 * interrupt nesting
@@ -456,7 +587,6 @@
 		}
 		break;
 	case _MACH_prep:
-#if 1
 		outb(0x0C, 0x20);
 		irq = inb(0x20) & 7;
 		if (irq == 2)
@@ -470,23 +600,6 @@
 			irq = (irq&7) + 8;
 		}
 		bits = 1UL << irq;
-#else
-		/*
-		 * get the isr from the intr controller since
-		 * the bit in the irr has been cleared
-		 */
-		outb(0x0a, 0x20);
-		bits = inb(0x20)&0xff;
-		/* handle cascade */
-		if ( bits & 4 )
-		{
-			bits &= ~4UL;
-			outb(0x0a, 0xA0);
-			bits |= inb(0xA0)<<8;
-		}
-		/* ignore masked irqs */
-		bits &= ~cached_irq_mask;
-#endif		
 		break;
 	}
 	
@@ -494,43 +607,62 @@
 		printk("Bogus interrupt from PC = %lx\n", regs->nip);
 		goto out;
 	}
+	
+#else /* CONFIG_8xx */
+	/* For MPC8xx, read the SIVEC register and shift the bits down
+	 * to get the irq number.
+	 */
+	bits = ((immap_t *)MBX_IMAP_ADDR)->im_siu_conf.sc_sivec;
+	irq = bits >> 26;
+#endif /* CONFIG_8xx */
 
 	mask_and_ack_irq(irq);
 
 	status = 0;
-	action = irq_action + irq;
-	kstat.interrupts[irq]++;
-	if (action->handler) {
+	action = irq_action[irq];
+	kstat.irqs[cpu][irq]++;
+	if ( action && action->handler) {
 		if (!(action->flags & SA_INTERRUPT))
 			__sti();
-		status |= action->flags;
-		action->handler(irq, action->dev_id, regs);
-		/*if (status & SA_SAMPLE_RANDOM)
-			add_interrupt_randomness(irq);*/
-		__cli(); /* in case the handler turned them on */
-		spin_lock(&irq_controller_lock);
+		do { 
+			status |= action->flags;
+			action->handler(irq, action->dev_id, regs);
+			/*if (status & SA_SAMPLE_RANDOM)
+				  add_interrupt_randomness(irq);*/
+			action = action->next;
+		} while ( action );
+		__cli();
+		/*		spin_lock(&irq_controller_lock);*/
 		unmask_irq(irq);
-		spin_unlock(&irq_controller_lock);
+		/*		spin_unlock(&irq_controller_lock);*/
 	} else {
+#ifndef CONFIG_8xx	  
 		if ( irq == 7 ) /* i8259 gives us irq 7 on 'short' intrs */
+#endif		  
 			spurious_interrupts++;
 		disable_irq( irq );
 	}
 	
 	/* make sure we don't miss any cascade intrs due to eoi-ing irq 2 */
+#ifndef CONFIG_8xx	
 	if ( is_prep && (irq > 7) )
 		goto retry_cascade;
 	/* do_bottom_half is called if necessary from int_return in head.S */
 out:
 	if (_machine == _MACH_chrp && !openpic_eoi_done)
 		openpic_eoi(0);
+#endif /* CONFIG_8xx */
 	hardirq_exit(cpu);
+	
+	/* restore the HID0 in case dcache was off - see idle.c
+	 * this hack should leave for a better solution -- Cort */
+	lock_dcache(dcache_locked);
 }
 
 int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *),
 	unsigned long irqflags, const char * devname, void *dev_id)
 {
-	struct irqaction * action;
+	struct irqaction *old, **p, *action;
 	unsigned long flags;
 
 #ifdef SHOW_IRQ
@@ -540,49 +672,58 @@
 
 	if (irq >= NR_IRQS)
 		return -EINVAL;
-	action = irq + irq_action;
-	if (action->handler)
-		return -EBUSY;
+
 	if (!handler)
-		return -EINVAL;
+	{
+		/* Free */
+		for (p = irq + irq_action; (action = *p) != NULL; p = &action->next)
+		{
+			/* Found it - now free it */
+			save_flags(flags);
+			cli();
+			*p = action->next;
+			restore_flags(flags);
+			irq_kfree(action);
+			return 0;
+		}
+		return -ENOENT;
+	}
+	
+	action = (struct irqaction *)
+		irq_kmalloc(sizeof(struct irqaction), GFP_KERNEL);
+	if (!action)
+		return -ENOMEM;
 	save_flags(flags);
 	cli();
+	
 	action->handler = handler;
 	action->flags = irqflags;
 	action->mask = 0;
 	action->name = devname;
 	action->dev_id = dev_id;
+	action->next = NULL;
 	enable_irq(irq);
-	restore_flags(flags);
+	p = irq_action + irq;
+	
+	if ((old = *p) != NULL) {
+		/* Can't share interrupts unless both agree to */
+		if (!(old->flags & action->flags & SA_SHIRQ))
+			return -EBUSY;
+		/* add new interrupt at end of irq queue */
+		do {
+			p = &old->next;
+			old = *p;
+		} while (old);
+	}
+	*p = action;
+	
+	restore_flags(flags);	
 	return 0;
 }
 
 void free_irq(unsigned int irq, void *dev_id)
 {
-	struct irqaction * action = irq + irq_action;
-	unsigned long flags;
-
-#ifdef SHOW_IRQ
-	printk("free_irq(): irq %d dev_id %04x\n", irq, dev_id);
-#endif /* SHOW_IRQ */
-
-	if (irq >= NR_IRQS) {
-		printk("Trying to free IRQ%d\n",irq);
-		return;
-	}
-	if (!action->handler) {
-		printk("Trying to free free IRQ%d\n",irq);
-		return;
-	}
-	disable_irq(irq);
-	save_flags(flags);
-	cli();
-	action->handler = NULL;
-	action->flags = 0;
-	action->mask = 0;
-	action->name = NULL;
-	action->dev_id = NULL;
-	restore_flags(flags);
+	request_irq(irq, NULL, 0, NULL, dev_id);
 }
 
 unsigned long probe_irq_on (void)
@@ -595,6 +736,7 @@
 	return 0;
 }
 
+#ifndef CONFIG_8xx 
 __initfunc(static void i8259_init(void))
 {
 	/* init master interrupt controller */
@@ -616,11 +758,17 @@
 		panic("Could not allocate cascade IRQ!");
 	enable_irq(2);  /* Enable cascade interrupt */
 }
+#endif /* CONFIG_8xx */
 
+/* On MBX8xx, the interrupt control (SIEL) was set by EPPC-bug.  External
+ * interrupts can be either edge or level triggered, but there is no
+ * reason for us to change the EPPC-bug values (it would not work if we did).
+ */
 __initfunc(void init_IRQ(void))
 {
 	extern void xmon_irq(int, void *, struct pt_regs *);
 
+#ifndef CONFIG_8xx
 	switch (_machine)
 	{
 	case _MACH_Pmac:
@@ -637,7 +785,8 @@
 		mask_and_ack_irq = chrp_mask_and_ack_irq;
 		mask_irq = chrp_mask_irq;
 		unmask_irq = chrp_unmask_irq;
-		ioremap(GG2_INT_ACK_SPECIAL, 1);
+		gg2_int_ack_special = (volatile unsigned char *)
+			ioremap(GG2_INT_ACK_SPECIAL, 1);
 		openpic_init();
 		i8259_init();
 #ifdef CONFIG_XMON
@@ -653,7 +802,7 @@
 		i8259_init();
 		route_pci_interrupts();
 		/*
-		 * According to the Carolina spec from ibm irq's 0,1,2, and 8
+		 * According to the Carolina spec from ibm irqs 0,1,2, and 8
 		 * must be edge triggered.  Also, the pci intrs must be level
 		 * triggered and _only_ isa intrs can be level sensitive
 		 * which are 3-7,9-12,14-15. 13 is special - it can be level.
@@ -686,5 +835,6 @@
 
 		}
 		break;
-	}
+	}	
+#endif /* CONFIG_8xx */
 }
diff -u --recursive --new-file v2.1.96/linux/arch/ppc/kernel/mbx_pci.c linux/arch/ppc/kernel/mbx_pci.c
--- v2.1.96/linux/arch/ppc/kernel/mbx_pci.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/ppc/kernel/mbx_pci.c	Tue Apr 14 17:33:57 1998
@@ -0,0 +1,254 @@
+/*
+ * MBX pci routines.
+ * The MBX uses the QSpan PCI bridge.  The config address register
+ * is located 0x500 from the base of the bridge control/status registers.
+ * The data register is located at 0x504.
+ * This is a two step operation.  First, the address register is written,
+ * then the data register is read/written as required.
+ * I don't know what to do about interrupts (yet).
+ */
+
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/bios32.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/init.h>
+
+#include <asm/io.h>
+#include <asm/mbx.h>
+
+
+/*
+ * This blows......The MBX uses the Tundra QSpan PCI bridge.  When
+ * reading the configuration space, if something does not respond
+ * the bus times out and we get a machine check interrupt.  So, the
+ * good ol' exception tables come to mind to trap it and return some
+ * value.
+ *
+ * On an error we just return a -1, since that is what the caller wants
+ * returned if nothing is present.  I copied this from __get_user_asm,
+ * with the only difference of returning -1 instead of EFAULT.
+ * There is an associated hack in the machine check trap code.
+ *
+ * The QSPAN is also a big endian device, that is it makes the PCI
+ * look big endian to us.  This presents a problem for the Linux PCI
+ * functions, which assume little endian.  For example, we see the
+ * first 32-bit word like this:
+ *	------------------------
+ *	| Device ID | Vendor ID |
+ *	------------------------
+ * If we read/write as a double word, that's OK.  But in our world,
+ * when read as a word, device ID is at location 0, not location 2 as
+ * the little endian PCI would believe.  We have to switch bits in
+ * the PCI addresses given to us to get the data to/from the correct
+ * byte lanes.
+ *
+ * The QSPAN only supports 4 bits of "slot" in the dev_fn instead of 5.
+ * It always forces the MS bit to zero.  Therefore, dev_fn values
+ * greater than 128 are returned as "no device found" errors.
+ *
+ * The QSPAN can only perform long word (32-bit) configuration cycles.
+ * The "offset" must have the two LS bits set to zero.  Read operations
+ * require we read the entire word and then sort out what should be
+ * returned.  Write operations other than long word require that we
+ * read the long word, update the proper word or byte, then write the
+ * entire long word back.
+ *
+ * PCI Bridge hack.  We assume (correctly) that bus 0 is the primary
+ * PCI bus from the QSPAN.  If we are called with a bus number other
+ * than zero, we create a Type 1 configuration access that a downstream
+ * PCI bridge will interpret.
+ */
+
+#define __get_mbx_pci_config(x, addr, op)		\
+	__asm__ __volatile__(				\
+		"1:	"op" %0,0(%1)\n"		\
+		"	eieio\n"			\
+		"2:\n"					\
+		".section .fixup,\"ax\"\n"		\
+		"3:	li %0,-1\n"			\
+		"	b 2b\n"				\
+		".section __ex_table,\"a\"\n"		\
+		"	.align 2\n"			\
+		"	.long 1b,3b\n"			\
+		".text"					\
+		: "=r"(x) : "r"(addr))
+
+#define QS_CONFIG_ADDR	((volatile uint *)(PCI_CSR_ADDR + 0x500))
+#define QS_CONFIG_DATA	((volatile uint *)(PCI_CSR_ADDR + 0x504))
+
+#define mk_config_addr(bus, dev, offset) \
+	(((bus)<<16) | ((dev)<<8) | (offset & 0xfc))
+
+#define mk_config_type1(bus, dev, offset) \
+	mk_config_addr(bus, dev, offset) | 1;
+
+int mbx_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn,
+				  unsigned char offset, unsigned char *val)
+{
+	uint	temp;
+	u_char	*cp;
+
+	if ((bus > 7) || (dev_fn > 127)) {
+		*val = 0xff;
+		return PCIBIOS_DEVICE_NOT_FOUND;
+	}
+
+	if (bus == 0)
+		*QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset);
+	else
+		*QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset);
+	__get_mbx_pci_config(temp, QS_CONFIG_DATA, "lwz");
+
+	offset ^= 0x03;
+	cp = ((u_char *)&temp) + (offset & 0x03);
+	*val = *cp;
+	return PCIBIOS_SUCCESSFUL;
+}
+
+int mbx_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn,
+				  unsigned char offset, unsigned short *val)
+{
+	uint	temp;
+	ushort	*sp;
+
+	if ((bus > 7) || (dev_fn > 127)) {
+		*val = 0xffff;
+		return PCIBIOS_DEVICE_NOT_FOUND;
+	}
+
+	if (bus == 0)
+		*QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset);
+	else
+		*QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset);
+	__get_mbx_pci_config(temp, QS_CONFIG_DATA, "lwz");
+	offset ^= 0x02;
+
+	sp = ((ushort *)&temp) + ((offset >> 1) & 1);
+	*val = *sp;
+	return PCIBIOS_SUCCESSFUL;
+}
+
+int mbx_pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn,
+				   unsigned char offset, unsigned int *val)
+{
+	if ((bus > 7) || (dev_fn > 127)) {
+		*val = 0xffffffff;
+		return PCIBIOS_DEVICE_NOT_FOUND;
+	}
+	if (bus == 0)
+		*QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset);
+	else
+		*QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset);
+	__get_mbx_pci_config(*val, QS_CONFIG_DATA, "lwz");
+	return PCIBIOS_SUCCESSFUL;
+}
+
+int mbx_pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn,
+				   unsigned char offset, unsigned char val)
+{
+	uint	temp;
+	u_char	*cp;
+
+	if ((bus > 7) || (dev_fn > 127))
+		return PCIBIOS_DEVICE_NOT_FOUND;
+
+	mbx_pcibios_read_config_dword(bus, dev_fn, offset, &temp);
+
+	offset ^= 0x03;
+	cp = ((u_char *)&temp) + (offset & 0x03);
+	*cp = val;
+
+	if (bus == 0)
+		*QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset);
+	else
+		*QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset);
+	*QS_CONFIG_DATA = temp;
+
+	return PCIBIOS_SUCCESSFUL;
+}
+
+int mbx_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn,
+				   unsigned char offset, unsigned short val)
+{
+	uint	temp;
+	ushort	*sp;
+
+	if ((bus > 7) || (dev_fn > 127))
+		return PCIBIOS_DEVICE_NOT_FOUND;
+
+	mbx_pcibios_read_config_dword(bus, dev_fn, offset, &temp);
+
+	offset ^= 0x02;
+	sp = ((ushort *)&temp) + ((offset >> 1) & 1);
+	*sp = val;
+
+	if (bus == 0)
+		*QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset);
+	else
+		*QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset);
+	*QS_CONFIG_DATA = temp;
+
+	return PCIBIOS_SUCCESSFUL;
+}
+
+int mbx_pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn,
+				    unsigned char offset, unsigned int val)
+{
+	if ((bus > 7) || (dev_fn > 127))
+		return PCIBIOS_DEVICE_NOT_FOUND;
+
+	if (bus == 0)
+		*QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset);
+	else
+		*QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset);
+	*(unsigned int *)QS_CONFIG_DATA = val;
+
+	return PCIBIOS_SUCCESSFUL;
+}
+
+int mbx_pcibios_find_device(unsigned short vendor, unsigned short dev_id,
+			     unsigned short index, unsigned char *bus_ptr,
+			     unsigned char *dev_fn_ptr)
+{
+    int num, devfn;
+    unsigned int x, vendev;
+
+    if (vendor == 0xffff)
+	return PCIBIOS_BAD_VENDOR_ID;
+    vendev = (dev_id << 16) + vendor;
+    num = 0;
+    for (devfn = 0;  devfn < 32;  devfn++) {
+	mbx_pcibios_read_config_dword(0, devfn<<3, PCI_VENDOR_ID, &x);
+	if (x == vendev) {
+	    if (index == num) {
+		*bus_ptr = 0;
+		*dev_fn_ptr = devfn<<3;
+		return PCIBIOS_SUCCESSFUL;
+	    }
+	    ++num;
+	}
+    }
+    return PCIBIOS_DEVICE_NOT_FOUND;
+}
+
+int mbx_pcibios_find_class(unsigned int class_code, unsigned short index,
+			    unsigned char *bus_ptr, unsigned char *dev_fn_ptr)
+{
+    int devnr, x, num;
+
+    num = 0;
+    for (devnr = 0;  devnr < 32;  devnr++) {
+	mbx_pcibios_read_config_dword(0, devnr<<3, PCI_CLASS_REVISION, &x);
+	if ((x>>8) == class_code) {
+	    if (index == num) {
+		*bus_ptr = 0;
+		*dev_fn_ptr = devnr<<3;
+		return PCIBIOS_SUCCESSFUL;
+	    }
+	    ++num;
+	}
+    }
+    return PCIBIOS_DEVICE_NOT_FOUND;
+}
diff -u --recursive --new-file v2.1.96/linux/arch/ppc/kernel/mbx_setup.c linux/arch/ppc/kernel/mbx_setup.c
--- v2.1.96/linux/arch/ppc/kernel/mbx_setup.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/ppc/kernel/mbx_setup.c	Tue Apr 14 17:33:57 1998
@@ -0,0 +1,169 @@
+/*
+ *  linux/arch/ppc/kernel/setup.c
+ *
+ *  Copyright (C) 1995  Linus Torvalds
+ *  Adapted from 'alpha' version by Gary Thomas
+ *  Modified by Cort Dougan (cort@cs.nmt.edu)
+ *  Modified for MBX using prep/chrp/pmac functions by Dan (dmalek@jlc.net)
+ */
+
+/*
+ * bootup setup stuff..
+ */
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/stddef.h>
+#include <linux/unistd.h>
+#include <linux/ptrace.h>
+#include <linux/malloc.h>
+#include <linux/user.h>
+#include <linux/a.out.h>
+#include <linux/tty.h>
+#include <linux/major.h>
+#include <linux/interrupt.h>
+#include <linux/reboot.h>
+#include <linux/init.h>
+#include <linux/blk.h>
+#include <linux/ioport.h>
+
+#include <asm/mmu.h>
+#include <asm/processor.h>
+#include <asm/residual.h>
+#include <asm/io.h>
+#include <asm/pgtable.h>
+#include <asm/ide.h>
+#include <asm/mbx.h>
+
+extern unsigned long loops_per_sec;
+
+unsigned long empty_zero_page[1024];
+
+#ifdef CONFIG_BLK_DEV_RAM
+extern int rd_doload;		/* 1 = load ramdisk, 0 = don't load */
+extern int rd_prompt;		/* 1 = prompt for ramdisk, 0 = don't prompt */
+extern int rd_image_start;	/* starting block # of image */
+#endif
+
+extern char saved_command_line[256];
+
+extern unsigned long find_available_memory(void);
+extern void mbx_cpm_reset(uint);
+
+
+void mbx_ide_init_hwif_ports(ide_ioreg_t *p, ide_ioreg_t base, int *irq)
+{
+
+	*p = 0;
+	*irq = 0;
+
+	if (base != 0)		/* Only map the first ATA flash drive */
+		return;
+#ifdef ATA_FLASH
+	base = (unsigned long) ioremap(PCMCIA_MEM_ADDR, 0x200);
+	for (i = 0; i < 8; ++i)
+		*p++ = base++;
+	*p = ++base;		/* Does not matter */
+	if (irq)
+		*irq = 13;
+#endif
+}
+
+int
+mbx_get_cpuinfo(char *buffer)
+{
+	int	pvr = _get_PVR();
+	int	len;
+	char	*model;
+	bd_t	*bp;
+	extern	RESIDUAL res;
+  
+	/* I know the MPC860 is 0x50.  I don't have the book handy
+	 * to check the others.
+	 */
+	if ((pvr>>16) == 0x50)
+		model = "MPC860";
+	else
+		model = "unknown";
+
+#ifdef __SMP__
+#define CD(X)		(cpu_data[n].X)  
+#else
+#define CD(X) (X)
+#define CPUN 0
+#endif
+	bp = (bd_t *)&res;
+
+	len = sprintf(buffer,"processor\t: %d\n"
+		      "cpu\t\t: %s\n"
+		      "revision\t: %d.%d\n"
+		      "clock\t\t: %d MHz\n"
+		      "bus clock\t: %d MHz\n",
+		      CPUN,
+		      model,
+		      MAJOR(pvr), MINOR(pvr),
+		      bp->bi_intfreq / 1000000,
+		      bp->bi_busfreq / 1000000
+		);
+  
+	return len;
+}
+
+__initfunc(void
+mbx_setup_arch(unsigned long * memory_start_p, unsigned long * memory_end_p))
+{
+	int	cpm_page;
+
+	cpm_page = *memory_start_p;
+	*memory_start_p += PAGE_SIZE;
+
+	/* Reset the Communication Processor Module.
+	*/
+	mbx_cpm_reset(cpm_page);
+
+#ifdef notdef
+	ROOT_DEV = to_kdev_t(0x0301); /* hda1 */
+#endif
+	
+#ifdef CONFIG_BLK_DEV_RAM
+#if 0
+	ROOT_DEV = to_kdev_t(0x0200); /* floppy */  
+	rd_prompt = 1;
+	rd_doload = 1;
+	rd_image_start = 0;
+#endif
+	/* initrd_start and size are setup by boot/head.S and kernel/head.S */
+	if ( initrd_start )
+	{
+		if (initrd_end > *memory_end_p)
+		{
+			printk("initrd extends beyond end of memory "
+			       "(0x%08lx > 0x%08lx)\ndisabling initrd\n",
+			       initrd_end,*memory_end_p);
+			initrd_start = 0;
+		}
+	}
+#endif
+
+#ifdef notdef
+	request_region(0x20,0x20,"pic1");
+	request_region(0xa0,0x20,"pic2");
+	request_region(0x00,0x20,"dma1");
+	request_region(0x40,0x20,"timer");
+	request_region(0x80,0x10,"dma page reg");
+	request_region(0xc0,0x20,"dma2");
+#endif
+}
+
+void
+abort(void)
+{
+#ifdef CONFIG_XMON
+	extern void xmon(void *);
+	xmon(0);
+#endif
+	machine_restart(NULL);
+}
diff -u --recursive --new-file v2.1.96/linux/arch/ppc/kernel/misc.S linux/arch/ppc/kernel/misc.S
--- v2.1.96/linux/arch/ppc/kernel/misc.S	Thu Mar 26 15:57:02 1998
+++ linux/arch/ppc/kernel/misc.S	Tue Apr 14 17:33:58 1998
@@ -19,6 +19,7 @@
 #include "ppc_asm.tmpl"
 #include "ppc_defs.h"
 
+#ifndef CONFIG_8xx
 /* This instruction is not implemented on the PPC 601 or 603 */
 #define tlbia \
 	li	r4,128; \
@@ -27,7 +28,7 @@
 0:	tlbie	r4; \
 	addi	r4,r4,0x1000; \
 	bdnz	0b
-	
+#endif	
 	.text
 
 /*
@@ -323,6 +324,18 @@
 	mr	r3,r1		/* Close enough */
 	blr
 
+_GLOBAL(_get_THRM1)
+	mfspr	r3,THRM1
+	blr
+
+_GLOBAL(_set_THRM1)
+	mtspr	THRM1,r3
+	blr
+	
+_GLOBAL(_get_L2CR)
+	mfspr	r3,L2CR
+	blr
+	
 _GLOBAL(_get_PVR)
 	mfspr	r3,PVR
 	blr
@@ -348,33 +361,6 @@
 	stfs	0,0(r4)
 	blr
 
-
-_GLOBAL(lock_dcache)
-	mfspr	r3,PVR		/* nop on 601 */
-	rlwinm	r3,r3,16,16,31
-	cmpwi	0,r3,1
-	beqlr-
-	mfspr	r3,HID0
-	ori	r3,r3,HID0_DLOCK
-	mtspr	HID0,r3
-	sync
-	isync
-	blr
-
-_GLOBAL(unlock_dcache)
-	mfspr	r3,PVR		/* nop on 601 */
-	rlwinm	r3,r3,16,16,31
-	cmpwi	0,r3,1
-	beqlr-
-	mfspr	r3,HID0
-	li	r4,HID0_DLOCK
-	andc	r3,r3,r4
-	mtspr	HID0,r3
-	sync
-	isync
-	blr
-	
-
 /*
  * Create a kernel thread
  *   __kernel_thread(flags, fn, arg)
@@ -386,6 +372,16 @@
 	bnelr			/* return if parent */
 	mtlr	r4              /* fn addr in lr */
 	mr	r3,r5	        /* load arg and call fn */
+#if 0/*def __SMP__*/
+	/* drop scheduler_lock since schedule() called us */
+	lis	r4,scheduler_lock@ha
+	li	r5,0
+	stw	r5,scheduler_lock@l+4(r4)	/* owner_pc */
+	stw	r5,scheduler_lock@l+8(r4)	/* owner_cpu */
+	stw	r5,scheduler_lock@l(r4)
+	sync
+	isync
+#endif /* __SMP__ */
 	blrl
 	li	r0,__NR_exit	/* exit after child exits */
         li	r3,0
@@ -413,7 +409,9 @@
 SYSCALL(open)
 SYSCALL(close)
 SYSCALL(waitpid)
+SYSCALL(fork)
 SYSCALL(delete_module)
+SYSCALL(_exit)
 
 
 /* Why isn't this a) automatic, b) written in 'C'? */	
@@ -593,5 +591,7 @@
 	.long sys_setresgid	
 	.long sys_getresgid	/* 170 */
 	.long sys_prctl
-	.space (NR_syscalls-171)*4
+	.long sys_xstat
+	.long sys_xmknod
+	.space (NR_syscalls-173)*4
 
diff -u --recursive --new-file v2.1.96/linux/arch/ppc/kernel/mk_defs.c linux/arch/ppc/kernel/mk_defs.c
--- v2.1.96/linux/arch/ppc/kernel/mk_defs.c	Mon Jan 12 15:18:13 1998
+++ linux/arch/ppc/kernel/mk_defs.c	Tue Apr 14 17:33:58 1998
@@ -19,6 +19,7 @@
 #include <linux/ptrace.h>
 #include <linux/mman.h>
 #include <linux/mm.h>
+#include <asm/io.h>
 #include <asm/page.h>
 #include <asm/pgtable.h>
 #include <asm/processor.h>
@@ -29,9 +30,11 @@
 void
 main(void)
 {
+	DEFINE(KERNELBASE, KERNELBASE);
 	DEFINE(STATE, offsetof(struct task_struct, state));
 	DEFINE(NEXT_TASK, offsetof(struct task_struct, next_task));
 	DEFINE(COUNTER, offsetof(struct task_struct, counter));
+	DEFINE(PROCESSOR, offsetof(struct task_struct, processor));
 	DEFINE(SIGPENDING, offsetof(struct task_struct, sigpending));
 	DEFINE(TSS, offsetof(struct task_struct, tss));
 	DEFINE(MM, offsetof(struct task_struct, mm));
@@ -45,6 +48,7 @@
 	DEFINE(TASK_FLAGS, offsetof(struct task_struct, flags));
 	DEFINE(TSS_FPR0, offsetof(struct thread_struct, fpr[0]));
 	DEFINE(TSS_FPSCR, offsetof(struct thread_struct, fpscr));
+	DEFINE(TSS_SMP_FORK_RET, offsetof(struct thread_struct, smp_fork_ret));
 	/* Interrupt register frame */
 	DEFINE(TASK_UNION_SIZE, sizeof(union task_union));
 	DEFINE(STACK_FRAME_OVERHEAD, STACK_FRAME_OVERHEAD);
diff -u --recursive --new-file v2.1.96/linux/arch/ppc/kernel/pci-bridge.c linux/arch/ppc/kernel/pci-bridge.c
--- v2.1.96/linux/arch/ppc/kernel/pci-bridge.c	Sat Aug 16 09:51:08 1997
+++ linux/arch/ppc/kernel/pci-bridge.c	Wed Dec 31 16:00:00 1969
@@ -1,428 +0,0 @@
-/*
- * Support for PCI bridges found on Power Macintoshes.
- * At present the "bandit" and "chaos" bridges are supported.
- * Fortunately you access configuration space in the same
- * way with either bridge.
- *
- * Copyright (C) 1997 Paul Mackerras (paulus@cs.anu.edu.au)
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- */
-
-#include <linux/kernel.h>
-#include <linux/pci.h>
-#include <linux/bios32.h>
-#include <linux/delay.h>
-#include <linux/string.h>
-#include <linux/init.h>
-#include <asm/io.h>
-#include <asm/prom.h>
-#include <asm/pci-bridge.h>
-
-struct bridge_data {
-	volatile unsigned int *cfg_addr;
-	volatile unsigned char *cfg_data;
-	void *io_base;
-	int bus_number;
-	int max_bus;
-	struct bridge_data *next;
-	struct device_node *node;
-};
-
-static struct bridge_data **bridges, *bridge_list;
-static int max_bus;
-
-static void add_bridges(struct device_node *dev, unsigned long *mem_ptr);
-
-/*
- * Magic constants for enabling cache coherency in the bandit/PSX bridge.
- */
-#define APPLE_VENDID	0x106b
-#define BANDIT_DEVID	1
-#define BANDIT_REVID	3
-
-#define BANDIT_DEVNUM	11
-#define BANDIT_MAGIC	0x50
-#define BANDIT_COHERENT	0x40
-
-/*
- * For a bandit bridge, turn on cache coherency if necessary.
- * N.B. we can't use pcibios_*_config_* here because bridges[]
- * is not initialized yet.
- */
-static void init_bandit(struct bridge_data *bp)
-{
-	unsigned int vendev, magic;
-	int rev;
-
-	/* read the word at offset 0 in config space for device 11 */
-	out_le32(bp->cfg_addr, (1UL << BANDIT_DEVNUM) + PCI_VENDOR_ID);
-	udelay(2);
-	vendev = in_le32((volatile unsigned int *)bp->cfg_data);
-	if (vendev != (BANDIT_DEVID << 16) + APPLE_VENDID) {
-		printk(KERN_WARNING "bandit isn't? (%x)\n", vendev);
-		return;
-	}
-
-	/* read the revision id */
-	out_le32(bp->cfg_addr, (1UL << BANDIT_DEVNUM) + PCI_REVISION_ID);
-	udelay(2);
-	rev = in_8(bp->cfg_data);
-	if (rev != BANDIT_REVID)
-		printk(KERN_WARNING "Unknown revision %d for bandit at %p\n",
-		       rev, bp->io_base);
-
-	/* read the word at offset 0x50 */
-	out_le32(bp->cfg_addr, (1UL << BANDIT_DEVNUM) + BANDIT_MAGIC);
-	udelay(2);
-	magic = in_le32((volatile unsigned int *)bp->cfg_data);
-	if ((magic & BANDIT_COHERENT) != 0)
-		return;
-	magic |= BANDIT_COHERENT;
-	udelay(2);
-	out_le32((volatile unsigned int *)bp->cfg_data, magic);
-	printk(KERN_INFO "Cache coherency enabled for bandit/PSX at %p\n",
-	       bp->io_base);
-}
-
-unsigned long pmac_find_bridges(unsigned long mem_start, unsigned long mem_end)
-{
-	int bus;
-	struct bridge_data *bridge;
-
-	bridge_list = 0;
-	max_bus = 0;
-	add_bridges(find_devices("bandit"), &mem_start);
-	add_bridges(find_devices("chaos"), &mem_start);
-	bridges = (struct bridge_data **) mem_start;
-	mem_start += (max_bus + 1) * sizeof(struct bridge_data *);
-	memset(bridges, 0, (max_bus + 1) * sizeof(struct bridge_data *));
-	for (bridge = bridge_list; bridge != NULL; bridge = bridge->next)
-		for (bus = bridge->bus_number; bus <= bridge->max_bus; ++bus)
-			bridges[bus] = bridge;
-
-	return mem_start;
-}
-
-static void add_bridges(struct device_node *dev, unsigned long *mem_ptr)
-{
-	int *bus_range;
-	int len;
-	struct bridge_data *bp;
-
-	for (; dev != NULL; dev = dev->next) {
-		if (dev->n_addrs < 1) {
-			printk(KERN_WARNING "Can't use %s: no address\n",
-			       dev->full_name);
-			continue;
-		}
-		bus_range = (int *) get_property(dev, "bus-range", &len);
-		if (bus_range == NULL || len < 2 * sizeof(int)) {
-			printk(KERN_WARNING "Can't get bus-range for %s\n",
-			       dev->full_name);
-			continue;
-		}
-		if (bus_range[1] == bus_range[0])
-			printk(KERN_INFO "PCI bus %d", bus_range[0]);
-		else
-			printk(KERN_INFO "PCI buses %d..%d", bus_range[0],
-			       bus_range[1]);
-		printk(" controlled by %s at %x\n",
-		       dev->name, dev->addrs[0].address);
-		bp = (struct bridge_data *) *mem_ptr;
-		*mem_ptr += sizeof(struct bridge_data);
-		bp->cfg_addr = (volatile unsigned int *)
-			(dev->addrs[0].address + 0x800000);
-		bp->cfg_data = (volatile unsigned char *)
-			(dev->addrs[0].address + 0xc00000);
-		bp->io_base = (void *) dev->addrs[0].address;
-		ioremap(dev->addrs[0].address, 0x800000);
-		bp->bus_number = bus_range[0];
-		bp->max_bus = bus_range[1];
-		bp->next = bridge_list;
-		bp->node = dev;
-		bridge_list = bp;
-		if (bp->max_bus > max_bus)
-			max_bus = bp->max_bus;
-
-		if (strcmp(dev->name, "bandit") == 0)
-			init_bandit(bp);
-	}
-}
-
-void *pci_io_base(unsigned int bus)
-{
-	struct bridge_data *bp;
-
-	if (bus > max_bus || (bp = bridges[bus]) == 0)
-		return 0;
-	return bp->io_base;
-}
-
-int pci_device_loc(struct device_node *dev, unsigned char *bus_ptr,
-		   unsigned char *devfn_ptr)
-{
-	unsigned int *reg;
-	int len;
-
-	reg = (unsigned int *) get_property(dev, "reg", &len);
-	if (reg == 0 || len < 5 * sizeof(unsigned int)) {
-		/* doesn't look like a PCI device */
-		*bus_ptr = 0xff;
-		*devfn_ptr = 0xff;
-		return -1;
-	}
-	*bus_ptr = reg[0] >> 16;
-	*devfn_ptr = reg[0] >> 8;
-	return 0;
-}
-
-int pmac_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn,
-				  unsigned char offset, unsigned char *val)
-{
-	struct bridge_data *bp;
-
-	*val = 0xff;
-	if (bus > max_bus || (bp = bridges[bus]) == 0)
-		return PCIBIOS_DEVICE_NOT_FOUND;
-	if (bus == bp->bus_number) {
-		if (dev_fn < (11 << 3))
-			return PCIBIOS_DEVICE_NOT_FOUND;
-		out_le32(bp->cfg_addr,
-			 (1UL << (dev_fn >> 3)) + ((dev_fn & 7) << 8)
-			 + (offset & ~3));
-	} else {
-		out_le32(bp->cfg_addr, (dev_fn << 8) + (offset & ~3) + 1);
-	}
-	udelay(2);
-	*val = in_8(bp->cfg_data + (offset & 3));
-
-	if (offset == PCI_INTERRUPT_LINE) {
-		/*
-		 * Open Firmware often doesn't initialize this
-		 * register properly, so we find the node and see
-		 * if it has an AAPL,interrupts property.
-		 */
-		struct device_node *node;
-		unsigned int *reg;
-
-		for (node = bp->node->child; node != 0; node = node->sibling) {
-			reg = (unsigned int *) get_property(node, "reg", 0);
-			if (reg == 0 || ((reg[0] >> 8) & 0xff) != dev_fn)
-				continue;
-			/* this is the node, see if it has interrupts */
-			if (node->n_intrs > 0)
-				*val = node->intrs[0];
-			break;
-		}
-	}
-
-	return PCIBIOS_SUCCESSFUL;
-}
-
-int pmac_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn,
-				  unsigned char offset, unsigned short *val)
-{
-	struct bridge_data *bp;
-
-	*val = 0xffff;
-	if (bus > max_bus || (bp = bridges[bus]) == 0 || (offset & 1) != 0)
-		return PCIBIOS_DEVICE_NOT_FOUND;
-	if (bus == bp->bus_number) {
-		if (dev_fn < (11 << 3))
-			return PCIBIOS_DEVICE_NOT_FOUND;
-		out_le32(bp->cfg_addr,
-			 (1UL << (dev_fn >> 3)) + ((dev_fn & 7) << 8)
-			 + (offset & ~3));
-	} else {
-		out_le32(bp->cfg_addr, (dev_fn << 8) + (offset & ~3) + 1);
-	}
-	udelay(2);
-	*val = in_le16((volatile unsigned short *)(bp->cfg_data + (offset & 3)));
-	return PCIBIOS_SUCCESSFUL;
-}
-
-int pmac_pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn,
-				   unsigned char offset, unsigned int *val)
-{
-	struct bridge_data *bp;
-
-	*val = 0xffffffff;
-	if (bus > max_bus || (bp = bridges[bus]) == 0 || (offset & 3) != 0)
-		return PCIBIOS_DEVICE_NOT_FOUND;
-	if (bus == bp->bus_number) {
-		if (dev_fn < (11 << 3))
-			return PCIBIOS_DEVICE_NOT_FOUND;
-		out_le32(bp->cfg_addr,
-			 (1UL << (dev_fn >> 3)) + ((dev_fn & 7) << 8)
-			 + offset);
-	} else {
-		out_le32(bp->cfg_addr, (dev_fn << 8) + offset + 1);
-	}
-	udelay(2);
-	*val = in_le32((volatile unsigned int *)bp->cfg_data);
-	return PCIBIOS_SUCCESSFUL;
-}
-
-int pmac_pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn,
-				   unsigned char offset, unsigned char val)
-{
-	struct bridge_data *bp;
-
-	if (bus > max_bus || (bp = bridges[bus]) == 0)
-		return PCIBIOS_DEVICE_NOT_FOUND;
-	if (bus == bp->bus_number) {
-		if (dev_fn < (11 << 3))
-			return PCIBIOS_DEVICE_NOT_FOUND;
-		out_le32(bp->cfg_addr,
-			 (1UL << (dev_fn >> 3)) + ((dev_fn & 7) << 8)
-			 + (offset & ~3));
-	} else {
-		out_le32(bp->cfg_addr, (dev_fn << 8) + (offset & ~3) + 1);
-	}
-	udelay(2);
-	out_8(bp->cfg_data + (offset & 3), val);
-	return PCIBIOS_SUCCESSFUL;
-}
-
-int pmac_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn,
-				   unsigned char offset, unsigned short val)
-{
-	struct bridge_data *bp;
-
-	if (bus > max_bus || (bp = bridges[bus]) == 0 || (offset & 1) != 0)
-		return PCIBIOS_DEVICE_NOT_FOUND;
-	if (bus == bp->bus_number) {
-		if (dev_fn < (11 << 3))
-			return PCIBIOS_DEVICE_NOT_FOUND;
-		out_le32(bp->cfg_addr,
-			 (1UL << (dev_fn >> 3)) + ((dev_fn & 7) << 8)
-			 + (offset & ~3));
-	} else {
-		out_le32(bp->cfg_addr, (dev_fn << 8) + (offset & ~3) + 1);
-	}
-	udelay(2);
-	out_le16((volatile unsigned short *)(bp->cfg_data + (offset & 3)), val);
-	return PCIBIOS_SUCCESSFUL;
-}
-
-int pmac_pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn,
-				    unsigned char offset, unsigned int val)
-{
-	struct bridge_data *bp;
-
-	if (bus > max_bus || (bp = bridges[bus]) == 0 || (offset & 3) != 0)
-		return PCIBIOS_DEVICE_NOT_FOUND;
-	if (bus == bp->bus_number) {
-		if (dev_fn < (11 << 3))
-			return PCIBIOS_DEVICE_NOT_FOUND;
-		out_le32(bp->cfg_addr,
-			 (1UL << (dev_fn >> 3)) + ((dev_fn & 7) << 8)
-			 + offset);
-	} else {
-		out_le32(bp->cfg_addr, (dev_fn << 8) + offset + 1);
-	}
-	udelay(2);
-	out_le32((volatile unsigned int *)bp->cfg_data, val);
-	return PCIBIOS_SUCCESSFUL;
-}
-
-int pmac_pcibios_find_device(unsigned short vendor, unsigned short dev_id,
-			     unsigned short index, unsigned char *bus_ptr,
-			     unsigned char *dev_fn_ptr)
-{
-	int bus, unit, fn, num, devfn;
-	unsigned int x, vendev;
-	unsigned char h;
-
-	if (vendor == 0xffff)
-		return PCIBIOS_BAD_VENDOR_ID;
-	vendev = (dev_id << 16) + vendor;
-	num = 0;
-	for (bus = 0; bus <= max_bus; ++bus) {
-		if (bridges[bus] == 0)
-			continue;
-		unit = fn = 0;
-		if (bus == bridges[bus]->bus_number)
-			unit = 11;
-		while (unit < 32) {
-			devfn = PCI_DEVFN(unit, fn);
-			if (pcibios_read_config_dword(bus, devfn,
-						      PCI_VENDOR_ID, &x)
-			    == PCIBIOS_SUCCESSFUL && x == vendev) {
-				if (index == num) {
-					*bus_ptr = bus;
-					*dev_fn_ptr = devfn;
-					return PCIBIOS_SUCCESSFUL;
-				}
-				++num;
-			}
-			if (fn != 0) {
-				if (++fn >= 8) {
-					++unit;
-					fn = 0;
-				}
-				continue;
-			}
-			if (pcibios_read_config_byte(bus, devfn,
-						     PCI_HEADER_TYPE, &h)
-			    == PCIBIOS_SUCCESSFUL && (h & 0x80) != 0)
-				++fn;
-			else
-				++unit;
-		}
-	}
-	return PCIBIOS_DEVICE_NOT_FOUND;
-}
-
-int pmac_pcibios_find_class(unsigned int class_code, unsigned short index,
-			    unsigned char *bus_ptr, unsigned char *dev_fn_ptr)
-{
-	int bus, unit, fn, num, devfn;
-	unsigned int x;
-	unsigned char h;
-
-	num = 0;
-	for (bus = 0; bus <= max_bus; ++bus) {
-		if (bridges[bus] == 0)
-			continue;
-		unit = fn = 0;
-		if (bus == bridges[bus]->bus_number)
-			unit = 11;
-		while (unit < 32) {
-			devfn = PCI_DEVFN(unit, fn);
-			if (pcibios_read_config_dword(bus, devfn,
-						      PCI_CLASS_REVISION, &x)
-			    == PCIBIOS_SUCCESSFUL && (x >> 8) == class_code) {
-				if (index == num) {
-					*bus_ptr = bus;
-					*dev_fn_ptr = devfn;
-					return PCIBIOS_SUCCESSFUL;
-				}
-				++num;
-			}
-			if (fn != 0) {
-				if (++fn >= 8) {
-					++unit;
-					fn = 0;
-				}
-				continue;
-			}
-			if (pcibios_read_config_byte(bus, devfn,
-						     PCI_HEADER_TYPE, &h)
-			    == PCIBIOS_SUCCESSFUL && (h & 0x80) != 0)
-				++fn;
-			else
-				++unit;
-		}
-	}
-	return PCIBIOS_DEVICE_NOT_FOUND;
-}
-
-__initfunc(unsigned long route_pci_interrupts(void))
-{
-	return 0;
-}
diff -u --recursive --new-file v2.1.96/linux/arch/ppc/kernel/pci.c linux/arch/ppc/kernel/pci.c
--- v2.1.96/linux/arch/ppc/kernel/pci.c	Mon Jan 12 15:18:13 1998
+++ linux/arch/ppc/kernel/pci.c	Tue Apr 14 17:33:58 1998
@@ -1,24 +1,27 @@
 /*
- * $Id: pci.c,v 1.18 1997/10/29 03:35:07 cort Exp $
+ * $Id: pci.c,v 1.24 1998/02/19 21:29:49 cort Exp $
  * Common pmac/prep/chrp pci routines. -- Cort
  */
 
 #include <linux/kernel.h>
 #include <linux/pci.h>
-#include <linux/bios32.h>
 #include <linux/delay.h>
 #include <linux/string.h>
 #include <linux/init.h>
 #include <linux/config.h>
 #include <linux/pci.h>
+#include <linux/openpic.h>
 
 #include <asm/processor.h>
 #include <asm/io.h>
 #include <asm/prom.h>
 #include <asm/pci-bridge.h>
+#include <asm/irq.h>
 
-#if !defined(CONFIG_MACH_SPECIFIC)
+#if !defined(CONFIG_MACH_SPECIFIC) || defined(CONFIG_PMAC)
 unsigned long isa_io_base;
+#endif /* CONFIG_MACH_SPECIFIC || CONFIG_PMAC */
+#if !defined(CONFIG_MACH_SPECIFIC)
 unsigned long isa_mem_base;
 unsigned long pci_dram_offset;
 #endif /* CONFIG_MACH_SPECIFIC */
@@ -121,49 +124,6 @@
 	return 1;
 }
 
-int pcibios_find_device (unsigned short vendor, unsigned short device_id,
-			 unsigned short index, unsigned char *bus,
-			 unsigned char *devfn)
-{
-	unsigned int curr = 0;
-	struct pci_dev *dev;
-	for (dev = pci_devices; dev; dev = dev->next) {
-		if (dev->vendor == vendor && dev->device == device_id) {
-			if (curr == index) {
-				*devfn = dev->devfn;
-				*bus = dev->bus->number;
-				return PCIBIOS_SUCCESSFUL;
-			}
-			++curr;
-		}
-	}
-	return PCIBIOS_DEVICE_NOT_FOUND;
-}
-
-/*
- * Given the class, find the n'th instance of that device
- * in the system.
- */
-int pcibios_find_class (unsigned int class_code, unsigned short index,
-			unsigned char *bus, unsigned char *devfn)
-{
-	unsigned int curr = 0;
-	struct pci_dev *dev;
-
-	for (dev = pci_devices; dev; dev = dev->next) {
-		if (dev->class == class_code) {
-			if (curr == index) {
-				*devfn = dev->devfn;
-				*bus = dev->bus->number;
-				return PCIBIOS_SUCCESSFUL;
-			}
-			++curr;
-		}
-	}
-	return PCIBIOS_DEVICE_NOT_FOUND;
-}
-
-
 __initfunc(unsigned long
 	   pcibios_init(unsigned long mem_start,unsigned long mem_end))
 {
@@ -203,6 +163,70 @@
 
 __initfunc(unsigned long
 	   pcibios_fixup(unsigned long mem_start, unsigned long mem_end))
+
 {
+	extern route_pci_interrupts(void);
+	struct pci_dev *dev;
+	extern struct bridge_data **bridges;
+	extern unsigned char *Motherboard_map;
+	extern unsigned char *Motherboard_routes;
+	
+	/*
+	 * FIXME: This is broken: We should not assign IRQ's to IRQless
+	 *	  devices (look at PCI_INTERRUPT_PIN) and we also should
+	 *	  honor the existence of multi-function devices where
+	 *	  different functions have different interrupt pins. [mj]
+	 */
+	switch (_machine )
+	{
+	case _MACH_prep:
+		route_pci_interrupts();
+		for(dev=pci_devices; dev; dev=dev->next)
+		{
+			unsigned char d = PCI_SLOT(dev->devfn);
+			dev->irq = Motherboard_routes[Motherboard_map[d]];
+		}
+		break;
+	case _MACH_chrp:
+		/* PCI interrupts are controlled by the OpenPIC */
+		for(dev=pci_devices; dev; dev=dev->next)
+			if (dev->irq)
+				dev->irq = openpic_to_irq(dev->irq);
+		break;
+	case _MACH_Pmac:
+		for(dev=pci_devices; dev; dev=dev->next)
+		{
+			/*
+			 * Open Firmware often doesn't initialize the,
+			 * PCI_INTERRUPT_LINE config register properly, so we
+			 * should find the device node and se if it has an
+			 * AAPL,interrupts property.
+			 */
+			struct bridge_data *bp = bridges[dev->bus->number];
+			struct device_node *node;
+			unsigned int *reg;
+			unsigned char pin;
+			
+			if (pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin) ||
+			    !pin)
+				continue;	/* No interrupt generated -> no fixup */
+			for (node = bp->node->child; node != 0;
+			     node = node->sibling) {
+				reg = (unsigned int *) get_property(node, "reg", 0);
+				if (reg == 0 || ((reg[0] >> 8) & 0xff) != dev->devfn)
+					continue;
+				/* this is the node, see if it has interrupts */
+				if (node->n_intrs > 0)
+					dev->irq = node->intrs[0].line;
+				break;
+			}
+		}
+		break;
+	}
 	return mem_start;
+}
+
+__initfunc(char *pcibios_setup(char *str))
+{
+	return str;
 }
diff -u --recursive --new-file v2.1.96/linux/arch/ppc/kernel/pmac_pci.c linux/arch/ppc/kernel/pmac_pci.c
--- v2.1.96/linux/arch/ppc/kernel/pmac_pci.c	Mon Jan 12 15:18:13 1998
+++ linux/arch/ppc/kernel/pmac_pci.c	Tue Apr 14 17:33:58 1998
@@ -14,25 +14,15 @@
 
 #include <linux/kernel.h>
 #include <linux/pci.h>
-#include <linux/bios32.h>
 #include <linux/delay.h>
 #include <linux/string.h>
 #include <linux/init.h>
 #include <asm/io.h>
+#include <asm/pgtable.h>
 #include <asm/prom.h>
 #include <asm/pci-bridge.h>
 
-struct bridge_data {
-	volatile unsigned int *cfg_addr;
-	volatile unsigned char *cfg_data;
-	void *io_base;
-	int bus_number;
-	int max_bus;
-	struct bridge_data *next;
-	struct device_node *node;
-};
-
-static struct bridge_data **bridges, *bridge_list;
+struct bridge_data **bridges, *bridge_list;
 static int max_bus;
 
 static void add_bridges(struct device_node *dev, unsigned long *mem_ptr);
@@ -112,9 +102,11 @@
 	int *bus_range;
 	int len;
 	struct bridge_data *bp;
+	struct reg_property *addr;
 
 	for (; dev != NULL; dev = dev->next) {
-		if (dev->n_addrs < 1) {
+		addr = (struct reg_property *) get_property(dev, "reg", &len);
+		if (addr == NULL || len < sizeof(*addr)) {
 			printk(KERN_WARNING "Can't use %s: no address\n",
 			       dev->full_name);
 			continue;
@@ -130,15 +122,18 @@
 		else
 			printk(KERN_INFO "PCI buses %d..%d", bus_range[0],
 			       bus_range[1]);
-		printk(" controlled by %s at %x\n",
-		       dev->name, dev->addrs[0].address);
+		printk(" controlled by %s at %x\n", dev->name, addr->address);
 		bp = (struct bridge_data *) *mem_ptr;
 		*mem_ptr += sizeof(struct bridge_data);
 		bp->cfg_addr = (volatile unsigned int *)
-			ioremap(dev->addrs[0].address + 0x800000, 0x1000);
+			ioremap(addr->address + 0x800000, 0x1000);
 		bp->cfg_data = (volatile unsigned char *)
-			ioremap(dev->addrs[0].address + 0xc00000, 0x1000);
-		bp->io_base = (void *) ioremap(dev->addrs[0].address, 0x10000);
+			ioremap(addr->address + 0xc00000, 0x1000);
+		bp->io_base = (void *) ioremap(addr->address, 0x10000);
+#ifdef CONFIG_PMAC
+		if (isa_io_base == 0)
+			isa_io_base = (unsigned long) bp->io_base;
+#endif
 		bp->bus_number = bus_range[0];
 		bp->max_bus = bus_range[1];
 		bp->next = bridge_list;
@@ -180,7 +175,7 @@
 }
 
 int pmac_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn,
-			     unsigned char offset, unsigned char *val)
+				  unsigned char offset, unsigned char *val)
 {
 	struct bridge_data *bp;
 
@@ -198,32 +193,11 @@
 	}
 	udelay(2);
 	*val = in_8(bp->cfg_data + (offset & 3));
-
-	if (offset == PCI_INTERRUPT_LINE) {
-		/*
-		 * Open Firmware often doesn't initialize this
-		 * register properly, so we find the node and see
-		 * if it has an AAPL,interrupts property.
-		 */
-		struct device_node *node;
-		unsigned int *reg;
-
-		for (node = bp->node->child; node != 0; node = node->sibling) {
-			reg = (unsigned int *) get_property(node, "reg", 0);
-			if (reg == 0 || ((reg[0] >> 8) & 0xff) != dev_fn)
-				continue;
-			/* this is the node, see if it has interrupts */
-			if (node->n_intrs > 0)
-				*val = node->intrs[0];
-			break;
-		}
-	}
-
 	return PCIBIOS_SUCCESSFUL;
 }
 
 int pmac_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn,
-			     unsigned char offset, unsigned short *val)
+				  unsigned char offset, unsigned short *val)
 {
 	struct bridge_data *bp;
 
@@ -245,7 +219,7 @@
 }
 
 int pmac_pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn,
-			      unsigned char offset, unsigned int *val)
+				   unsigned char offset, unsigned int *val)
 {
 	struct bridge_data *bp;
 
@@ -267,7 +241,7 @@
 }
 
 int pmac_pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn,
-			      unsigned char offset, unsigned char val)
+				   unsigned char offset, unsigned char val)
 {
 	struct bridge_data *bp;
 
@@ -288,7 +262,7 @@
 }
 
 int pmac_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn,
-			      unsigned char offset, unsigned short val)
+				   unsigned char offset, unsigned short val)
 {
 	struct bridge_data *bp;
 
@@ -309,7 +283,7 @@
 }
 
 int pmac_pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn,
-			       unsigned char offset, unsigned int val)
+				    unsigned char offset, unsigned int val)
 {
 	struct bridge_data *bp;
 
diff -u --recursive --new-file v2.1.96/linux/arch/ppc/kernel/pmac_setup.c linux/arch/ppc/kernel/pmac_setup.c
--- v2.1.96/linux/arch/ppc/kernel/pmac_setup.c	Mon Jan 12 15:18:13 1998
+++ linux/arch/ppc/kernel/pmac_setup.c	Tue Apr 14 17:33:58 1998
@@ -47,23 +47,18 @@
 #include <asm/ide.h>
 #include <asm/pci-bridge.h>
 #include <asm/adb.h>
+#include <asm/mediabay.h>
+#include <asm/ohare.h>
+#include <asm/mediabay.h>
 #include "time.h"
 
-/*
- * A magic address and value to put into it on machines with the
- * "ohare" I/O controller.  This makes the IDE CD work on Starmaxes.
- * Contributed by Harry Eaton.
- */
-#define OMAGICPLACE	((volatile unsigned *) 0xf3000038)
-#define OMAGICCONT	0xbeff7a
-
 extern int root_mountflags;
 
 unsigned char drive_info;
 
 #define DEFAULT_ROOT_DEVICE 0x0801	/* sda1 - slightly silly choice */
 
-static void gc_init(const char *, int);
+static void ohare_init(void);
 
 void
 pmac_setup_arch(unsigned long *memory_start_p, unsigned long *memory_end_p)
@@ -91,27 +86,44 @@
 			loops_per_sec = 50000000;
 	}
 
+	/* this area has the CPU identification register
+	   and some registers used by smp boards */
+	ioremap(0xf8000000, 0x1000);
+
 	*memory_start_p = pmac_find_bridges(*memory_start_p, *memory_end_p);
-	gc_init("gc", 0);
-	gc_init("ohare", 1);
 
-#ifdef CONFIG_ABSTRACT_CONSOLE
+	ohare_init();
+
+#ifdef CONFIG_FB
 	/* Frame buffer device based console */
 	conswitchp = &fb_con;
 #endif
 }
 
-static void gc_init(const char *name, int isohare)
+static volatile u32 *feature_addr;
+
+static void ohare_init(void)
 {
 	struct device_node *np;
 
-	for (np = find_devices(name); np != NULL; np = np->next) {
-		if (np->n_addrs > 0)
-			ioremap(np->addrs[0].address, np->addrs[0].size);
-		if (isohare) {
-			printk(KERN_INFO "Twiddling the magic ohare bits\n");
-			out_le32(OMAGICPLACE, OMAGICCONT);
-		}
+	np = find_devices("ohare");
+	if (np == 0)
+		return;
+	if (np->next != 0)
+		printk(KERN_WARNING "only using the first ohare\n");
+	if (np->n_addrs == 0) {
+		printk(KERN_ERR "No addresses for %s\n", np->full_name);
+		return;
+	}
+	feature_addr = (volatile u32 *)
+		ioremap(np->addrs[0].address + OHARE_FEATURE_REG, 4);
+
+	if (find_devices("via-pmu") == 0) {
+		printk(KERN_INFO "Twiddling the magic ohare bits\n");
+		out_le32(feature_addr, STARMAX_FEATURES);
+	} else {
+		out_le32(feature_addr, in_le32(feature_addr) | PBOOK_FEATURES);
+		printk(KERN_DEBUG "feature reg = %x\n", in_le32(feature_addr));
 	}
 }
 
@@ -125,10 +137,15 @@
 unsigned long
 powermac_init(unsigned long mem_start, unsigned long mem_end)
 {
-	pmac_nvram_init();
+#ifdef CONFIG_KGDB
+	extern void zs_kgdb_hook(int tty_num);
+	zs_kgdb_hook(0);
+#endif
 	adb_init();
+	pmac_nvram_init();
 	if (_machine == _MACH_Pmac) {
 		pmac_read_rtc_time();
+		media_bay_init();
 	}
 #ifdef CONFIG_PMAC_CONSOLE
 	pmac_find_display();
@@ -175,7 +192,7 @@
 #include "../../../drivers/scsi/sd.h"
 #include "../../../drivers/scsi/hosts.h"
 
-int sd_find_target(void *host, int tgt)
+kdev_t sd_find_target(void *host, int tgt)
 {
     Scsi_Disk *dp;
     int i;
@@ -190,7 +207,7 @@
 
 void find_boot_device(void)
 {
-	int dev;
+	kdev_t dev;
 
 	if (kdev_t_to_nr(ROOT_DEV) != 0)
 		return;
@@ -201,7 +218,7 @@
 	dev = sd_find_target(boot_host, boot_target);
 	if (dev == 0)
 		return;
-	boot_dev = to_kdev_t(dev + boot_part);
+	boot_dev = MKDEV(MAJOR(dev), MINOR(dev) + boot_part);
 #endif
 	/* XXX should cope with booting from IDE also */
 }
@@ -221,39 +238,92 @@
 	}
 }
 
+#ifdef CONFIG_BLK_DEV_IDE
+int pmac_ide_ports_known;
+ide_ioreg_t pmac_ide_regbase[MAX_HWIFS];
+int pmac_ide_irq[MAX_HWIFS];
+
 void pmac_ide_init_hwif_ports(ide_ioreg_t *p, ide_ioreg_t base, int *irq)
 {
-	struct device_node *np;
 	int i;
-	static struct device_node *atas;
-	static int atas_valid;
 
 	*p = 0;
-	*irq = 0;
-	if (!atas_valid) {
-		atas = find_devices("ATA");
-		atas_valid = 1;
-	}
-	for (i = (int)base, np = atas; i > 0 && np != NULL; --i, np = np->next)
-		;
-	if (np == NULL)
+	if (base == 0)
 		return;
-	if (np->n_addrs == 0) {
-		printk("ide: no addresses for device %s\n", np->full_name);
+	if (base == mb_cd_base && !check_media_bay(MB_CD)) {
+		mb_cd_index = -1;
 		return;
 	}
-	if (np->n_intrs == 0) {
-		printk("ide: no intrs for device %s, using 13\n",
-		       np->full_name);
-		*irq = 13;
-	} else {
-		*irq = np->intrs[0];
-	}
-	base = (unsigned long) ioremap(np->addrs[0].address, 0x200);
 	for (i = 0; i < 8; ++i)
 		*p++ = base + i * 0x10;
 	*p = base + 0x160;
+	if (irq != NULL) {
+		*irq = 0;
+		for (i = 0; i < MAX_HWIFS; ++i) {
+			if (base == pmac_ide_regbase[i]) {
+				*irq = pmac_ide_irq[i];
+				break;
+			}
+		}
+	}
+}
+
+void pmac_ide_probe(void)
+{
+	struct device_node *np;
+	int i;
+	struct device_node *atas;
+	struct device_node *p, **pp, *removables, **rp;
+
+	pp = &atas;
+	rp = &removables;
+	p = find_devices("ATA");
+	if (p == NULL)
+		p = find_devices("IDE");
+	/* Move removable devices such as the media-bay CDROM
+	   on the PB3400 to the end of the list. */
+	for (; p != NULL; p = p->next) {
+		if (p->parent && p->parent->name
+		    && strcasecmp(p->parent->name, "media-bay") == 0) {
+			*rp = p;
+			rp = &p->next;
+		} else {
+			*pp = p;
+			pp = &p->next;
+		}
+	}
+	*rp = NULL;
+	*pp = removables;
+
+	for (i = 0, np = atas; i < MAX_HWIFS && np != NULL; np = np->next) {
+		if (np->n_addrs == 0) {
+			printk(KERN_WARNING "ide: no address for device %s\n",
+			       np->full_name);
+			continue;
+		}
+		pmac_ide_regbase[i] = (unsigned long)
+			ioremap(np->addrs[0].address, 0x200);
+		if (np->n_intrs == 0) {
+			printk("ide: no intrs for device %s, using 13\n",
+			       np->full_name);
+			pmac_ide_irq[i] = 13;
+		} else {
+			pmac_ide_irq[i] = np->intrs[0].line;
+		}
+
+		if (np->parent && np->parent->name
+		    && strcasecmp(np->parent->name, "media-bay") == 0) {
+			mb_cd_index = i;
+			mb_cd_base = pmac_ide_regbase[i];
+			mb_cd_irq = pmac_ide_irq[i];
+		}
+
+		++i;
+	}
+
+	pmac_ide_ports_known = 1;
 }
+#endif /* CONFIG_BLK_DEV_IDE */
 
 int
 pmac_get_cpuinfo(char *buffer)
diff -u --recursive --new-file v2.1.96/linux/arch/ppc/kernel/pmac_support.c linux/arch/ppc/kernel/pmac_support.c
--- v2.1.96/linux/arch/ppc/kernel/pmac_support.c	Mon Jan 12 15:18:13 1998
+++ linux/arch/ppc/kernel/pmac_support.c	Tue Apr 14 17:33:58 1998
@@ -7,8 +7,11 @@
 #include <linux/nvram.h>
 #include <asm/ptrace.h>
 #include <asm/io.h>
+#include <asm/pgtable.h>
 #include <asm/system.h>
 #include <asm/prom.h>
+#include <asm/adb.h>
+#include <asm/pmu.h>
 
 /*
  * Read and write the non-volatile RAM on PowerMacs and CHRP machines.
@@ -32,8 +35,7 @@
 	}
 	nvram_naddrs = dp->n_addrs;
 	if (_machine == _MACH_chrp && nvram_naddrs == 1) {
-		/* XXX for now */
-		nvram_data = ioremap(0xf70e0000, NVRAM_SIZE);
+		nvram_data = ioremap(dp->addrs[0].address, dp->addrs[0].size);
 		nvram_mult = 1;
 	} else if (nvram_naddrs == 1) {
 		nvram_data = ioremap(dp->addrs[0].address, dp->addrs[0].size);
@@ -41,6 +43,8 @@
 	} else if (nvram_naddrs == 2) {
 		nvram_addr = ioremap(dp->addrs[0].address, dp->addrs[0].size);
 		nvram_data = ioremap(dp->addrs[1].address, dp->addrs[1].size);
+	} else if (nvram_naddrs == 0 && adb_hardware == ADB_VIAPMU) {
+		nvram_naddrs = -1;
 	} else {
 		printk(KERN_ERR "Don't know how to access NVRAM with %d addresses\n",
 		       nvram_naddrs);
@@ -49,7 +53,16 @@
 
 unsigned char nvram_read_byte(int addr)
 {
+	struct adb_request req;
+
 	switch (nvram_naddrs) {
+	case -1:
+		if (pmu_request(&req, NULL, 3, PMU_READ_NVRAM,
+				(addr >> 8) & 0xff, addr & 0xff))
+			break;
+		while (!req.complete)
+			pmu_poll();
+		return req.reply[1];
 	case 1:
 		return nvram_data[(addr & (NVRAM_SIZE - 1)) * nvram_mult];
 	case 2:
@@ -62,7 +75,16 @@
 
 void nvram_write_byte(unsigned char val, int addr)
 {
+	struct adb_request req;
+
 	switch (nvram_naddrs) {
+	case -1:
+		if (pmu_request(&req, NULL, 4, PMU_WRITE_NVRAM,
+				(addr >> 8) & 0xff, addr & 0xff, val))
+			break;
+		while (!req.complete)
+			pmu_poll();
+		break;
 	case 1:
 		nvram_data[(addr & (NVRAM_SIZE - 1)) * nvram_mult] = val;
 		break;
diff -u --recursive --new-file v2.1.96/linux/arch/ppc/kernel/pmac_time.c linux/arch/ppc/kernel/pmac_time.c
--- v2.1.96/linux/arch/ppc/kernel/pmac_time.c	Mon Jan 12 15:18:13 1998
+++ linux/arch/ppc/kernel/pmac_time.c	Tue Apr 14 17:33:58 1998
@@ -15,8 +15,11 @@
 #include <linux/mm.h>
 #include <asm/adb.h>
 #include <asm/cuda.h>
+#include <asm/pmu.h>
 #include <asm/prom.h>
 #include <asm/system.h>
+#include <asm/io.h>
+#include <asm/pgtable.h>
 
 #include "time.h"
 
@@ -44,7 +47,11 @@
 /* Bits in IFR and IER */
 #define T1_INT		0x40		/* Timer 1 interrupt */
 
-static int via_calibrate_decr(void)
+/*
+ * Calibrate the decrementer register using VIA timer 1.
+ * This is used both on powermacs and CHRP machines.
+ */
+int via_calibrate_decr(void)
 {
 	struct device_node *vias;
 	volatile unsigned char *via;
@@ -54,9 +61,12 @@
 	vias = find_devices("via-cuda");
 	if (vias == 0)
 		vias = find_devices("via-pmu");
+	if (vias == 0)
+		vias = find_devices("via");
 	if (vias == 0 || vias->n_addrs == 0)
 		return 0;
-	via = (volatile unsigned char *) vias->addrs[0].address;
+	via = (volatile unsigned char *)
+		ioremap(vias->addrs[0].address, vias->addrs[0].size);
 
 	/* set timer 1 for continuous interrupts */
 	out_8(&via[ACR], (via[ACR] & ~T1MODE) | T1MODE_CONT);
@@ -123,14 +133,30 @@
 	struct adb_request req;
 
 	/* Get the time from the RTC */
-	cuda_request(&req, NULL, 2, CUDA_PACKET, CUDA_GET_TIME);
-	while (!req.complete)
-		cuda_poll();
-	if (req.reply_len != 7)
-		printk(KERN_ERR "pmac_get_rtc_time: got %d byte reply\n",
-		      req.reply_len);
-	return (req.reply[3] << 24) + (req.reply[4] << 16)
-		+ (req.reply[5] << 8) + req.reply[6] - RTC_OFFSET;
+	switch (adb_hardware) {
+	case ADB_VIACUDA:
+		if (cuda_request(&req, NULL, 2, CUDA_PACKET, CUDA_GET_TIME) < 0)
+			return 0;
+		while (!req.complete)
+			cuda_poll();
+		if (req.reply_len != 7)
+			printk(KERN_ERR "pmac_get_rtc_time: got %d byte reply\n",
+			       req.reply_len);
+		return (req.reply[3] << 24) + (req.reply[4] << 16)
+			+ (req.reply[5] << 8) + req.reply[6] - RTC_OFFSET;
+	case ADB_VIAPMU:
+		if (pmu_request(&req, NULL, 1, PMU_READ_RTC) < 0)
+			return 0;
+		while (!req.complete)
+			pmu_poll();
+		if (req.reply_len != 5)
+			printk(KERN_ERR "pmac_get_rtc_time: got %d byte reply\n",
+			       req.reply_len);
+		return (req.reply[1] << 24) + (req.reply[2] << 16)
+			+ (req.reply[3] << 8) + req.reply[4] - RTC_OFFSET;
+	default:
+		return 0;
+	}
 }
 
 int pmac_set_rtc_time(unsigned long nowtime)
diff -u --recursive --new-file v2.1.96/linux/arch/ppc/kernel/ppc-stub.c linux/arch/ppc/kernel/ppc-stub.c
--- v2.1.96/linux/arch/ppc/kernel/ppc-stub.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/ppc/kernel/ppc-stub.c	Tue Apr 14 17:33:58 1998
@@ -0,0 +1,705 @@
+/* $Id: ppc-stub.c,v 1.2 1998/04/11 17:29:03 geert Exp $
+ * ppc-stub.c:  KGDB support for the Linux kernel.
+ *
+ * adapted from arch/sparc/kernel/sparc-stub.c for the PowerPC
+ * some stuff borrowed from Paul Mackerras' xmon
+ * Copyright (C) 1998 Michael AK Tesch (tesch@cs.wisc.edu)
+ *
+ * Modifications to run under Linux
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ *
+ * This file originally came from the gdb sources, and the
+ * copyright notices have been retained below.
+ */
+
+/****************************************************************************
+
+		THIS SOFTWARE IS NOT COPYRIGHTED
+
+   HP offers the following for use in the public domain.  HP makes no
+   warranty with regard to the software or its performance and the
+   user accepts the software "AS IS" with all faults.
+
+   HP DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED, WITH REGARD
+   TO THIS SOFTWARE INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+   OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+
+****************************************************************************/
+
+/****************************************************************************
+ *  Header: remcom.c,v 1.34 91/03/09 12:29:49 glenne Exp $
+ *
+ *  Module name: remcom.c $
+ *  Revision: 1.34 $
+ *  Date: 91/03/09 12:29:49 $
+ *  Contributor:     Lake Stevens Instrument Division$
+ *
+ *  Description:     low level support for gdb debugger. $
+ *
+ *  Considerations:  only works on target hardware $
+ *
+ *  Written by:      Glenn Engel $
+ *  ModuleState:     Experimental $
+ *
+ *  NOTES:           See Below $
+ *
+ *  Modified for SPARC by Stu Grossman, Cygnus Support.
+ *
+ *  This code has been extensively tested on the Fujitsu SPARClite demo board.
+ *
+ *  To enable debugger support, two things need to happen.  One, a
+ *  call to set_debug_traps() is necessary in order to allow any breakpoints
+ *  or error conditions to be properly intercepted and reported to gdb.
+ *  Two, a breakpoint needs to be generated to begin communication.  This
+ *  is most easily accomplished by a call to breakpoint().  Breakpoint()
+ *  simulates a breakpoint by executing a trap #1.
+ *
+ *************
+ *
+ *    The following gdb commands are supported:
+ *
+ * command          function                               Return value
+ *
+ *    g             return the value of the CPU registers  hex data or ENN
+ *    G             set the value of the CPU registers     OK or ENN
+ *    qOffsets      Get section offsets.  Reply is Text=xxx;Data=yyy;Bss=zzz
+ *
+ *    mAA..AA,LLLL  Read LLLL bytes at address AA..AA      hex data or ENN
+ *    MAA..AA,LLLL: Write LLLL bytes at address AA.AA      OK or ENN
+ *
+ *    c             Resume at current address              SNN   ( signal NN)
+ *    cAA..AA       Continue at address AA..AA             SNN
+ *
+ *    s             Step one instruction                   SNN
+ *    sAA..AA       Step one instruction from AA..AA       SNN
+ *
+ *    k             kill
+ *
+ *    ?             What was the last sigval ?             SNN   (signal NN)
+ *
+ *    bBB..BB	    Set baud rate to BB..BB		   OK or BNN, then sets
+ *							   baud rate
+ *
+ * All commands and responses are sent with a packet which includes a
+ * checksum.  A packet consists of
+ *
+ * $<packet info>#<checksum>.
+ *
+ * where
+ * <packet info> :: <characters representing the command or response>
+ * <checksum>    :: <two hex digits computed as modulo 256 sum of <packetinfo>>
+ *
+ * When a packet is received, it is first acknowledged with either '+' or '-'.
+ * '+' indicates a successful transfer.  '-' indicates a failed transfer.
+ *
+ * Example:
+ *
+ * Host:                  Reply:
+ * $m0,10#2a               +$00010203040506070809101112131415#42
+ *
+ ****************************************************************************/
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+
+#include <asm/system.h>
+#include <asm/signal.h>
+#include <asm/system.h>
+#include <asm/kgdb.h>
+#include <asm/pgtable.h>
+#include <asm/ptrace.h>
+
+void breakinst(void);
+
+/*
+ * BUFMAX defines the maximum number of characters in inbound/outbound buffers
+ * at least NUMREGBYTES*2 are needed for register packets
+ */
+#define BUFMAX 2048
+static char remcomInBuffer[BUFMAX];
+static char remcomOutBuffer[BUFMAX];
+
+static int initialized = 0;
+static int kgdb_active = 0;
+static u_int fault_jmp_buf[100];
+static int kdebug;
+
+static const char hexchars[]="0123456789abcdef";
+
+/* Place where we save old trap entries for restoration - sparc*/
+/* struct tt_entry kgdb_savettable[256]; */
+/* typedef void (*trapfunc_t)(void); */
+
+#if 0
+/* Install an exception handler for kgdb */
+static void exceptionHandler(int tnum, unsigned int *tfunc)
+{
+	/* We are dorking with a live trap table, all irqs off */
+}
+#endif
+
+int
+kgdb_setjmp(long *buf)
+{
+	asm ("mflr 0; stw 0,0(%0);"
+	     "stw 1,4(%0); stw 2,8(%0);"
+	     "mfcr 0; stw 0,12(%0);"
+	     "stmw 13,16(%0)"
+	     : : "r" (buf));
+	/* XXX should save fp regs as well */
+	return 0;
+}
+void
+kgdb_longjmp(long *buf, int val)
+{
+	if (val == 0)
+		val = 1;
+	asm ("lmw 13,16(%0);"
+	     "lwz 0,12(%0); mtcrf 0x38,0;"
+	     "lwz 0,0(%0); lwz 1,4(%0); lwz 2,8(%0);"
+	     "mtlr 0; mr 3,%1"
+	     : : "r" (buf), "r" (val));
+}
+/* Convert ch from a hex digit to an int */
+static int
+hex(unsigned char ch)
+{
+	if (ch >= 'a' && ch <= 'f')
+		return ch-'a'+10;
+	if (ch >= '0' && ch <= '9')
+		return ch-'0';
+	if (ch >= 'A' && ch <= 'F')
+		return ch-'A'+10;
+	return -1;
+}
+
+/* Convert the memory pointed to by mem into hex, placing result in buf.
+ * Return a pointer to the last char put in buf (null), in case of mem fault,
+ * return 0.
+ */
+static unsigned char *
+mem2hex(char *mem, char *buf, int count)
+{
+	unsigned char ch;
+
+	if (kgdb_setjmp((long*)fault_jmp_buf) == 0) {
+		debugger_fault_handler = kgdb_fault_handler;
+		while (count-- > 0) {
+			ch = *mem++;
+			*buf++ = hexchars[ch >> 4];
+			*buf++ = hexchars[ch & 0xf];
+		}
+	} else {
+		/* error condition */
+	}
+	debugger_fault_handler = 0;
+	*buf = 0;
+	return buf;
+}
+
+/* convert the hex array pointed to by buf into binary to be placed in mem
+ * return a pointer to the character AFTER the last byte written.
+*/
+static char *
+hex2mem(char *buf, char *mem, int count)
+{
+	int i;
+	unsigned char ch;
+
+	if (kgdb_setjmp((long*)fault_jmp_buf) == 0) {
+		debugger_fault_handler = kgdb_fault_handler;
+		for (i=0; i<count; i++) {
+			ch = hex(*buf++) << 4;
+			ch |= hex(*buf++);
+			*mem++ = ch;
+		}
+		flush_icache_range((int)mem, (int)mem+count);
+	} else {
+		/* error condition */
+	}
+	debugger_fault_handler = 0;
+	return mem;
+}
+
+/*
+ * While we find nice hex chars, build an int.
+ * Return number of chars processed.
+ */
+static int
+hexToInt(char **ptr, int *intValue)
+{
+	int numChars = 0;
+	int hexValue;
+
+	*intValue = 0;
+
+	if (kgdb_setjmp((long*)fault_jmp_buf) == 0) {
+		debugger_fault_handler = kgdb_fault_handler;
+		while (**ptr) {
+			hexValue = hex(**ptr);
+			if (hexValue < 0)
+				break;
+
+			*intValue = (*intValue << 4) | hexValue;
+			numChars ++;
+
+			(*ptr)++;
+		}
+	} else {
+	     /* error condition */
+	}
+	debugger_fault_handler = 0;
+
+	return (numChars);
+}
+
+/* scan for the sequence $<data>#<checksum>     */
+static void
+getpacket(char *buffer)
+{
+	unsigned char checksum;
+	unsigned char xmitcsum;
+	int i;
+	int count;
+	unsigned char ch;
+
+	do {
+		/* wait around for the start character, ignore all other
+		 * characters */
+		while ((ch = (getDebugChar() & 0x7f)) != '$') ;
+
+		checksum = 0;
+		xmitcsum = -1;
+
+		count = 0;
+
+		/* now, read until a # or end of buffer is found */
+		while (count < BUFMAX) {
+			ch = getDebugChar() & 0x7f;
+			if (ch == '#')
+				break;
+			checksum = checksum + ch;
+			buffer[count] = ch;
+			count = count + 1;
+		}
+
+		if (count >= BUFMAX)
+			continue;
+
+		buffer[count] = 0;
+
+		if (ch == '#') {
+			xmitcsum = hex(getDebugChar() & 0x7f) << 4;
+			xmitcsum |= hex(getDebugChar() & 0x7f);
+			if (checksum != xmitcsum)
+				putDebugChar('-');	/* failed checksum */
+			else {
+				putDebugChar('+'); /* successful transfer */
+				/* if a sequence char is present, reply the ID */
+				if (buffer[2] == ':') {
+					putDebugChar(buffer[0]);
+					putDebugChar(buffer[1]);
+					/* remove sequence chars from buffer */
+					count = strlen(buffer);
+					for (i=3; i <= count; i++)
+						buffer[i-3] = buffer[i];
+				}
+			}
+		}
+	} while (checksum != xmitcsum);
+}
+
+/* send the packet in buffer.  */
+static void putpacket(unsigned char *buffer)
+{
+	unsigned char checksum;
+	int count;
+	unsigned char ch, recv;
+
+	/*  $<packet info>#<checksum>. */
+	do {
+		putDebugChar('$');
+		checksum = 0;
+		count = 0;
+
+		while ((ch = buffer[count])) {
+			putDebugChar(ch);
+			checksum += ch;
+			count += 1;
+		}
+
+		putDebugChar('#');
+		putDebugChar(hexchars[checksum >> 4]);
+		putDebugChar(hexchars[checksum & 0xf]);
+		recv = getDebugChar();
+	} while ((recv & 0x7f) != '+');
+}
+
+static void kgdb_flush_cache_all(void)
+{
+	flush_instruction_cache();
+}
+
+static inline int get_msr()
+{
+	int msr;
+	asm volatile("mfmsr %0" : "=r" (msr):);
+	return msr;
+}
+
+static inline void set_msr(int msr)
+{
+	asm volatile("mfmsr %0" : : "r" (msr));
+}
+
+/* Set up exception handlers for tracing and breakpoints
+ * [could be called kgdb_init()]
+ */
+void set_debug_traps(void)
+{
+#if 0
+	unsigned char c;
+
+	save_and_cli(flags);
+
+	/* In case GDB is started before us, ack any packets (presumably
+	 * "$?#xx") sitting there.
+	 *
+	 * I've found this code causes more problems than it solves,
+	 * so that's why it's commented out.  GDB seems to work fine
+	 * now starting either before or after the kernel   -bwb
+	 */
+
+	while((c = getDebugChar()) != '$');
+	while((c = getDebugChar()) != '#');
+	c = getDebugChar(); /* eat first csum byte */
+	c = getDebugChar(); /* eat second csum byte */
+	putDebugChar('+'); /* ack it */
+#endif
+	debugger = kgdb;
+	debugger_bpt = kgdb_bpt;
+	debugger_sstep = kgdb_sstep;
+	debugger_iabr_match = kgdb_iabr_match;
+	debugger_dabr_match = kgdb_dabr_match;
+
+	kgdb_interruptible(1);
+	initialized = 1;
+}
+
+static void kgdb_fault_handler(struct pt_regs *regs)
+{
+	kgdb_longjmp((long*)fault_jmp_buf, 1);
+}
+
+int kgdb_bpt(struct pt_regs *regs)
+{
+	handle_exception(regs);
+	return 1;
+}
+
+int kgdb_sstep(struct pt_regs *regs)
+{
+	handle_exception(regs);
+	return 1;
+}
+
+void kgdb(struct pt_regs *regs)
+{
+	handle_exception(regs);
+}
+
+int kgdb_iabr_match(struct pt_regs *regs)
+{
+	printk("kgdb doesn't support iabr, what?!?\n");
+	handle_exception(regs);
+	return 1;
+}
+
+int kgdb_dabr_match(struct pt_regs *regs)
+{
+	printk("kgdb doesn't support dabr, what?!?\n");
+	handle_exception(regs);
+	return 1;
+}
+
+/* Convert the SPARC hardware trap type code to a unix signal number. */
+/*
+ * This table contains the mapping between PowerPC hardware trap types, and
+ * signals, which are primarily what GDB understands.
+ */
+static struct hard_trap_info
+{
+	unsigned int tt;		/* Trap type code for powerpc */
+	unsigned char signo;		/* Signal that we map this trap into */
+} hard_trap_info[] = {
+	{ 0x200, SIGSEGV },			/* machine check */
+	{ 0x300, SIGSEGV },			/* address error (store) */
+	{ 0x400, SIGBUS },			/* instruction bus error */
+	{ 0x500, SIGINT },			/* interrupt */
+	{ 0x600, SIGBUS },			/* alingment */
+	{ 0x700, SIGILL },			/* reserved instruction or sumpin' */
+	{ 0x800, SIGFPE },			/* fpu unavail */
+	{ 0x900, SIGALRM },			/* decrementer */
+	{ 0xa00, SIGILL },			/* reserved */
+	{ 0xb00, SIGILL },			/* reserved */
+	{ 0xc00, SIGCHLD },			/* syscall */
+	{ 0xd00, SIGINT },			/* watch */
+	{ 0xe00, SIGFPE },			/* fp assist */
+	{ 0, 0}				/* Must be last */
+};
+
+static int computeSignal(unsigned int tt)
+{
+	struct hard_trap_info *ht;
+
+	for (ht = hard_trap_info; ht->tt && ht->signo; ht++)
+		if (ht->tt == tt)
+			return ht->signo;
+
+	return SIGHUP;         /* default for things we don't know about */
+}
+
+/*
+ * This function does all command processing for interfacing to gdb.
+ */
+static void
+handle_exception (struct pt_regs *regs)
+{
+	int sigval;
+	int addr;
+	int length;
+	char *ptr;
+	unsigned int msr;
+
+	if (debugger_fault_handler) {
+		debugger_fault_handler(regs);
+		panic("kgdb longjump failed!\n");
+	}
+	if (kgdb_active) {
+		printk("interrupt while in kgdb, returning\n");
+		return;
+	}
+	kgdb_active = 1;
+
+	printk("kgdb: entering handle_exception; trap [0x%x]\n",
+	       (unsigned int)regs->trap);
+
+	kgdb_interruptible(0);
+	lock_kernel();
+	msr = get_msr();
+	set_msr(msr & ~MSR_EE);	/* disable interrupts */
+
+	if (regs->nip == (unsigned long)breakinst) {
+		/* Skip over breakpoint trap insn */
+		regs->nip += 4;
+	}
+
+	/* reply to host that an exception has occurred */
+	sigval = computeSignal(regs->trap);
+	ptr = remcomOutBuffer;
+
+	*ptr++ = 'S';
+	*ptr++ = hexchars[sigval >> 4];
+	*ptr++ = hexchars[sigval & 0xf];
+
+	*ptr++ = 0;
+
+	putpacket(remcomOutBuffer);
+
+	/* XXX We may want to add some features dealing with poking the
+	 * XXX page tables, ... (look at sparc-stub.c for more info)
+	 * XXX also required hacking to the gdb sources directly...
+	 */
+
+	while (1) {
+		remcomOutBuffer[0] = 0;
+
+		getpacket(remcomInBuffer);
+		switch (remcomInBuffer[0]) {
+		case '?':               /* report most recent signal */
+			remcomOutBuffer[0] = 'S';
+			remcomOutBuffer[1] = hexchars[sigval >> 4];
+			remcomOutBuffer[2] = hexchars[sigval & 0xf];
+			remcomOutBuffer[3] = 0;
+			break;
+#if 0
+		case 'q': /* this screws up gdb for some reason...*/
+		{
+			extern long _start, sdata, __bss_start;
+
+			ptr = &remcomInBuffer[1];
+			if (strncmp(ptr, "Offsets", 7) != 0)
+				break;
+
+			ptr = remcomOutBuffer;
+			sprintf(ptr, "Text=%8.8x;Data=%8.8x;Bss=%8.8x",
+				&_start, &sdata, &__bss_start);
+			break;
+		}
+#endif
+		case 'd':
+			/* toggle debug flag */
+			kdebug ^= 1;
+			break;
+
+		case 'g':	/* return the value of the CPU registers.
+				 * some of them are non-PowerPC names :(
+				 * they are stored in gdb like:
+				 * struct {
+				 *     u32 gpr[32];
+				 *     f64 fpr[32];
+				 *     u32 pc, ps, cnd, lr; (ps=msr)
+				 *     u32 cnt, xer, mq;
+				 * }
+				 */
+		{
+			int i;
+			ptr = remcomOutBuffer;
+			/* General Purpose Regs */
+			ptr = mem2hex((char *)regs, ptr, 32 * 4);
+			/* Floating Point Regs - FIXME */
+			/*ptr = mem2hex((char *), ptr, 32 * 8);*/
+			for(i=0; i<(32*8*2); i++) { /* 2chars/byte */
+				ptr[i] = '0';
+			}
+			ptr += 32*8*2;
+			/* pc, msr, cr, lr, ctr, xer, (mq is unused) */
+			ptr = mem2hex((char *)&regs->nip, ptr, 4);
+			ptr = mem2hex((char *)&regs->msr, ptr, 4);
+			ptr = mem2hex((char *)&regs->ccr, ptr, 4);
+			ptr = mem2hex((char *)&regs->link, ptr, 4);
+			ptr = mem2hex((char *)&regs->ctr, ptr, 4);
+			ptr = mem2hex((char *)&regs->xer, ptr, 4);
+		}
+			break;
+
+		case 'G':   /* set the value of the CPU registers */
+		{
+			ptr = &remcomInBuffer[1];
+
+			/*
+			 * If the stack pointer has moved, you should pray.
+			 * (cause only god can help you).
+			 */
+
+			/* General Purpose Regs */
+			hex2mem(ptr, (char *)regs, 32 * 4);
+
+			/* Floating Point Regs - FIXME?? */
+			/*ptr = hex2mem(ptr, ??, 32 * 8);*/
+			ptr += 32*8*2;
+
+			/* pc, msr, cr, lr, ctr, xer, (mq is unused) */
+			ptr = hex2mem(ptr, (char *)&regs->nip, 4);
+			ptr = hex2mem(ptr, (char *)&regs->msr, 4);
+			ptr = hex2mem(ptr, (char *)&regs->ccr, 4);
+			ptr = hex2mem(ptr, (char *)&regs->link, 4);
+			ptr = hex2mem(ptr, (char *)&regs->ctr, 4);
+			ptr = hex2mem(ptr, (char *)&regs->xer, 4);
+
+			strcpy(remcomOutBuffer,"OK");
+		}
+			break;
+		case 'H':
+			/* dont do anything, yet, just acknowledge */
+			hexToInt(&ptr, &addr);
+			strcpy(remcomOutBuffer,"OK");
+			break;
+
+		case 'm':	/* mAA..AA,LLLL  Read LLLL bytes at address AA..AA */
+				/* Try to read %x,%x.  */
+
+			ptr = &remcomInBuffer[1];
+
+			if (hexToInt(&ptr, &addr)
+			    && *ptr++ == ','
+			    && hexToInt(&ptr, &length))	{
+				if (mem2hex((char *)addr, remcomOutBuffer,length))
+					break;
+				strcpy (remcomOutBuffer, "E03");
+			} else {
+				strcpy(remcomOutBuffer,"E01");
+			}
+			break;
+
+		case 'M': /* MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK */
+			/* Try to read '%x,%x:'.  */
+
+			ptr = &remcomInBuffer[1];
+
+			if (hexToInt(&ptr, &addr)
+			    && *ptr++ == ','
+			    && hexToInt(&ptr, &length)
+			    && *ptr++ == ':') {
+				if (hex2mem(ptr, (char *)addr, length)) {
+					strcpy(remcomOutBuffer, "OK");
+				} else {
+					strcpy(remcomOutBuffer, "E03");
+				}
+			} else {
+				strcpy(remcomOutBuffer, "E02");
+			}
+			break;
+
+
+		case 'k':    /* kill the program, actually just continue */
+		case 'c':    /* cAA..AA  Continue; address AA..AA optional */
+			/* try to read optional parameter, pc unchanged if no parm */
+
+			ptr = &remcomInBuffer[1];
+			if (hexToInt(&ptr, &addr)) {
+				regs->nip = addr;
+			}
+
+/* Need to flush the instruction cache here, as we may have deposited a
+ * breakpoint, and the icache probably has no way of knowing that a data ref to
+ * some location may have changed something that is in the instruction cache.
+ */
+			kgdb_flush_cache_all();
+			set_msr(msr);
+			kgdb_interruptible(1);
+			unlock_kernel();
+			kgdb_active = 0;
+			return;
+
+		case 's':
+			kgdb_flush_cache_all();
+			regs->msr |= MSR_SE;
+			set_msr(msr | MSR_SE);
+			unlock_kernel();
+			kgdb_active = 0;
+			return;
+
+		case 'r':		/* Reset (if user process..exit ???)*/
+			panic("kgdb reset.");
+			break;
+		}			/* switch */
+		if (remcomOutBuffer[0] && kdebug) {
+			printk("remcomInBuffer: %s\n", remcomInBuffer);
+			printk("remcomOutBuffer: %s\n", remcomOutBuffer);
+		}
+		/* reply to the request */
+		putpacket(remcomOutBuffer);
+	} /* while(1) */
+}
+
+/* This function will generate a breakpoint exception.  It is used at the
+   beginning of a program to sync up with a debugger and can be used
+   otherwise as a quick means to stop program execution and "break" into
+   the debugger. */
+
+void
+breakpoint(void)
+{
+	if (!initialized) {
+		printk("breakpoint() called b4 kgdb init\n");
+		return;
+	}
+
+	asm("	.globl breakinst
+	     breakinst: trap
+            ");
+}
diff -u --recursive --new-file v2.1.96/linux/arch/ppc/kernel/ppc_htab.c linux/arch/ppc/kernel/ppc_htab.c
--- v2.1.96/linux/arch/ppc/kernel/ppc_htab.c	Mon Jan 12 15:18:13 1998
+++ linux/arch/ppc/kernel/ppc_htab.c	Tue Apr 14 17:33:58 1998
@@ -1,5 +1,5 @@
 /*
- * $Id: ppc_htab.c,v 1.16 1997/11/17 18:25:04 cort Exp $
+ * $Id: ppc_htab.c,v 1.17 1998/03/14 07:52:49 cort Exp $
  *
  * PowerPC hash table management proc entry.  Will show information
  * about the current hash table and will allow changes to it.
@@ -88,6 +88,7 @@
 #define PMC1 953
 #define PMC2 954
 
+#ifndef CONFIG_8xx
 char *pmc1_lookup(unsigned long mmcr0)
 {
 	switch ( mmcr0 & (0x7f<<7) )
@@ -123,7 +124,7 @@
 		return "unknown";
 	}
 }	
-
+#endif /* CONFIG_8xx */
 
 /*
  * print some useful info about the hash table.  This function
@@ -133,6 +134,7 @@
 static ssize_t ppc_htab_read(struct file * file, char * buf,
 			     size_t count, loff_t *ppos)
 {
+#ifndef CONFIG_8xx
 	unsigned long mmcr0 = 0, pmc1 = 0, pmc2 = 0;
 	int n = 0, valid;
 	unsigned int kptes = 0, overflow = 0, uptes = 0, zombie_ptes = 0;
@@ -249,6 +251,9 @@
 	copy_to_user(buf, buffer + *ppos, n);
 	*ppos += n;
 	return n;
+#else /* CONFIG_8xx */
+	return 0;
+#endif /* CONFIG_8xx */
 }
 
 /*
@@ -257,6 +262,7 @@
 static ssize_t ppc_htab_write(struct file * file, const char * buffer,
 			      size_t count, loff_t *ppos)
 {
+#ifndef CONFIG_8xx
 	unsigned long tmp;
 	if ( current->uid != 0 )
 		return -EACCES;
@@ -493,6 +499,9 @@
 	reset_SDR1();
 #endif	
 	return count;
+#else /* CONFIG_8xx */
+	return 0;
+#endif /* CONFIG_8xx */
 }
 
 
@@ -512,4 +521,3 @@
 	return(-EINVAL);
     }
 }
-
diff -u --recursive --new-file v2.1.96/linux/arch/ppc/kernel/ppc_ksyms.c linux/arch/ppc/kernel/ppc_ksyms.c
--- v2.1.96/linux/arch/ppc/kernel/ppc_ksyms.c	Tue Mar 17 22:18:14 1998
+++ linux/arch/ppc/kernel/ppc_ksyms.c	Tue Apr 14 17:33:58 1998
@@ -4,9 +4,7 @@
 #include <linux/elfcore.h>
 #include <linux/sched.h>
 #include <linux/string.h>
-#include <linux/bios32.h>
 #include <linux/interrupt.h>
-#include <linux/pci.h>
 
 #include <asm/semaphore.h>
 #include <asm/processor.h>
@@ -18,9 +16,11 @@
 #include <asm/pgtable.h>
 #include <asm/adb.h>
 #include <asm/cuda.h>
+#include <asm/pmu.h>
 #include <asm/prom.h>
 #include <asm/system.h>
 #include <asm/pci-bridge.h>
+#include <asm/irq.h>
 
 extern void transfer_to_handler(void);
 extern void int_return(void);
@@ -49,9 +49,13 @@
 EXPORT_SYMBOL(lost_interrupts);
 EXPORT_SYMBOL(do_lost_interrupts);
 EXPORT_SYMBOL(__ppc_bh_counter);
+EXPORT_SYMBOL(enable_irq);
+EXPORT_SYMBOL(disable_irq);
 
-#if !defined(CONFIG_MACH_SPECIFIC)
+#if !defined(CONFIG_MACH_SPECIFIC) || defined(CONFIG_PMAC)
 EXPORT_SYMBOL(isa_io_base);
+#endif
+#if !defined(CONFIG_MACH_SPECIFIC)
 EXPORT_SYMBOL(pci_dram_offset);
 #endif
 
@@ -114,11 +118,15 @@
 EXPORT_SYMBOL(outl);
 EXPORT_SYMBOL(outsl);*/
 
+EXPORT_SYMBOL(_insb);
+EXPORT_SYMBOL(_outsb);
 EXPORT_SYMBOL(_insw);
 EXPORT_SYMBOL(_outsw);
 EXPORT_SYMBOL(_insl);
 EXPORT_SYMBOL(_outsl);
 EXPORT_SYMBOL(ioremap);
+EXPORT_SYMBOL(__ioremap);
+EXPORT_SYMBOL(iounmap);
 
 EXPORT_SYMBOL(start_thread);
 
@@ -140,6 +148,10 @@
 EXPORT_SYMBOL(adb_register);
 EXPORT_SYMBOL(cuda_request);
 EXPORT_SYMBOL(cuda_send_request);
+EXPORT_SYMBOL(cuda_poll);
+EXPORT_SYMBOL(pmu_request);
+EXPORT_SYMBOL(pmu_send_request);
+EXPORT_SYMBOL(pmu_poll);
 EXPORT_SYMBOL(abort);
 EXPORT_SYMBOL(find_devices);
 EXPORT_SYMBOL(find_type_devices);
@@ -148,7 +160,3 @@
 EXPORT_SYMBOL(pci_io_base);
 EXPORT_SYMBOL(pci_device_loc);
 EXPORT_SYMBOL(note_scsi_host);
-
-#if CONFIG_PCI
-EXPORT_SYMBOL(pci_devices);
-#endif
diff -u --recursive --new-file v2.1.96/linux/arch/ppc/kernel/prep_pci.c linux/arch/ppc/kernel/prep_pci.c
--- v2.1.96/linux/arch/ppc/kernel/prep_pci.c	Mon Feb 23 18:12:02 1998
+++ linux/arch/ppc/kernel/prep_pci.c	Tue Apr 14 17:33:58 1998
@@ -1,5 +1,5 @@
 /*
- * $Id: prep_pci.c,v 1.12 1997/10/29 03:35:08 cort Exp $
+ * $Id: prep_pci.c,v 1.16 1998/02/23 02:47:32 davem Exp $
  * PReP pci functions.
  * Originally by Gary Thomas
  * rewritten and updated by Cort Dougan (cort@cs.nmt.edu)
@@ -8,7 +8,6 @@
  */
 
 #include <linux/types.h>
-#include <linux/bios32.h>
 #include <linux/pci.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
@@ -271,6 +270,12 @@
 #define CAROLINA_IRQ_EDGE_MASK_LO   0x00  /* IRQ's 0-7  */
 #define CAROLINA_IRQ_EDGE_MASK_HI   0xA4  /* IRQ's 8-15 [10,13,15] */
 
+/*
+ * FIXME: This code incorrectly assumes there's only bus #0, breaking all
+ *	  PCI-to-PCI bridges. Also multi-function devices are not supported
+ *	  at all. [mj]
+ */
+
 int
 prep_pcibios_read_config_dword (unsigned char bus,
 			   unsigned char dev, unsigned char offset, unsigned int *val)
@@ -319,16 +324,6 @@
 	unsigned char _val;
 	volatile unsigned char *ptr;
 	dev >>= 3;
-	/* Note: the configuration registers don't always have this right! */
-	if (offset == PCI_INTERRUPT_LINE)
-	{
-		*val = Motherboard_routes[Motherboard_map[dev]];
-/*printk("dev %d map %d route %d on board %d\n",
-  dev,Motherboard_map[dev],
-  Motherboard_routes[Motherboard_map[dev]],
-  *(unsigned char *)(0x80800000 | (1<<dev) | (offset ^ 1)));*/
-		return PCIBIOS_SUCCESSFUL;
-	}
 	if ((bus != 0) || (dev > MAX_DEVNR))
 	{
 		*(unsigned long *)val = (unsigned long) 0xFFFFFFFF;
@@ -406,7 +401,7 @@
 	int i;
 	
 	if ( _prep_type == _PREP_Motorola)
-	{ 
+	{
 		switch (inb(0x800) & 0xF0)
 		{
 		case 0x10: /* MVME16xx */
@@ -430,7 +425,6 @@
 			break;
 		case 0x40: /* PowerStack */
 		default: /* Can't hurt, can it? */
-		  
 			Motherboard_map_name = "Blackhawk (Powerstack)";
 			Motherboard_map = Blackhawk_pci_IRQ_map;
 			Motherboard_routes = Blackhawk_pci_IRQ_routes;
@@ -474,3 +468,4 @@
 	*ibc_pcicon |= 0x20;
 	return 0;
 }
+
diff -u --recursive --new-file v2.1.96/linux/arch/ppc/kernel/prep_setup.c linux/arch/ppc/kernel/prep_setup.c
--- v2.1.96/linux/arch/ppc/kernel/prep_setup.c	Mon Jan 12 15:18:13 1998
+++ linux/arch/ppc/kernel/prep_setup.c	Tue Apr 14 17:33:58 1998
@@ -80,8 +80,7 @@
 {
 	extern char *Motherboard_map_name;
 	extern RESIDUAL res;
-	int i;
-	int len;
+	int len, i;
   
 #ifdef __SMP__
 #define CD(X)		(cpu_data[n].X)  
@@ -93,7 +92,7 @@
 	
 	if ( res.ResidualLength == 0 )
 		return len;
-
+	
 	/* print info about SIMMs */
 	len += sprintf(buffer+len,"simms\t\t: ");
 	for ( i = 0 ; (res.ActualNumMemories) && (i < MAX_MEMS) ; i++ )
@@ -106,6 +105,7 @@
 	}
 	len += sprintf(buffer+len,"\n");
 
+#if 0	
 	/* TLB */
 	len += sprintf(buffer+len,"tlb\t\t:");
 	switch(res.VitalProductData.TLBAttrib)
@@ -123,7 +123,6 @@
 		len += sprintf(buffer+len," not present\n");
 		break;
 	}
-
 	/* L1 */
 	len += sprintf(buffer+len,"l1\t\t: ");
 	switch(res.VitalProductData.CacheAttrib)
@@ -144,6 +143,7 @@
 		len += sprintf(buffer+len,"not present\n");
 		break;
 	}
+#endif
 
 	/* L2 */
 	if ( (inb(IBM_EQUIP_PRESENT) & 1) == 0) /* l2 present */
@@ -201,7 +201,11 @@
 		}
 	}
 #endif
-
+	/* make the serial port the console */
+	/* strcat(cmd_line,"console=ttyS0,9600n8"); */
+	/* use the normal console but send output to the serial port, too */
+	/*strcat(cmd_line,"console=tty0 console=ttyS0,9600n8");*/
+        sprintf(cmd_line,"%s console=tty0 console=ttyS0,9600n8", cmd_line);
 	printk("Boot arguments: %s\n", cmd_line);
 	
 #ifdef CONFIG_CS4232
@@ -255,10 +259,6 @@
 #ifdef CONFIG_ABSTRACT_CONSOLE
 #ifdef CONFIG_VGA_CONSOLE
         conswitchp = &vga_con;
-#endif
-#ifdef CONFIG_FB
-	/* Frame buffer device based console */
-	conswitchp = &fb_con;
 #endif
 #endif
 }
diff -u --recursive --new-file v2.1.96/linux/arch/ppc/kernel/prep_time.c linux/arch/ppc/kernel/prep_time.c
--- v2.1.96/linux/arch/ppc/kernel/prep_time.c	Mon Jan 12 15:18:13 1998
+++ linux/arch/ppc/kernel/prep_time.c	Tue Apr 14 17:33:58 1998
@@ -220,7 +220,7 @@
 #ifdef CONFIG_HEARTBEAT
 	/* use hard disk LED as a heartbeat instead -- much more useful
 	   for debugging -- Cort */
-	switch(kstat.interrupts[0] % 101)
+	switch(kstat_irqs(0) % 101)
 	{
 	/* act like an actual heart beat -- ie thump-thump-pause... */
 	case 0:
diff -u --recursive --new-file v2.1.96/linux/arch/ppc/kernel/process.c linux/arch/ppc/kernel/process.c
--- v2.1.96/linux/arch/ppc/kernel/process.c	Tue Mar 17 22:18:14 1998
+++ linux/arch/ppc/kernel/process.c	Tue Apr 14 17:33:58 1998
@@ -1,4 +1,3 @@
-
 /*
  *  linux/arch/ppc/kernel/process.c
  *
@@ -77,8 +76,13 @@
 int
 dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpregs)
 {
+#ifdef __SMP__
+	if ( regs->msr & MSR_FP )
+		smp_giveup_fpu(current);
+#else
 	if (last_task_used_math == current)
 		giveup_fpu();
+#endif	
 	memcpy(fpregs, &current->tss.fpr[0], sizeof(*fpregs));
 	return 1;
 }
@@ -98,7 +102,7 @@
 		printk("tss.magic bad: %08x\n", tsk->tss.magic);
 	}
 #endif
-	
+
 	if ( !tsk )
 		printk("check_stack(): tsk bad tsk %p\n",tsk);
 	
@@ -157,17 +161,21 @@
 #endif
 
 #ifdef SHOW_TASK_SWITCHES
-	printk("%s/%d -> %s/%d cpu %d\n",
+	printk("%s/%d -> %s/%d NIP %08lx cpu %d sfr %d lock %x\n",
 	       prev->comm,prev->pid,
-	       new->comm,new->pid,new->processor);
+	       new->comm,new->pid,new->tss.regs->nip,new->processor,
+	       new->tss.smp_fork_ret,scheduler_lock.lock);
 #endif
 #ifdef __SMP__
-	/* bad news if last_task_used_math changes processors right now -- Cort */
-	if ( (last_task_used_math == new) &&
-	     (new->processor != new->last_processor) )
-		panic("last_task_used_math switched processors");
+	/* avoid complexity of lazy save/restore of fpu
+	 * by just saving it every time we switch out -- Cort
+	 */
+	if ( prev->tss.regs->msr & MSR_FP )
+		smp_giveup_fpu(prev);
+
 	/* be noisy about processor changes for debugging -- Cort */
-	if ( new->last_processor != new->processor )
+	if ( (new->last_processor != NO_PROC_ID) &&
+	     (new->last_processor != new->processor) )
 		printk("switch_to(): changing cpu's %d -> %d %s/%d\n",
 		       new->last_processor,new->processor,
 		       new->comm,new->pid);
@@ -181,11 +189,6 @@
 	_enable_interrupts(s);
 }
 
-asmlinkage int sys_debug(long a, long b, long c, long d, long e, long f,struct pt_regs *regs)
-{
-	return 0;
-}
-
 void show_regs(struct pt_regs * regs)
 {
 	int i;
@@ -257,12 +260,11 @@
 		     ((unsigned long)p + sizeof(union task_union)
 		      - STACK_FRAME_OVERHEAD)) - 2;
 	*childregs = *regs;
-
 	if ((childregs->msr & MSR_PR) == 0)
 		childregs->gpr[2] = (unsigned long) p;	/* `current' in new task */
 	childregs->gpr[3] = 0;  /* Result from fork() */
 	p->tss.ksp = (unsigned long) childregs - STACK_FRAME_OVERHEAD;
-	p->tss.regs = childregs;
+	p->tss.regs = childregs;	
 	if (usp >= (unsigned long) regs) {
 		/* Stack is in kernel space - must adjust */
 		childregs->gpr[1] = (unsigned long)(childregs + 1);
@@ -271,18 +273,28 @@
 		childregs->gpr[1] = usp;
 	}
 	p->tss.last_syscall = -1;
-
+	  
 	/*
 	 * copy fpu info - assume lazy fpu switch now always
 	 *  -- Cort
 	 */
+#ifdef __SMP__
+	if ( regs->msr & MSR_FP )
+		smp_giveup_fpu(current);
+#else	
 	if ( last_task_used_math == current )
 		giveup_fpu();
+#endif	  
 
 	memcpy(&p->tss.fpr, &current->tss.fpr, sizeof(p->tss.fpr));
 	p->tss.fpscr = current->tss.fpscr;
 	childregs->msr &= ~MSR_FP;
 
+#ifdef __SMP__
+	if ( (p->pid != 0) || !(clone_flags & CLONE_PID) )
+		p->tss.smp_fork_ret = 1;
+	p->last_processor = NO_PROC_ID;
+#endif /* __SMP__ */
 	return 0;
 }
 
@@ -337,20 +349,48 @@
 	shove_aux_table(sp);
 }
 
+asmlinkage int sys_clone(int p1, int p2, int p3, int p4, int p5, int p6,
+			 struct pt_regs *regs)
+{
+	unsigned long clone_flags = p1;
+	int res;
+	lock_kernel();
+	res = do_fork(clone_flags, regs->gpr[1], regs);
+	/*
+	 * only parent returns here, child returns to either
+	 * syscall_ret_1() or kernel_thread()
+	 * -- Cort
+	 */
+#ifdef __SMP__
+	/* When we clone the idle task we keep the same pid but
+	 * the return value of 0 for both causes problems.
+	 * -- Cort
+	 */
+	if ((current->pid == 0) && (current == &init_task))
+		res = 1;
+#endif /* __SMP__ */
+	unlock_kernel();
+	return res;
+}
 
 asmlinkage int sys_fork(int p1, int p2, int p3, int p4, int p5, int p6,
 			struct pt_regs *regs)
 {
-	int ret;
 
+	int res;
 	lock_kernel();
-	ret = do_fork(SIGCHLD, regs->gpr[1], regs);
-#if 0/*def __SMP__*/
-	if ( ret ) /* drop scheduler lock in child */
-		scheduler_lock.lock = 0L;
-#endif /* __SMP__ */	
+	res = do_fork(SIGCHLD, regs->gpr[1], regs);
+	/* only parent returns here */
+#ifdef __SMP__
+	/* When we clone the idle task we keep the same pid but
+	 * the return value of 0 for both causes problems.
+	 * -- Cort
+	 */
+	if ((current->pid == 0) && (current == &init_task))
+		res = 1;
+#endif /* __SMP__ */
 	unlock_kernel();
-	return ret;
+	return res;
 }
 
 asmlinkage int sys_execve(unsigned long a0, unsigned long a1, unsigned long a2,
@@ -372,28 +412,6 @@
 out:
 	unlock_kernel();
 	return error;
-}
-
-asmlinkage int sys_clone(int p1, int p2, int p3, int p4, int p5, int p6,
-			 struct pt_regs *regs)
-{
-	unsigned long clone_flags = p1;
-	int res;
-
-	lock_kernel();
-	res = do_fork(clone_flags, regs->gpr[1], regs);
-#ifdef __SMP__
-	/* When we clone the idle task we keep the same pid but
-	 * the return value of 0 for both causes problems.
-	 * -- Cort
-	 */
-	if ((current->pid == 0) && (current == &init_task))
-		res = 1;
-	if ( 0 /*res*/ ) /* drop scheduler lock in child */
-		scheduler_lock.lock = 0L;
-#endif /* __SMP__ */	
-	unlock_kernel();
-	return res;
 }
 
 void
diff -u --recursive --new-file v2.1.96/linux/arch/ppc/kernel/prom.c linux/arch/ppc/kernel/prom.c
--- v2.1.96/linux/arch/ppc/kernel/prom.c	Mon Jan 12 15:18:13 1998
+++ linux/arch/ppc/kernel/prom.c	Tue Apr 14 17:33:58 1998
@@ -16,6 +16,8 @@
 #include <asm/prom.h>
 #include <asm/page.h>
 #include <asm/processor.h>
+#include <asm/irq.h>
+#include <asm/io.h>
 
 /*
  * Properties whose value is longer than this get excluded from our
@@ -50,6 +52,19 @@
 	unsigned size_lo;
 };
 
+struct isa_reg_property {
+	unsigned space;
+	unsigned address;
+	unsigned size;
+};
+
+typedef unsigned long interpret_func(struct device_node *, unsigned long);
+static interpret_func interpret_pci_props;
+static interpret_func interpret_dbdma_props;
+static interpret_func interpret_isa_props;
+static interpret_func interpret_macio_props;
+static interpret_func interpret_root_props;
+
 char *prom_display_paths[FB_MAX] __initdata = { 0, };
 unsigned int prom_num_displays = 0;
 
@@ -60,19 +75,21 @@
 char *bootpath = 0;
 char *bootdevice = 0;
 
-unsigned int rtas_data = 0;
-unsigned int rtas_entry = 0;
+unsigned int rtas_data = 0;   /* virtual pointer */
+unsigned int rtas_entry = 0;  /* physical pointer */
+unsigned int rtas_size = 0;
+char chunk[PAGE_SIZE*64];
 
 static struct device_node *allnodes = 0;
 
 static void *call_prom(const char *service, int nargs, int nret, ...);
-static void prom_print(const char *msg);
+ void prom_print(const char *msg);
 static void prom_exit(void);
 static unsigned long copy_device_tree(unsigned long, unsigned long);
 static unsigned long inspect_node(phandle, struct device_node *, unsigned long,
 				  unsigned long, struct device_node ***);
 static unsigned long finish_node(struct device_node *, unsigned long,
-				 unsigned long);
+				 interpret_func *);
 static unsigned long check_display(unsigned long);
 static int prom_next_node(phandle *);
 
@@ -119,6 +136,18 @@
 		;
 }
 
+void
+prom_enter(void)
+{
+	struct prom_args args;
+	unsigned long offset = reloc_offset();
+
+	args.service = RELOC("enter");
+	args.nargs = 0;
+	args.nret = 0;
+	RELOC(prom)(&args);
+}
+
 static void *
 call_prom(const char *service, int nargs, int nret, ...)
 {
@@ -140,7 +169,7 @@
 	return prom_args.args[nargs];
 }
 
-static void
+void
 prom_print(const char *msg)
 {
 	const char *p, *q;
@@ -160,6 +189,11 @@
 	}
 }
 
+
+#ifdef CONFIG_ALL_PPC
+unsigned char OF_type[16], OF_model[16];
+#endif
+
 /*
  * We enter here early on, when the Open Firmware prom is still
  * handling exceptions and the MMU hash table for us.
@@ -169,11 +203,14 @@
 {
 	unsigned long mem;
 	ihandle prom_rtas;
-	unsigned int rtas_size;
 	unsigned long offset = reloc_offset();
 	int l;
 	char *p, *d;
 
+	/* check if we're prep, return if we are */
+	if ( *(unsigned long *)(0) == 0xdeadc0de )
+		return;
+	
 	/* First get a handle for the stdout device */
 	RELOC(prom) = pp;
 	RELOC(prom_chosen) = call_prom(RELOC("finddevice"), 1, 1,
@@ -209,27 +246,57 @@
 
 	prom_rtas = call_prom(RELOC("finddevice"), 1, 1, RELOC("/rtas"));
 	if (prom_rtas != (void *) -1) {
-		rtas_size = 0;
+		RELOC(rtas_size) = 0;
 		call_prom(RELOC("getprop"), 4, 1, prom_rtas,
-			  RELOC("rtas-size"), &rtas_size, sizeof(rtas_size));
+			  RELOC("rtas-size"), &RELOC(rtas_size), sizeof(rtas_size));
 		prom_print(RELOC("instantiating rtas..."));
-		if (rtas_size == 0) {
+		if (RELOC(rtas_size) == 0) {
 			RELOC(rtas_data) = 0;
 		} else {
 			mem = (mem + 4095) & -4096; /* round to page bdry */
 			RELOC(rtas_data) = mem - KERNELBASE;
-			mem += rtas_size;
+			mem += RELOC(rtas_size);
+		}
+		prom_rtas = call_prom(RELOC("open"), 1, 1, RELOC("/rtas"));
+		RELOC(rtas_data) = ((ulong)chunk+4095)&-4096;
+		{
+			int i, nargs;
+			struct prom_args prom_args;
+			nargs = 3;
+			prom_args.service = RELOC("call-method");
+			prom_args.nargs = nargs;
+			prom_args.nret = 2;
+			prom_args.args[0] = RELOC("instantiate-rtas");
+			prom_args.args[1] = prom_rtas;
+			prom_args.args[2] = ((void *)RELOC(rtas_data)-KERNELBASE);
+			RELOC(prom)(&prom_args);
+			if (prom_args.args[nargs] != 0)
+				i = 0;
+			else
+				i = (int)prom_args.args[nargs+1];
+			RELOC(rtas_entry) = i;
 		}
-		RELOC(rtas_entry) = (unsigned int)
-			call_prom(RELOC("instantiate-rtas"), 1, 1,
-				  RELOC(rtas_data));
-		if (RELOC(rtas_entry) == -1)
+		if ((RELOC(rtas_entry) == -1) || (RELOC(rtas_entry) == 0))
 			prom_print(RELOC(" failed\n"));
 		else
 			prom_print(RELOC(" done\n"));
 	}
 
 	RELOC(klimit) = (char *) (mem - offset);
+#ifdef CONFIG_ALL_PPC
+	{
+
+		ihandle prom_root;
+
+		RELOC(prom_root) = call_prom(RELOC("finddevice"), 1, 1, RELOC("/"));
+		call_prom(RELOC("getprop"), 4, 1, RELOC(prom_root),
+			  RELOC("device_type"), RELOC(OF_type),
+			  (void *) 16);
+		call_prom(RELOC("getprop"), 4, 1, RELOC(prom_root),
+			  RELOC("model"), RELOC(OF_model),
+			  (void *) 16);
+	}
+#endif
 }
 
 /*
@@ -397,12 +464,18 @@
 	return mem_start;
 }
 
+/*
+ * finish_device_tree is called once things are running normally
+ * (i.e. with text and data mapped to the address they were linked at).
+ * It traverses the device tree and fills in the name, type,
+ * {n_}addrs and {n_}intrs fields of each node.
+ */
 void
 finish_device_tree(void)
 {
 	unsigned long mem = (unsigned long) klimit;
 
-	mem = finish_node(allnodes, mem, 0UL);
+	mem = finish_node(allnodes, mem, NULL);
 	printk(KERN_INFO "device tree used %lu bytes\n",
 	       mem - (unsigned long) allnodes);
 	klimit = (char *) mem;
@@ -410,23 +483,53 @@
 
 static unsigned long
 finish_node(struct device_node *np, unsigned long mem_start,
-	    unsigned long base_address)
+	    interpret_func *ifunc)
 {
-	struct reg_property *rp;
-	struct pci_reg_property *pci_addrs;
-	struct address_range *adr;
 	struct device_node *child;
-	int i, l;
 
 	np->name = get_property(np, "name", 0);
 	np->type = get_property(np, "device_type", 0);
 
-	/* get all the device addresses and interrupts */
-	adr = (struct address_range *) mem_start;
+	/* get the device addresses and interrupts */
+	if (ifunc != NULL)
+		mem_start = ifunc(np, mem_start);
+
+	if (!strcmp(np->name, "device-tree"))
+		ifunc = interpret_root_props;
+	else if (np->type == 0)
+		ifunc = NULL;
+	else if (!strcmp(np->type, "pci") || !strcmp(np->type, "vci"))
+		ifunc = interpret_pci_props;
+	else if (!strcmp(np->type, "dbdma")
+		|| (ifunc == interpret_dbdma_props
+		    && (!strcmp(np->type, "escc")
+			|| !strcmp(np->type, "media-bay"))))
+		ifunc = interpret_dbdma_props;
+	else if (!strcmp(np->type, "mac-io"))
+		ifunc = interpret_macio_props;
+	else if (!strcmp(np->type, "isa"))
+		ifunc = interpret_isa_props;
+	else
+		ifunc = NULL;
+
+	for (child = np->child; child != NULL; child = child->sibling)
+		mem_start = finish_node(child, mem_start, ifunc);
+
+	return mem_start;
+}
+
+static unsigned long
+interpret_pci_props(struct device_node *np, unsigned long mem_start)
+{
+	struct address_range *adr;
+	struct pci_reg_property *pci_addrs;
+	int i, l, *ip;
+
 	pci_addrs = (struct pci_reg_property *)
 		get_property(np, "assigned-addresses", &l);
-	i = 0;
-	if (pci_addrs != 0) {
+	if (pci_addrs != 0 && l >= sizeof(struct pci_reg_property)) {
+		i = 0;
+		adr = (struct address_range *) mem_start;
 		while ((l -= sizeof(struct pci_reg_property)) >= 0) {
 			/* XXX assumes PCI addresses mapped 1-1 to physical */
 			adr[i].space = pci_addrs[i].addr.a_hi;
@@ -434,36 +537,194 @@
 			adr[i].size = pci_addrs[i].size_lo;
 			++i;
 		}
-	} else {
-		rp = (struct reg_property *) get_property(np, "reg", &l);
-		if (rp != 0) {
-			while ((l -= sizeof(struct reg_property)) >= 0) {
-				adr[i].space = 0;
-				adr[i].address = rp[i].address + base_address;
-				adr[i].size = rp[i].size;
-				++i;
-			}
+		np->addrs = adr;
+		np->n_addrs = i;
+		mem_start += i * sizeof(struct address_range);
+	}
+
+	ip = (int *) get_property(np, "AAPL,interrupts", &l);
+	if (ip == 0)
+		ip = (int *) get_property(np, "interrupts", &l);
+	if (ip != 0) {
+		np->intrs = (struct interrupt_info *) mem_start;
+		np->n_intrs = l / sizeof(int);
+		mem_start += np->n_intrs * sizeof(struct interrupt_info);
+		for (i = 0; i < np->n_intrs; ++i) {
+			np->intrs[i].line = *ip++;
+			np->intrs[i].sense = 0;
+		}
+	}
+
+	return mem_start;
+}
+
+static unsigned long
+interpret_dbdma_props(struct device_node *np, unsigned long mem_start)
+{
+	struct reg_property *rp;
+	struct address_range *adr;
+	unsigned long base_address;
+	int i, l, *ip;
+	struct device_node *db;
+
+	base_address = 0;
+	for (db = np->parent; db != NULL; db = db->parent) {
+		if (!strcmp(db->type, "dbdma") && db->n_addrs != 0) {
+			base_address = db->addrs[0].address;
+			break;
 		}
 	}
-	if (i > 0) {
+
+	rp = (struct reg_property *) get_property(np, "reg", &l);
+	if (rp != 0 && l >= sizeof(struct reg_property)) {
+		i = 0;
+		adr = (struct address_range *) mem_start;
+		while ((l -= sizeof(struct reg_property)) >= 0) {
+			adr[i].space = 0;
+			adr[i].address = rp[i].address + base_address;
+			adr[i].size = rp[i].size;
+			++i;
+		}
 		np->addrs = adr;
 		np->n_addrs = i;
 		mem_start += i * sizeof(struct address_range);
 	}
 
-	np->intrs = (int *) get_property(np, "AAPL,interrupts", &l);
-	if (np->intrs == 0)
-		np->intrs = (int *) get_property(np, "interrupts", &l);
-	if (np->intrs != 0)
+	ip = (int *) get_property(np, "AAPL,interrupts", &l);
+	if (ip == 0)
+		ip = (int *) get_property(np, "interrupts", &l);
+	if (ip != 0) {
+		np->intrs = (struct interrupt_info *) mem_start;
 		np->n_intrs = l / sizeof(int);
+		mem_start += np->n_intrs * sizeof(struct interrupt_info);
+		for (i = 0; i < np->n_intrs; ++i) {
+			np->intrs[i].line = *ip++;
+			np->intrs[i].sense = 0;
+		}
+	}
 
-	if (np->type != 0 && np->n_addrs > 0
-	    && (strcmp(np->type, "dbdma") == 0
-		|| strcmp(np->type, "mac-io") == 0))
-		base_address = np->addrs[0].address;
+	return mem_start;
+}
 
-	for (child = np->child; child != NULL; child = child->sibling)
-		mem_start = finish_node(child, mem_start, base_address);
+static unsigned long
+interpret_macio_props(struct device_node *np, unsigned long mem_start)
+{
+	struct reg_property *rp;
+	struct address_range *adr;
+	unsigned long base_address;
+	int i, l, *ip;
+	struct device_node *db;
+
+	base_address = 0;
+	for (db = np->parent; db != NULL; db = db->parent) {
+		if (!strcmp(db->type, "mac-io") && db->n_addrs != 0) {
+			base_address = db->addrs[0].address;
+			break;
+		}
+	}
+
+	rp = (struct reg_property *) get_property(np, "reg", &l);
+	if (rp != 0 && l >= sizeof(struct reg_property)) {
+		i = 0;
+		adr = (struct address_range *) mem_start;
+		while ((l -= sizeof(struct reg_property)) >= 0) {
+			adr[i].space = 0;
+			adr[i].address = rp[i].address + base_address;
+			adr[i].size = rp[i].size;
+			++i;
+		}
+		np->addrs = adr;
+		np->n_addrs = i;
+		mem_start += i * sizeof(struct address_range);
+	}
+
+	ip = (int *) get_property(np, "interrupts", &l);
+	if (ip == 0)
+		ip = (int *) get_property(np, "AAPL,interrupts", &l);
+	if (ip != 0) {
+		np->intrs = (struct interrupt_info *) mem_start;
+		np->n_intrs = l / (2 * sizeof(int));
+		mem_start += np->n_intrs * sizeof(struct interrupt_info);
+		for (i = 0; i < np->n_intrs; ++i) {
+			np->intrs[i].line = openpic_to_irq(*ip++);
+			np->intrs[i].sense = *ip++;
+		}
+	}
+
+	return mem_start;
+}
+
+static unsigned long
+interpret_isa_props(struct device_node *np, unsigned long mem_start)
+{
+	struct isa_reg_property *rp;
+	struct address_range *adr;
+	int i, l, *ip;
+
+	rp = (struct isa_reg_property *) get_property(np, "reg", &l);
+	if (rp != 0 && l >= sizeof(struct isa_reg_property)) {
+		i = 0;
+		adr = (struct address_range *) mem_start;
+		while ((l -= sizeof(struct reg_property)) >= 0) {
+			adr[i].space = rp[i].space;
+			adr[i].address = rp[i].address
+				+ (adr[i].space? 0: _ISA_MEM_BASE);
+			adr[i].size = rp[i].size;
+			++i;
+		}
+		np->addrs = adr;
+		np->n_addrs = i;
+		mem_start += i * sizeof(struct address_range);
+	}
+
+	ip = (int *) get_property(np, "interrupts", &l);
+	if (ip != 0) {
+		np->intrs = (struct interrupt_info *) mem_start;
+		np->n_intrs = l / (2 * sizeof(int));
+		mem_start += np->n_intrs * sizeof(struct interrupt_info);
+		for (i = 0; i < np->n_intrs; ++i) {
+			np->intrs[i].line = *ip++;
+			np->intrs[i].sense = *ip++;
+		}
+	}
+
+	return mem_start;
+}
+
+static unsigned long
+interpret_root_props(struct device_node *np, unsigned long mem_start)
+{
+	struct reg_property *rp;
+	struct address_range *adr;
+	int i, l, *ip;
+
+	rp = (struct reg_property *) get_property(np, "reg", &l);
+	if (rp != 0 && l >= sizeof(struct reg_property)) {
+		i = 0;
+		adr = (struct address_range *) mem_start;
+		while ((l -= sizeof(struct reg_property)) >= 0) {
+			adr[i].space = 0;
+			adr[i].address = rp[i].address;
+			adr[i].size = rp[i].size;
+			++i;
+		}
+		np->addrs = adr;
+		np->n_addrs = i;
+		mem_start += i * sizeof(struct address_range);
+	}
+
+	ip = (int *) get_property(np, "AAPL,interrupts", &l);
+	if (ip == 0)
+		ip = (int *) get_property(np, "interrupts", &l);
+	if (ip != 0) {
+		np->intrs = (struct interrupt_info *) mem_start;
+		np->n_intrs = l / sizeof(int);
+		mem_start += np->n_intrs * sizeof(struct interrupt_info);
+		for (i = 0; i < np->n_intrs; ++i) {
+			np->intrs[i].line = *ip++;
+			np->intrs[i].sense = 0;
+		}
+	}
 
 	return mem_start;
 }
@@ -648,14 +909,14 @@
 		printk(KERN_ERR "No RTAS service called %s\n", service);
 		return -1;
 	}
-	u.words[0] = *tokp;
+	u.words[0] = __pa(*tokp);
 	u.words[1] = nargs;
 	u.words[2] = nret;
 	va_start(list, outputs);
 	for (i = 0; i < nargs; ++i)
 		u.words[i+3] = va_arg(list, unsigned long);
 	va_end(list);
-	enter_rtas(&u);
+	enter_rtas((void *)__pa(&u));
 	if (nret > 1 && outputs != NULL)
 		for (i = 0; i < nret-1; ++i)
 			outputs[i] = u.words[i+nargs+4];
diff -u --recursive --new-file v2.1.96/linux/arch/ppc/kernel/ptrace.c linux/arch/ppc/kernel/ptrace.c
--- v2.1.96/linux/arch/ppc/kernel/ptrace.c	Mon Jan 12 15:18:13 1998
+++ linux/arch/ppc/kernel/ptrace.c	Tue Apr 14 17:33:58 1998
@@ -390,8 +390,14 @@
 				tmp = get_reg(child, addr);
 			}
 			else if (addr >= PT_FPR0 && addr <= PT_FPSCR) {
+#ifdef __SMP__
+				if (child->tss.regs->msr & MSR_FP )
+					smp_giveup_fpu(child);
+#else			  
+			  /* only current can be last task to use math on SMP */
 				if (last_task_used_math == child)
 					giveup_fpu();
+#endif				
 				tmp = ((long *)child->tss.fpr)[addr - PT_FPR0];
 			}
 			else
@@ -423,8 +429,13 @@
 				goto out;
 			}
 			if (addr >= PT_FPR0 && addr < PT_FPR0 + 64) {
+#ifndef __SMP__
+				if (child->tss.regs->msr & MSR_FP )
+					smp_giveup_fpu(child);
+#else	
 				if (last_task_used_math == child)
 					giveup_fpu();
+#endif				
 				((long *)child->tss.fpr)[addr - PT_FPR0] = data;
 				ret = 0;
 				goto out;
diff -u --recursive --new-file v2.1.96/linux/arch/ppc/kernel/residual.c linux/arch/ppc/kernel/residual.c
--- v2.1.96/linux/arch/ppc/kernel/residual.c	Tue Mar 10 10:03:30 1998
+++ linux/arch/ppc/kernel/residual.c	Tue Apr 14 17:33:58 1998
@@ -1,5 +1,5 @@
 /*
- * $Id: residual.c,v 1.5 1997/10/30 21:25:19 cort Exp $
+ * $Id: residual.c,v 1.7 1998/03/08 05:49:20 davem Exp $
  *
  * Code to deal with the PReP residual data.
  *
diff -u --recursive --new-file v2.1.96/linux/arch/ppc/kernel/setup.c linux/arch/ppc/kernel/setup.c
--- v2.1.96/linux/arch/ppc/kernel/setup.c	Mon Jan 12 15:18:13 1998
+++ linux/arch/ppc/kernel/setup.c	Tue Apr 14 17:33:58 1998
@@ -1,5 +1,5 @@
 /*
- * $Id: setup.c,v 1.48 1998/01/01 10:04:44 paulus Exp $
+ * $Id: setup.c,v 1.68 1998/04/07 08:20:33 geert Exp $
  * Common prep/pmac/chrp boot and setup code.
  */
 
@@ -13,10 +13,26 @@
 
 #include <asm/adb.h>
 #include <asm/cuda.h>
+#include <asm/pmu.h>
 #include <asm/residual.h>
 #include <asm/io.h>
 #include <asm/ide.h>
 #include <asm/prom.h>
+#include <asm/processor.h>
+#ifdef CONFIG_MBX
+#include <asm/mbx.h>
+#endif
+/* ifdef APUS specific stuff until the merge is completed. -jskov */
+#ifdef CONFIG_APUS
+#include <asm/bootinfo.h>
+#include <asm/setup.h>
+#include <asm/amigappc.h>
+extern unsigned long m68k_machtype;
+extern void amiga_reset (void);
+extern struct mem_info m68k_ramdisk;
+extern int m68k_parse_bootinfo(const struct bi_record *);
+extern char _end[];
+#endif
 
 extern char cmd_line[512];
 char saved_command_line[256];
@@ -26,13 +42,13 @@
 unsigned long ISA_DMA_THRESHOLD;
 unsigned long DMA_MODE_READ, DMA_MODE_WRITE;
 int _machine;
+/* if we have openfirmware */
+unsigned long have_of;
 #endif /* ! CONFIG_MACH_SPECIFIC */
 
 /* copy of the residual data */
 RESIDUAL res;
 int _prep_type;
-/* if we have openfirmware */
-unsigned long have_of;
 
 /*
  * Perhaps we can put the pmac screen_info[] here
@@ -40,6 +56,7 @@
  * Until we get multiple-console support in here
  * that is.  -- Cort
  */ 
+#ifndef CONFIG_MBX
 #if !defined(CONFIG_PMAC_CONSOLE)
 struct screen_info screen_info = {
 	0, 25,			/* orig-x, orig-y */
@@ -65,29 +82,66 @@
 }
 #endif
 
+#else /* CONFIG_MBX */
+
+/* We need this to satisfy some external references until we can
+ * strip the kernel down.
+ */
+struct screen_info screen_info = {
+	0, 25,			/* orig-x, orig-y */
+	{ 0, 0 },		/* unused */
+	0,			/* orig-video-page */
+	0,			/* orig-video-mode */
+	80,			/* orig-video-cols */
+	0,0,0,			/* ega_ax, ega_bx, ega_cx */
+	25,			/* orig-video-lines */
+	0,			/* orig-video-isVGA */
+	16			/* orig-video-points */
+};
+#endif /* CONFIG_MBX */
+
 /* cmd is ignored for now... */
 void machine_restart(char *cmd)
 {
 	struct adb_request req;
 	unsigned long flags;
 	unsigned long i = 10000;
-#if 0	
+#if 0
 	int err;
 #endif	
 
 	switch(_machine)
 	{
 	case _MACH_Pmac:
-		cuda_request(&req, NULL, 2, CUDA_PACKET, CUDA_RESET_SYSTEM);
-		for (;;)
-			cuda_poll();
+		switch (adb_hardware) {
+		case ADB_VIACUDA:
+			cuda_request(&req, NULL, 2, CUDA_PACKET,
+				     CUDA_RESET_SYSTEM);
+			for (;;)
+				cuda_poll();
+			break;
+		case ADB_VIAPMU:
+			pmu_request(&req, NULL, 1, PMU_RESET);
+			for (;;)
+				pmu_poll();
+			break;
+		default:
+		}
 		break;
+
 	case _MACH_chrp:
 #if 0		/* RTAS doesn't seem to work on Longtrail.
 		   For now, do it the same way as the PReP. */
-		err = call_rtas("system-reboot", 0, 1, NULL);
+	        /*err = call_rtas("system-reboot", 0, 1, NULL);
 		printk("RTAS system-reboot returned %d\n", err);
-		for (;;);
+		for (;;);*/
+		
+		{
+			extern unsigned int rtas_entry, rtas_data, rtas_size;
+			unsigned long status, value;
+			printk("rtas_entry: %08x rtas_data: %08x rtas_size: %08x\n",
+			       rtas_entry,rtas_data,rtas_size);
+	}
 #endif
 	case _MACH_prep:
 		_disable_interrupts();
@@ -104,6 +158,23 @@
 		while ( i != 0 ) i++;
 		panic("restart failed\n");
 		break;
+	case _MACH_apus:
+		cli();
+		/* APUS:FIXME: Reset the system. Apparently there's
+		 * more magic to it than this!?!?
+		 */
+#if 0
+		APUS_WRITE(APUS_REG_SHADOW, REGSHADOW_SELFRESET);
+		APUS_WRITE(APUS_REG_RESET, 
+			   REGRESET_PPCRESET|REGRESET_M68KRESET|
+			   REGRESET_AMIGARESET|REGRESET_AUXRESET|
+			   REGRESET_SCSIRESET);
+#endif
+		printk("\n**************************************\n");
+		printk("*** You can make a hard reset now! ***\n");
+		printk("**************************************\n");
+		for(;;);
+		break;
 	}
 }
 
@@ -116,9 +187,23 @@
 
 	switch (_machine) {
 	case _MACH_Pmac:
-		cuda_request(&req, NULL, 2, CUDA_PACKET, CUDA_POWERDOWN);
-		for (;;)
-			cuda_poll();
+		switch (adb_hardware) {
+		case ADB_VIACUDA:
+			cuda_request(&req, NULL, 2, CUDA_PACKET,
+				     CUDA_POWERDOWN);
+			for (;;)
+				cuda_poll();
+			break;
+		case ADB_VIAPMU:
+			pmu_request(&req, NULL, 5, PMU_SHUTDOWN,
+				    'M', 'A', 'T', 'T');
+			for (;;)
+				pmu_poll();
+			break;
+		default:
+		}
+		break;
+
 	case _MACH_chrp:
 #if 0		/* RTAS doesn't seem to work on Longtrail.
 		   For now, do it the same way as the PReP. */
@@ -126,9 +211,19 @@
 		printk("RTAS system-reboot returned %d\n", err);
 		for (;;);
 #endif
+
 	case _MACH_prep:
 		machine_restart(NULL);
+#ifdef CONFIG_APUS
+	case _MACH_apus:
+#if defined(CONFIG_APM) && defined(CONFIG_APM_POWER_OFF)
+		apm_set_power_state(APM_STATE_OFF);
+		for (;;);
+#endif
+#endif
 	}
+	for (;;)
+		;
 }
 
 void machine_halt(void)
@@ -141,18 +236,90 @@
 		machine_power_off();	/* for now */
 #endif
 	}
-	else /* prep or chrp */
+	else /* prep, chrp or apus */
 		machine_restart(NULL);
 
 }
 
+#ifdef CONFIG_BLK_DEV_IDE
 void ide_init_hwif_ports (ide_ioreg_t *p, ide_ioreg_t base, int *irq)
 {
-	if ( _machine == _MACH_Pmac )
+	switch (_machine) {
+	case _MACH_Pmac:
 		pmac_ide_init_hwif_ports(p,base,irq);
-	else /* prep or chrp */
+		break;
+	case _MACH_chrp:
+		chrp_ide_init_hwif_ports(p,base,irq);
+		break;
+	case _MACH_prep:
 		prep_ide_init_hwif_ports(p,base,irq);
+		break;
+	}
+}
+#endif
+
+unsigned long cpu_temp(void)
+{
+	unsigned long i, temp, thrm1, dir;
+	int sanity;
 	
+	/*
+	 * setup thrm3 - need to give TAU at least 20us
+	 * to do the compare so assume a 300MHz clock.
+	 * We need 300*20 ticks then.
+	 * -- Cort
+	 */
+	asm("mtspr 1020, %1\n\t"
+	    "mtspr 1021, %1\n\t"
+	    "mtspr 1022, %0\n\t"::
+	    "r" ( ((300*20)<<18) | THRM3_E), "r" (0) );
+		
+#if 0
+	for ( i = 127 ; i >= 0 ; i-- )
+	{
+		asm("mtspr 1020, %0\n\t"::
+		    "r" (THRM1_TID|THRM1_V|(i<<2)) );
+		/* check value */
+		while ( !( thrm1 & THRM1_TIV) )
+			asm("mfspr %0, 1020 \n\t": "=r" (thrm1) );
+		if ( thrm1 & THRM1_TIN )
+		{
+			printk("tin set: %x tiv %x\n", thrm1,thrm1&THRM1_TIV);
+			goto out;
+		}
+
+	}
+#endif
+#if 0
+	i = 32;			/* increment */
+	dir = 1;		/* direction we're checking 0=up 1=down */
+	temp = 64;		/* threshold checking against */
+	while ( i )
+	{
+		_set_THRM1((1<<29) | THRM1_V | (temp<<2) );
+		printk("checking %d in dir %d thrm set to %x/%x\n", temp,dir,
+		       ( (1<<29) | THRM1_V | (temp<<2)),_get_THRM1());
+		/* check value */
+		sanity = 0x0fffffff;
+		while ( (!( thrm1 & THRM1_TIV)) && (sanity--) )
+			thrm1 = _get_THRM1();
+			/*asm("mfspr %0, 1020 \n\t": "=r" (thrm1) );*/
+		if ( ! sanity || sanity==0xffffffff ) printk("no sanity\n");
+		/* temp is not in that direction */
+		if ( !(thrm1 & THRM1_TIN) )
+		{
+			printk("not in that dir thrm1 %x\n",thrm1);
+			if ( dir == 0 ) dir = 1;
+			else dir = 0;
+		}
+		if ( dir ) temp -= i;
+		else temp += i;
+		i /= 2;
+	}
+	asm("mtspr 1020, %0\n\t"
+	    "mtspr 1022, %0\n\t" ::"r" (0) );
+#endif	
+	return temp;
 }
 
 int get_cpuinfo(char *buffer)
@@ -160,9 +327,11 @@
 	extern int pmac_get_cpuinfo(char *);
 	extern int chrp_get_cpuinfo(char *);	
 	extern int prep_get_cpuinfo(char *);
+	extern int apus_get_cpuinfo(char *);
 	unsigned long len = 0;
 	unsigned long bogosum = 0;
 	unsigned long i;
+	unsigned long cr;
 #ifdef __SMP__
 	extern unsigned long cpu_present_map;	
 	extern struct cpuinfo_PPC cpu_data[NR_CPUS];
@@ -202,7 +371,18 @@
 			len += sprintf(len+buffer, "603ev\n");
 			break;
 		case 8:
-			len += sprintf(len+buffer, "750 (Arthur)\n");
+			len += sprintf(len+buffer,"750\n");
+			cr = _get_L2CR();
+			len += sprintf(len+buffer,"L2CR\t\t: %lx\n",cr);
+			if ( cr & (0x1<<1)) cr = 256;
+			else if ( cr & (0x2<<1)) cr = 512;
+			else if ( cr & (0x3<<1)) cr = 1024;
+			else cr = 0;
+			len += sprintf(len+buffer,"on-chip l2\t: "
+				       "%ld KB (%s)\n",
+				       cr,(_get_L2CR()&1) ? "on" : "off");
+			len += sprintf(len+buffer,"temperature \t: %lu C\n",
+				       cpu_temp());
 			break;
 		case 9:
 			len += sprintf(len+buffer, "604e\n");
@@ -216,6 +396,7 @@
 			break;
 		}
 
+
 		/*
 		 * Assume here that all clock rates are the same in a
 		 * smp system.  -- Cort
@@ -290,6 +471,11 @@
 	case _MACH_chrp:
 		len += chrp_get_cpuinfo(buffer+len);
 		break;
+#ifdef CONFIG_APUS
+	case _MACH_apus:
+		len += apus_get_cpuinfo(buffer+len);
+		break;
+#endif
 	}
 	return len;
 }
@@ -302,43 +488,63 @@
 identify_machine(unsigned long r3, unsigned long r4, unsigned long r5,
 		 unsigned long r6, unsigned long r7))
 {
-	extern unsigned long initrd_start, initrd_end;
 	extern setup_pci_ptrs(void);
-	unsigned long boot_sdr1;
-	ihandle prom_root;
-	unsigned char type[16], model[16];
-
-	asm("mfspr %0,25\n\t" :"=r" (boot_sdr1));
-
-	/* 
-	 * if we have a sdr1 then we have openfirmware 
-	 * and can ask it what machine we are (chrp/pmac/prep).
-	 * otherwise we're definitely prep. -- Cort
-	 */
-	if ( !boot_sdr1 )
+#ifndef CONFIG_MBX8xx
+
+#ifdef CONFIG_APUS
+	if ( r3 == 0x61707573 )
 	{
-		/* we know for certain we're prep if no OF */
+		/* Parse bootinfo. The bootinfo is located right after
+                   the kernel bss */
+		m68k_parse_bootinfo((const struct bi_record *)&_end);
+
 		have_of = 0;
-		/* make a copy of residual data */
-		if ( r3 )
-			memcpy((void *)&res,(void *)(r3+KERNELBASE),
-			       sizeof(RESIDUAL));
+		
+#ifdef CONFIG_BLK_DEV_INITRD
+		/* Take care of initrd if we have one. Use data from
+		   bootinfo to avoid the need to initialize PPC
+		   registers when kernel is booted via a PPC reset. */
+		if ( m68k_ramdisk.addr ) {
+			initrd_start = (unsigned long) __va(m68k_ramdisk.addr);
+			initrd_end = (unsigned long) 
+				__va(m68k_ramdisk.size + m68k_ramdisk.addr);
+		}
+#endif /* CONFIG_BLK_DEV_INITRD */
+
+		return 0;
+	}
+#endif
+
 #ifndef CONFIG_MACH_SPECIFIC
+	/* prep boot loader tells us if we're prep or not */
+	if ( *(unsigned long *)(KERNELBASE) == (0xdeadc0de) )
+	{
 		_machine = _MACH_prep;
-#endif /* CONFIG_MACH_SPECIFIC */
+		have_of = 0;
+	} else
+	{
+		/* need to ask OF if we're chrp or pmac */
+		extern unsigned char OF_type[16], OF_model[16];
+		prom_print(OF_type);
+		prom_print(OF_model);
+		if ( !strncmp("chrp", OF_type,4) )
+		{
+			_machine = _MACH_chrp;
+		}
+		else 
+		{
+		        /*if ( !strncmp("Power Macintosh", type,15) )*/
+			_machine = _MACH_Pmac;
+                }
+		_machine = _MACH_Pmac;
+
 	}
-	else
+#endif /* CONFIG_MACH_SPECIFIC */		
+
+	if ( have_of )
 	{
-		/*
-		 * init prom here, then ask the openfirmware
-		 * what machine we are (prep/chrp/pmac).  We don't use
-		 * OF on prep just yet.  -- Cort
-		 */
-#ifndef CONFIG_PREP		/* don't use OF on prep yet */
-		have_of = 1;
 		/* prom_init has already been called from __start */
 		finish_device_tree();
-
 		/*
 		 * If we were booted via quik, r3 points to the physical
 		 * address of the command-line parameters.
@@ -356,7 +562,7 @@
 		} else {
 			struct device_node *chosen;
 			char *p;
-
+			
 #ifdef CONFIG_BLK_DEV_INITRD
 			if (r3 - KERNELBASE < 0x800000
 			    && r4 != 0 && r4 != 0xdeadbeef) {
@@ -365,7 +571,7 @@
 				ROOT_DEV = MKDEV(RAMDISK_MAJOR, 0);
 			}
 #endif
-			chosen = find_path_device("/chosen");
+			chosen = find_devices("chosen");
 			if (chosen != NULL) {
 				p = get_property(chosen, "bootargs", NULL);
 				if (p != NULL)
@@ -373,47 +579,18 @@
 			}
 		}
 		cmd_line[sizeof(cmd_line) - 1] = 0;
-#endif /* CONFIG_PREP */
-
-#ifndef CONFIG_MACH_SPECIFIC
-#if 0
-		prom_root = call_prom("finddevice", 1, 1, "/");		
-		call_prom("getprop", 4, 1, prom_root, "device_type", &type,
-			  (void *) sizeof(type));
-		call_prom("getprop", 4, 1, prom_root, "model", &type,
-			  (void *) sizeof(model));
-		if ( !strncmp("chrp", type,4) )
-		{
-			_machine = _MACH_chrp;
-		}
-		else 
-		{
-		        /*if ( !strncmp("Power Macintosh", type,15) )*/
-			_machine = _MACH_Pmac;
-                }
-#else
-
-#ifdef CONFIG_CHRP		
-		_machine = _MACH_chrp;
-#endif /* CONFIG_CHRP */
-#ifdef CONFIG_PMAC		
-		_machine = _MACH_Pmac;
-#endif /* CONFIG_PMAC */
-#ifdef CONFIG_PREP
-		_machine = _MACH_Prep;
-#endif /* CONFIG_PREP */
-#endif /* #if */		
-#endif /* CONFIG_MACH_SPECIFIC */
 	}
 
+#ifdef CONFIG_PCI
 	/* so that pmac/chrp can use pci to find its console -- Cort */
 	setup_pci_ptrs();
-	
+#endif
+
 	switch (_machine)
 	{
 	case _MACH_Pmac:
 #if !defined(CONFIG_MACH_SPECIFIC)
-		isa_io_base = PMAC_ISA_IO_BASE;
+		/* isa_io_base gets set in pmac_find_bridges */
 		isa_mem_base = PMAC_ISA_MEM_BASE;
 		pci_dram_offset = PMAC_PCI_DRAM_OFFSET;
 		ISA_DMA_THRESHOLD = ~0L;
@@ -422,6 +599,10 @@
 #endif /* ! CONFIG_MACH_SPECIFIC */
 		break;
 	case _MACH_prep:
+		/* make a copy of residual data */
+		if ( r3 )
+			memcpy((void *)&res,(void *)(r3+KERNELBASE),
+			       sizeof(RESIDUAL));
 #if !defined(CONFIG_MACH_SPECIFIC)
 		isa_io_base = PREP_ISA_IO_BASE;
 		isa_mem_base = PREP_ISA_MEM_BASE;
@@ -434,13 +615,12 @@
 		if ( res.ResidualLength != 0 )
 		{
 			if ( !strncmp(res.VitalProductData.PrintableModel,"IBM",3) )
-				_prep_type = 0x00;
+				_prep_type = _PREP_IBM;
 			else
-				_prep_type = 0x01;
+				_prep_type = _PREP_Motorola;
 		}
 		else /* assume motorola if no residual (netboot?) */
 			_prep_type = _PREP_Motorola;
-
 #ifdef CONFIG_BLK_DEV_RAM
 		/* take care of initrd if we have one */
 		if ( r4 )
@@ -457,7 +637,14 @@
 		}
 		break;
 	case _MACH_chrp:
-		/* LongTrail */
+#ifdef CONFIG_BLK_DEV_RAM
+		/* take care of initrd if we have one */
+		if ( r3 )
+		{
+			initrd_start = r3 + KERNELBASE;
+			initrd_end = r3+ r4 + KERNELBASE;
+		}
+#endif /* CONFIG_BLK_DEV_RAM */
 #if !defined(CONFIG_MACH_SPECIFIC)
 		isa_io_base = CHRP_ISA_IO_BASE;
 		isa_mem_base = CHRP_ISA_MEM_BASE;
@@ -470,13 +657,33 @@
 	default:
 		printk("Unknown machine type in identify_machine!\n");
 	}
-	return 0;
-}
+#else /* CONFIG_MBX8xx */
+	extern setup_pci_ptrs(void);
 
-__initfunc(unsigned long
-bios32_init(unsigned long memory_start, unsigned long memory_end))
-{
-	return memory_start;
+	if ( r3 )
+		memcpy( (void *)&res,(void *)(r3+KERNELBASE), sizeof(bd_t) );
+
+	setup_pci_ptrs();
+
+#ifdef CONFIG_BLK_DEV_RAM
+	/* take care of initrd if we have one */
+	if ( r4 )
+	{
+		initrd_start = r4 + KERNELBASE;
+		initrd_end = r5 + KERNELBASE;
+	}
+#endif /* CONFIG_BLK_DEV_RAM */
+	/* take care of cmd line */
+	if ( r6 )
+	{
+		
+		*(char *)(r7+KERNELBASE) = 0;
+		strcpy(cmd_line, (char *)(r6+KERNELBASE));
+	}
+
+#endif /* CONFIG_MBX */
+	
+	return 0;
 }
 
 __initfunc(void setup_arch(char **cmdline_p,
@@ -485,12 +692,18 @@
 	extern void pmac_setup_arch(unsigned long *, unsigned long *);
 	extern void chrp_setup_arch(unsigned long *, unsigned long *);
 	extern void prep_setup_arch(unsigned long *, unsigned long *);
+	extern void apus_setup_arch(char **, unsigned long *, unsigned long *);
 	extern int panic_timeout;
 	extern char _etext[], _edata[];
 	extern char *klimit;
 	extern unsigned long find_available_memory(void);
 	extern unsigned long *end_of_DRAM;
 
+#ifdef CONFIG_XMON
+	extern void xmon_map_scc(void);
+	xmon_map_scc();
+#endif /* CONFIG_XMON */
+
 	/* reboot on panic */	
 	panic_timeout = 180;
 	
@@ -516,6 +729,12 @@
 	case _MACH_chrp:
 		chrp_setup_arch(memory_start_p, memory_end_p);
 		break;
+#ifdef CONFIG_APUS
+	case _MACH_apus:
+		m68k_machtype = MACH_AMIGA;
+		apus_setup_arch(cmdline_p,memory_start_p,memory_end_p);
+		break;
+#endif
 	default:
 		printk("Unknown machine %d in setup_arch()\n", _machine);
 	}
diff -u --recursive --new-file v2.1.96/linux/arch/ppc/kernel/signal.c linux/arch/ppc/kernel/signal.c
--- v2.1.96/linux/arch/ppc/kernel/signal.c	Mon Jan 12 15:18:13 1998
+++ linux/arch/ppc/kernel/signal.c	Tue Apr 14 17:33:58 1998
@@ -201,8 +201,13 @@
 	if (sc == (struct sigcontext_struct *)(sigctx.regs)) {
 		/* Last stacked signal - restore registers */
 		sr = (struct sigregs *) sigctx.regs;
+#ifdef __SMP__
+		if ( regs->msr & MSR_FP  )
+			smp_giveup_fpu(current);
+#else	
 		if (last_task_used_math == current)
 			giveup_fpu();
+#endif		
 		if (copy_from_user(saved_regs, &sr->gp_regs,
 				   sizeof(sr->gp_regs)))
 			goto badframe;
@@ -249,8 +254,13 @@
 
 	if (verify_area(VERIFY_WRITE, frame, sizeof(*frame)))
 		goto badframe;
-	if (last_task_used_math == current)
-		giveup_fpu();
+#ifdef __SMP__
+		if ( regs->msr & MSR_FP  )
+			smp_giveup_fpu(current);
+#else	
+		if (last_task_used_math == current)
+			giveup_fpu();
+#endif		
 	if (__copy_to_user(&frame->gp_regs, regs, GP_REGS_SIZE)
 	    || __copy_to_user(&frame->fp_regs, current->tss.fpr,
 			      ELF_NFPREG * sizeof(double))
diff -u --recursive --new-file v2.1.96/linux/arch/ppc/kernel/smp.c linux/arch/ppc/kernel/smp.c
--- v2.1.96/linux/arch/ppc/kernel/smp.c	Mon Jan 12 15:18:13 1998
+++ linux/arch/ppc/kernel/smp.c	Tue Apr 14 17:33:58 1998
@@ -1,5 +1,5 @@
 /*
- * $Id: smp.c,v 1.8 1998/01/06 06:44:57 cort Exp $
+ * $Id: smp.c,v 1.22 1998/04/10 01:53:34 cort Exp $
  *
  * Smp support for ppc.
  *
@@ -30,22 +30,27 @@
 #include <asm/init.h>
 #include <asm/io.h>
 
+#include "time.h"
+
 int smp_threads_ready = 0;
 volatile int smp_commenced = 0;
 int smp_num_cpus = 1;
 unsigned long cpu_present_map = 0;
 volatile int cpu_number_map[NR_CPUS];
 volatile unsigned long cpu_callin_map[NR_CPUS] = {0,};
+volatile int __cpu_logical_map[NR_CPUS];
 static unsigned char boot_cpu_id = 0;
 struct cpuinfo_PPC cpu_data[NR_CPUS];
-struct klock_info klock_info = { KLOCK_CLEAR, 0 };
+struct klock_info_struct klock_info = { KLOCK_CLEAR, 0 };
 volatile unsigned char active_kernel_processor = NO_PROC_ID;	/* Processor holding kernel spinlock		*/
 
-volatile unsigned long hash_table_lock;
+volatile unsigned long ipi_count;
+
+unsigned int prof_multiplier[NR_CPUS];
+unsigned int prof_counter[NR_CPUS];
 
 int start_secondary(void *);
 
-extern void init_IRQ(void);
 extern int cpu_idle(void *unused);
 
 void smp_boot_cpus(void)
@@ -56,49 +61,75 @@
 	struct task_struct *p;
 	
         printk("Entering SMP Mode...\n");
+
+	for (i = 0; i < NR_CPUS; i++) {
+		cpu_number_map[i] = -1;
+		prof_counter[i] = 1;
+		prof_multiplier[i] = 1;
+	}
+	
 	cpu_present_map = 0;
 	for(i=0; i < NR_CPUS; i++)
-		cpu_number_map[i] = -1;
+		__cpu_logical_map[i] = -1;
 
         smp_store_cpu_info(boot_cpu_id);
         active_kernel_processor = boot_cpu_id;
 	current->processor = boot_cpu_id;
-	
         cpu_present_map |= 1;
+        cpu_number_map[boot_cpu_id] = 0;
+	__cpu_logical_map[0] = boot_cpu_id;
+	
+	if ( _machine != _MACH_Pmac )
+	{
+		printk("SMP not supported on this machine.\n");
+		return;
+	}
+	
         /* assume a 2nd processor for now */	
         cpu_present_map |= (1 << 1); 
 	smp_num_cpus = 2;
-        cpu_number_map[boot_cpu_id] = 0;
 	
 	/* create a process for second processor */
         kernel_thread(start_secondary, NULL, CLONE_PID);
-        cpu_number_map[1] = 1;
 	p = task[1];
 	if ( !p )
 		panic("No idle task for secondary processor\n");
 	p->processor = 1;
 	current_set[1] = p;
+	/* need to flush here since secondary bat's aren't setup */
+	dcbf((volatile unsigned long *)&current_set[1]);
+	
 	/* setup entry point of secondary processor */
 	*(volatile unsigned long *)(0xf2800000)
 		= (unsigned long)secondary_entry-KERNELBASE;
-	/* interrupt secondary to begin executing code */
 	eieio();
-	*(volatile unsigned long *)(0xf80000c0) = 0;
+	/* interrupt secondary to begin executing code */
+	*(volatile unsigned long *)(0xf80000c0) = 0L;
 	eieio();
 	/* wait to see if the secondary made a callin (is actually up) */
-	for ( timeout = 0; timeout < 1500 ; timeout++ )
+	for ( timeout = 0; timeout < 15000 ; timeout++ )
+	{
+		if(cpu_callin_map[1])
+			break;
 		udelay(100);
+	}
 	if(cpu_callin_map[1]) {
 		cpu_number_map[1] = 1;
+		__cpu_logical_map[i] = 1;
 		printk("Processor 1 found.\n");
+
+#if 0 /* this sync's the decr's */
+		set_dec(decrementer_count);
+#endif
+		/* interrupt secondary to sync the time bases */
+		smp_message_pass(1,0xf0f0, 0, 0);
+		/* interrupt secondary to begin executing code */
+		/**(volatile unsigned long *)(0xf80000c0) = 0L;
+		eieio();*/
 	} else {
 		smp_num_cpus--;
 		printk("Processor %d is stuck.\n", 1);
 	}
-{
-  extern unsigned long amhere;
-  printk("amhere: %x\n", amhere);
-}
 }
 
 void smp_commence(void)
@@ -128,16 +159,15 @@
 void smp_callin(void)
 {
 	printk("SMP %d: smp_callin()\n",current->processor);
-	/*calibrate_delay();*/
         smp_store_cpu_info(1);
-	
-	/* assume we're just the secondary processor for now */
-	cpu_callin_map[1] = 1;
-	dcbf(&cpu_callin_map[1]);
+	set_dec(decrementer_count);
 	
 	current->mm->mmap->vm_page_prot = PAGE_SHARED;
 	current->mm->mmap->vm_start = PAGE_OFFSET;
 	current->mm->mmap->vm_end = init_task.mm->mmap->vm_end;
+	
+	/* assume we're just the secondary processor for now */
+	cpu_callin_map[1] = 1;
 
 	while(!smp_commenced)
 		barrier();
@@ -149,21 +179,120 @@
 	printk("SMP %d: smp_setup()\n",current->processor);
 }
 
-void smp_message_pass(int target, int msg, unsigned long data, int wait)
+void smp_local_timer_interrupt(struct pt_regs * regs)
+{
+	int cpu = smp_processor_id();
+	extern void update_one_process(struct task_struct *,unsigned long,
+				       unsigned long,unsigned long,int);
+
+	if (!--prof_counter[cpu]) {
+		int user=0,system=0;
+		struct task_struct * p = current;
+
+		/*
+		 * After doing the above, we need to make like
+		 * a normal interrupt - otherwise timer interrupts
+		 * ignore the global interrupt lock, which is the
+		 * WrongThing (tm) to do.
+		 */
+
+		if (user_mode(regs))
+			user=1;
+		else
+			system=1;
+
+		if (p->pid) {
+			update_one_process(p, 1, user, system, cpu);
+
+			p->counter -= 1;
+			if (p->counter < 0) {
+				p->counter = 0;
+				need_resched = 1;
+			}
+			if (p->priority < DEF_PRIORITY) {
+				kstat.cpu_nice += user;
+				kstat.per_cpu_nice[cpu] += user;
+			} else {
+				kstat.cpu_user += user;
+				kstat.per_cpu_user[cpu] += user;
+			}
+
+			kstat.cpu_system += system;
+			kstat.per_cpu_system[cpu] += system;
+
+		}
+		prof_counter[cpu]=prof_multiplier[cpu];
+	}
+}
+
+/*
+ * Dirty hack to get smp message passing working.
+ * Right now it only works for stop cpu's but will be setup
+ * later for more general message passing.
+ *
+ * As it is now, if we're sending two message as the same time
+ * we have race conditions.  I avoided doing locks here since
+ * all that works right now is the stop cpu message.
+ *
+ *  -- Cort
+ */
+int smp_message[NR_CPUS];
+void smp_message_recv(void)
 {
-	printk("SMP %d: sending smp message\n",current->processor);
-#if 0	
-	if ( smp_processor_id() == 0 )
+	int msg = smp_message[smp_processor_id()];
+	
+	printk("SMP %d: smp_message_recv() msg %x\n", smp_processor_id(),msg);
+	
+	/* make sure msg is for us */
+	if ( msg == -1 ) return;
+printk("recv after msg check\n");	
+	switch( msg )
 	{
-		/* interrupt secondary */
-		*(volatile unsigned long *)(0xf80000c0) = 0;
+	case MSG_STOP_CPU:
+		__cli();
+		while (1) ;
+		break;
+	case 0xf0f0: /* syncing time bases - just return */
+		break;
+	default:
+		printk("SMP %d: smp_message_recv(): unknown msg %d\n",
+		       smp_processor_id(), msg);
+		break;
 	}
-	else
+	/* reset message */
+	smp_message[smp_processor_id()] = -1;
+}
+
+spinlock_t mesg_pass_lock = SPIN_LOCK_UNLOCKED;
+void smp_message_pass(int target, int msg, unsigned long data, int wait)
+{
+	printk("SMP %d: sending smp message\n", current->processor);
+
+	spin_lock(&mesg_pass_lock);
+	if ( _machine != _MACH_Pmac )
+		return;
+
+#define OTHER (~smp_processor_id() & 1)
+	
+	switch( target )
 	{
-		/* interrupt primary */
-		*(volatile unsigned long *)(0xf3019000);
+	case MSG_ALL:
+		smp_message[smp_processor_id()] = msg;
+		/* fall through */
+	case MSG_ALL_BUT_SELF:
+		smp_message[OTHER] = msg;
+		break;
+	default:
+		smp_message[target] = msg;
+		break;
 	}
-#endif	
+	/* interrupt secondary processor */
+	/**(volatile unsigned long *)(0xf80000c0) = 0xffffffff;
+	eieio();*/
+	*(volatile unsigned long *)(0xf80000c0) = 0;
+	/* interrupt primary */
+	/**(volatile unsigned long *)(0xf3019000);*/
+	spin_unlock(&mesg_pass_lock);	
 }
 
 int setup_profiling_timer(unsigned int multiplier)
diff -u --recursive --new-file v2.1.96/linux/arch/ppc/kernel/softemu8xx.c linux/arch/ppc/kernel/softemu8xx.c
--- v2.1.96/linux/arch/ppc/kernel/softemu8xx.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/ppc/kernel/softemu8xx.c	Tue Apr 14 17:33:58 1998
@@ -0,0 +1,97 @@
+/*
+ * Software emulation of some PPC instructions for the 8xx core.
+ *
+ * Copyright (C) 1998 Dan Malek (dmalek@jlc.net)
+ *
+ * Software floating emuation for the MPC8xx processor.  I did this mostly
+ * because it was easier than trying to get the libraries compiled for
+ * software floating point.  The goal is still to get the libraries done,
+ * but I lost patience and needed some hacks to at least get init and
+ * shells running.  The first problem is the setjmp/longjmp that save
+ * and restore the floating point registers.
+ *
+ * For this emulation, our working registers are found on the register
+ * save area.
+ */
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/stddef.h>
+#include <linux/unistd.h>
+#include <linux/ptrace.h>
+#include <linux/malloc.h>
+#include <linux/user.h>
+#include <linux/a.out.h>
+#include <linux/interrupt.h>
+#include <linux/config.h>
+
+#include <asm/pgtable.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/processor.h>
+
+/* Eventually we may need a look-up table, but this works for now.
+*/
+#define LFD	50
+#define LFDU	51
+#define STFD	54
+#define STFDU	55
+
+/*
+ * We return 0 on success, 1 on unimplemented instruction, and EFAULT
+ * if a load/store faulted.
+ */
+int
+Soft_emulate_8xx(struct pt_regs *regs)
+{
+	uint	inst, instword;
+	uint	flreg, idxreg, disp;
+	uint	retval;
+	uint	*ea, *ip;
+
+	retval = 0;
+
+	instword = *((uint *)regs->nip);
+	inst = instword >> 26;
+
+	flreg = (instword >> 21) & 0x1f;
+	idxreg = (instword >> 16) & 0x1f;
+	disp = instword & 0xffff;
+
+	ea = (uint *)(regs->gpr[idxreg] + disp);
+	ip = (uint *)&current->tss.fpr[flreg];
+
+	if (inst == LFD) {
+		if (copy_from_user(ip, ea, sizeof(double)))
+			retval = EFAULT;
+	}
+	else if (inst == LFDU) {
+
+		if (copy_from_user(ip, ea, sizeof(double)))
+			retval = EFAULT;
+		else
+			regs->gpr[idxreg] = (uint)ea;
+	}
+	else if (inst == STFD) {
+
+		if (copy_to_user(ea, ip, sizeof(double)))
+			retval = EFAULT;
+	}
+	else if (inst == STFDU) {
+
+		if (copy_to_user(ea, ip, sizeof(double)))
+			retval = EFAULT;
+		else
+			regs->gpr[idxreg] = (uint)ea;
+	}
+	else {
+		retval = 1;
+	}
+
+	if (retval == 0)
+		regs->nip += 4;
+	return(retval);
+}
diff -u --recursive --new-file v2.1.96/linux/arch/ppc/kernel/time.c linux/arch/ppc/kernel/time.c
--- v2.1.96/linux/arch/ppc/kernel/time.c	Mon Feb 23 18:12:02 1998
+++ linux/arch/ppc/kernel/time.c	Tue Apr 14 17:33:58 1998
@@ -1,9 +1,22 @@
 /*
- * $Id: time.c,v 1.17 1997/12/28 22:47:21 paulus Exp $
+ * $Id: time.c,v 1.28 1998/04/07 18:49:49 cort Exp $
  * Common time routines among all ppc machines.
  *
  * Written by Cort Dougan (cort@cs.nmt.edu) to merge
  * Paul Mackerras' version and mine for PReP and Pmac.
+ * MPC8xx/MBX changes by Dan Malek (dmalek@jlc.net).
+ *
+ * Since the MPC8xx has a programmable interrupt timer, I decided to
+ * use that rather than the decrementer.  Two reasons: 1.) the clock
+ * frequency is low, causing 2.) a long wait in the timer interrupt
+ *		while ((d = get_dec()) == dval)
+ * loop.  The MPC8xx can be driven from a variety of input clocks,
+ * so a number of assumptions have been made here because the kernel
+ * parameter HZ is a constant.  We assume (correctly, today :-) that
+ * the MPC8xx on the MBX board is driven from a 32.768 kHz crystal.
+ * This is then divided by 4, providing a 8192 Hz clock into the PIT.
+ * Since it is not possible to get a nice 100 Hz clock out of this, without
+ * creating a software PLL, I have set HZ to 128.  -- Dan
  */
 
 #include <linux/errno.h>
@@ -22,12 +35,21 @@
 #include <asm/io.h>
 #include <asm/processor.h>
 #include <asm/nvram.h>
+#include <asm/cache.h>
+#ifdef CONFIG_MBX
+#include <asm/mbx.h>
+#endif
+#ifdef CONFIG_8xx
+#include <asm/8xx_immap.h>
+#endif
 
 #include "time.h"
 
 /* this is set to the appropriate pmac/prep/chrp func in init_IRQ() */
 int (*set_rtc_time)(unsigned long);
 
+void smp_local_timer_interrupt(struct pt_regs *);
+
 /* keep track of when we need to update the rtc */
 unsigned long last_rtc_update = 0;
 
@@ -48,6 +70,14 @@
 void timer_interrupt(struct pt_regs * regs)
 {
 	int dval, d;
+	unsigned long cpu = smp_processor_id();
+	/* save the HID0 in case dcache was off - see idle.c
+	 * this hack should leave for a better solution -- Cort */
+	unsigned dcache_locked = unlock_dcache();
+	
+if ( smp_processor_id() ) printk("SMP 1: timer intr\n");	
+	hardirq_enter(cpu);
+	
 	while ((dval = get_dec()) < 0) {
 		/*
 		 * Wait for the decrementer to change, then jump
@@ -57,17 +87,49 @@
 		while ((d = get_dec()) == dval)
 			;
 		set_dec(d + decrementer_count);
-		do_timer(regs);
-		/*
-		 * update the rtc when needed
-		 */
-		if ( xtime.tv_sec > last_rtc_update + 660 )
-			if (set_rtc_time(xtime.tv_sec) == 0)
-				last_rtc_update = xtime.tv_sec;
-			else
-				last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */
+		if ( !smp_processor_id() )
+		{
+			do_timer(regs);
+			/*
+			 * update the rtc when needed
+			 */
+			if ( xtime.tv_sec > last_rtc_update + 660 )
+				if (set_rtc_time(xtime.tv_sec) == 0)
+					last_rtc_update = xtime.tv_sec;
+				else
+					last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */
+		}
 	}
+#ifdef __SMP__
+	smp_local_timer_interrupt(regs);
+#endif		
+	
+	hardirq_exit(cpu);
+	/* restore the HID0 in case dcache was off - see idle.c
+	 * this hack should leave for a better solution -- Cort */
+	lock_dcache(dcache_locked);
+}
+
+#ifdef CONFIG_MBX
+/* A place holder for time base interrupts, if they are ever enabled.
+*/
+void timebase_interrupt(int irq, void * dev, struct pt_regs * regs)
+{
+}
+
+/* The RTC on the MPC8xx is an internal register.
+ * We want to protect this during power down, so we need to unlock,
+ * modify, and re-lock.
+ */
+static int
+mbx_set_rtc_time(unsigned long time)
+{
+	((immap_t *)MBX_IMAP_ADDR)->im_sitk.sitk_rtck = KAPWR_KEY;
+	((immap_t *)MBX_IMAP_ADDR)->im_sit.sit_rtc = time;
+	((immap_t *)MBX_IMAP_ADDR)->im_sitk.sitk_rtck = ~KAPWR_KEY;
+	return(0);
 }
+#endif /* CONFIG_MBX */
 
 /*
  * This version of gettimeofday has microsecond resolution.
@@ -108,6 +170,7 @@
 void
 time_init(void)
 {
+#ifndef CONFIG_MBX
 	if ((_get_PVR() >> 16) == 1) {
 		/* 601 processor: dec counts down by 128 every 128ns */
 		decrementer_count = DECREMENTER_COUNT_601;
@@ -119,9 +182,10 @@
 	case _MACH_Pmac:
 		/* can't call pmac_get_rtc_time() yet,
 		   because via-cuda isn't initialized yet. */
-		if ((_get_PVR() >> 16) != 1)
+		if ( (_get_PVR() >> 16) != 1 && (!smp_processor_id()) )
 			pmac_calibrate_decr();
-		set_rtc_time = pmac_set_rtc_time;
+		if ( !smp_processor_id() )
+			set_rtc_time = pmac_set_rtc_time;
 		break;
 	case _MACH_chrp:
 		chrp_time_init();
@@ -135,18 +199,63 @@
 		prep_calibrate_decr();
 		set_rtc_time = prep_set_rtc_time;
 		break;
+/* ifdef APUS specific stuff until the merge is completed. -jskov */
+#ifdef CONFIG_APUS
+	case _MACH_apus:
+	{
+		xtime.tv_sec = apus_get_rtc_time();
+		apus_calibrate_decr();
+		set_rtc_time = apus_set_rtc_time;
+ 		break;
+	}
+#endif
 	}
 	xtime.tv_usec = 0;
+	set_dec(decrementer_count);
+#else
+	mbx_calibrate_decr();
+	set_rtc_time = mbx_set_rtc_time;
+
+	/* First, unlock all of the registers we are going to modify.
+	 * To protect them from corruption during power down, registers
+	 * that are maintained by keep alive power are "locked".  To
+	 * modify these registers we have to write the key value to
+	 * the key location associated with the register.
+	 */
+	((immap_t *)MBX_IMAP_ADDR)->im_sitk.sitk_tbscrk = KAPWR_KEY;
+	((immap_t *)MBX_IMAP_ADDR)->im_sitk.sitk_rtcsck = KAPWR_KEY;
+
 
-	/*
-	 * mark the rtc/on-chip timer as in sync
+	/* Disable the RTC one second and alarm interrupts.
+	*/
+	((immap_t *)MBX_IMAP_ADDR)->im_sit.sit_rtcsc &=
+						~(RTCSC_SIE | RTCSC_ALE);
+
+	/* Enabling the decrementer also enables the timebase interrupts
+	 * (or from the other point of view, to get decrementer interrupts
+	 * we have to enable the timebase).  The decrementer interrupt
+	 * is wired into the vector table, nothing to do here for that.
+	 */
+	((immap_t *)MBX_IMAP_ADDR)->im_sit.sit_tbscr =
+				((mk_int_int_mask(DEC_INTERRUPT) << 8) |
+					 (TBSCR_TBF | TBSCR_TBE));
+	if (request_irq(DEC_INTERRUPT, timebase_interrupt, 0, "tbint", NULL) != 0)
+		panic("Could not allocate timer IRQ!");
+
+	/* Get time from the RTC.
+	*/
+	xtime.tv_sec = ((immap_t *)MBX_IMAP_ADDR)->im_sit.sit_rtc;
+	xtime.tv_usec = 0;
+
+#endif /* CONFIG_MBX */
+	
+	/* mark the rtc/on-chip timer as in sync
 	 * so we don't update right away
 	 */
 	last_rtc_update = xtime.tv_sec;
-
-	set_dec(decrementer_count);
 }
 
+#ifndef CONFIG_MBX
 /*
  * Uses the on-board timer to calibrate the on-chip decrementer register
  * for prep systems.  On the pmac the OF tells us what the frequency is
@@ -158,6 +267,27 @@
 void prep_calibrate_decr(void)
 {
 	unsigned long flags;
+
+	/* the Powerstack II's have trouble with the timer so
+	 * we use a default value -- Cort
+	 */
+	if ( (_prep_type == _PREP_Motorola) &&
+	     ((inb(0x800) & 0xF0) & 0x40) )
+	{
+		unsigned long freq, divisor;
+		static unsigned long t2 = 0;
+		
+		t2 = 998700000/60;
+		freq = t2 * 60;	/* try to make freq/1e6 an integer */
+		divisor = 60;
+		printk("time_init: decrementer frequency = %lu/%lu (%luMHz)\n",
+		       freq, divisor,t2>>20);
+		decrementer_count = freq / HZ / divisor;
+		count_period_num = divisor;
+		count_period_den = freq / 1000000;
+		return;
+	}
+	
 	
 	save_flags(flags);
 
@@ -181,7 +311,7 @@
 {
 	unsigned long freq, divisor;
 	static unsigned long t1 = 0, t2 = 0;
-
+	
 	if ( !t1 )
 		t1 = get_dec();
 	else if (!t2)
@@ -189,11 +319,6 @@
 		t2 = get_dec();
 		t2 = t1-t2;  /* decr's in 1/HZ */
 		t2 = t2*HZ;  /* # decrs in 1s - thus in Hz */
-if ( (t2>>20) > 100 )
-{
-  printk("Decrementer frequency too high: %luMHz.  Using 15MHz.\n",t2>>20);
-  t2 = 998700000/60;
-}
 		freq = t2 * 60;	/* try to make freq/1e6 an integer */
 		divisor = 60;
 		printk("time_init: decrementer frequency = %lu/%lu (%luMHz)\n",
@@ -205,6 +330,32 @@
 	}
 }
 
+#else /* CONFIG_MBX */
+
+/* The decrementer counts at the system (internal) clock frequency divided by
+ * sixteen, or external oscillator divided by four.  Currently, we only
+ * support the MBX, which is system clock divided by sixteen.
+ */
+void mbx_calibrate_decr(void)
+{
+	int freq, fp, divisor;
+
+	if ((((immap_t *)MBX_IMAP_ADDR)->im_clkrst.car_sccr & 0x02000000) == 0)
+		printk("WARNING: Wrong decrementer source clock.\n");
+
+	/* The manual says the frequency is in Hz, but it is really
+	 * as MHz.  The value 'fp' is the number of decrementer ticks
+	 * per second.
+	 */
+	/*fp = (mbx_board_info.bi_intfreq * 1000000) / 16;*/
+	freq = fp*60;	/* try to make freq/1e6 an integer */
+        divisor = 60;
+        printk("time_init: decrementer frequency = %d/%d\n", freq, divisor);
+        decrementer_count = freq / HZ / divisor;
+        count_period_num = divisor;
+        count_period_den = freq / 1000000;
+}
+#endif /* CONFIG_MBX */
 
 /* Converts Gregorian date to seconds since 1970-01-01 00:00:00.
  * Assumes input in normal date format, i.e. 1980-12-31 23:59:59
diff -u --recursive --new-file v2.1.96/linux/arch/ppc/kernel/time.h linux/arch/ppc/kernel/time.h
--- v2.1.96/linux/arch/ppc/kernel/time.h	Mon Jan 12 15:18:13 1998
+++ linux/arch/ppc/kernel/time.h	Tue Apr 14 17:33:58 1998
@@ -1,5 +1,5 @@
 /*
- * $Id: time.h,v 1.7 1997/12/28 22:47:24 paulus Exp $
+ * $Id: time.h,v 1.10 1998/04/01 07:46:03 geert Exp $
  * Common time prototypes and such for all ppc machines.
  *
  * Written by Cort Dougan (cort@cs.nmt.edu) to merge
@@ -12,6 +12,7 @@
 void prep_calibrate_decr_handler(int, void *,struct pt_regs *);
 void prep_calibrate_decr(void);
 void pmac_calibrate_decr(void);
+extern void apus_calibrate_decr(void);
 extern unsigned decrementer_count;
 extern unsigned count_period_num;
 extern unsigned count_period_den;
@@ -24,12 +25,16 @@
 unsigned long prep_get_rtc_time(void);
 unsigned long pmac_get_rtc_time(void);
 unsigned long chrp_get_rtc_time(void);
+unsigned long apus_get_rtc_time(void);
 int prep_set_rtc_time(unsigned long nowtime);
 int pmac_set_rtc_time(unsigned long nowtime);
 int chrp_set_rtc_time(unsigned long nowtime);
+int apus_set_rtc_time(unsigned long nowtime);
 void pmac_read_rtc_time(void);
 void chrp_calibrate_decr(void);
 void chrp_time_init(void);
+int via_calibrate_decr(void);
+void mbx_calibrate_decr(void);
 
 /* Accessor functions for the decrementer register. */
 static __inline__ unsigned int get_dec(void)
diff -u --recursive --new-file v2.1.96/linux/arch/ppc/kernel/traps.c linux/arch/ppc/kernel/traps.c
--- v2.1.96/linux/arch/ppc/kernel/traps.c	Mon Jan 12 15:18:13 1998
+++ linux/arch/ppc/kernel/traps.c	Tue Apr 14 17:33:58 1998
@@ -39,13 +39,31 @@
 extern void bad_page_fault(struct pt_regs *, unsigned long);
 
 #ifdef CONFIG_XMON
+extern void xmon(struct pt_regs *regs);
 extern int xmon_bpt(struct pt_regs *regs);
 extern int xmon_sstep(struct pt_regs *regs);
-extern void xmon(struct pt_regs *regs);
 extern int xmon_iabr_match(struct pt_regs *regs);
+extern int xmon_dabr_match(struct pt_regs *regs);
 extern void (*xmon_fault_handler)(struct pt_regs *regs);
 #endif
 
+#ifdef CONFIG_XMON
+void (*debugger)(struct pt_regs *regs) = xmon;
+int (*debugger_bpt)(struct pt_regs *regs) = xmon_bpt;
+int (*debugger_sstep)(struct pt_regs *regs) = xmon_sstep;
+int (*debugger_iabr_match)(struct pt_regs *regs) = xmon_iabr_match;
+int (*debugger_dabr_match)(struct pt_regs *regs) = xmon_dabr_match;
+void (*debugger_fault_handler)(struct pt_regs *regs);
+#else
+#ifdef CONFIG_KGDB
+void (*debugger)(struct pt_regs *regs);
+int (*debugger_bpt)(struct pt_regs *regs);
+int (*debugger_sstep)(struct pt_regs *regs);
+int (*debugger_iabr_match)(struct pt_regs *regs);
+int (*debugger_dabr_match)(struct pt_regs *regs);
+void (*debugger_fault_handler)(struct pt_regs *regs);
+#endif
+#endif
 /*
  * Trap & Exception support
  */
@@ -61,8 +79,8 @@
 	if (!user_mode(regs))
 	{
 		show_regs(regs);
-#ifdef CONFIG_XMON
-		xmon(regs);
+#if defined(CONFIG_XMON) || defined(CONFIG_KGDB)
+		debugger(regs);
 #endif
 		print_backtrace((unsigned long *)regs->gpr[1]);
 		panic("Exception in kernel pc %lx signal %d",regs->nip,signr);
@@ -75,9 +93,9 @@
 {
 	if ( !user_mode(regs) )
 	{
-#ifdef CONFIG_XMON
-		if (xmon_fault_handler) {
-			xmon_fault_handler(regs);
+#if defined(CONFIG_XMON) || defined(CONFIG_KGDB)
+		if (debugger_fault_handler) {
+			debugger_fault_handler(regs);
 			return;
 		}
 #endif
@@ -103,8 +121,8 @@
 			printk("Unknown values in msr\n");
 		}
 		show_regs(regs);
-#ifdef CONFIG_XMON
-		xmon(regs);
+#if defined(CONFIG_XMON) || defined(CONFIG_KGDB)
+		debugger(regs);
 #endif
 		print_backtrace((unsigned long *)regs->gpr[1]);
 		panic("machine check");
@@ -123,8 +141,8 @@
 void
 InstructionBreakpoint(struct pt_regs *regs)
 {
-#ifdef CONFIG_XMON
-	if (xmon_iabr_match(regs))
+#if defined(CONFIG_XMON) || defined(CONFIG_KGDB)
+	if (debugger_iabr_match(regs))
 		return;
 #endif
 	_exception(SIGTRAP, regs);
@@ -144,8 +162,8 @@
 		_exception(SIGFPE, regs);
 	} else if (regs->msr & 0x20000) {
 		/* trap exception */
-#ifdef CONFIG_XMON
-		if (xmon_bpt(regs))
+#if defined(CONFIG_XMON) || defined(CONFIG_KGDB)
+		if (debugger_bpt(regs))
 			return;
 #endif
 		_exception(SIGTRAP, regs);
@@ -158,8 +176,8 @@
 SingleStepException(struct pt_regs *regs)
 {
 	regs->msr &= ~MSR_SE;  /* Turn off 'trace' bit */
-#ifdef CONFIG_XMON
-	if (xmon_sstep(regs))
+#if defined(CONFIG_XMON) || defined(CONFIG_KGDB)
+	if (debugger_sstep(regs))
 		return;
 #endif
 	_exception(SIGTRAP, regs);	
@@ -170,8 +188,13 @@
 {
 	int fixed;
 
+#ifdef __SMP__
+	if (regs->msr & MSR_FP )
+		smp_giveup_fpu(current);
+#else	
 	if (last_task_used_math == current)
 		giveup_fpu();
+#endif	
 	fixed = fix_alignment(regs);
 	if (fixed == 1) {
 		regs->nip += 4;	/* skip over emulated instruction */
@@ -190,8 +213,8 @@
 {
 	printk(KERN_CRIT "Kernel stack overflow in process %p, r1=%lx\n",
 	       current, regs->gpr[1]);
-#ifdef CONFIG_XMON
-	xmon(regs);
+#if defined(CONFIG_XMON) || defined(CONFIG_KGDB)
+	debugger(regs);
 #endif
 	show_regs(regs);
 	print_backtrace((unsigned long *)regs->gpr[1]);
@@ -204,4 +227,42 @@
 	printk("Task: %p(%d), PC: %08lX/%08lX, Syscall: %3ld, Result: %s%ld\n",
 	       current, current->pid, regs->nip, regs->link, regs->gpr[0],
 	       regs->ccr&0x10000000?"Error=":"", regs->gpr[3]);
+}
+
+#ifdef CONFIG_8xx
+
+void
+SoftwareEmulation(struct pt_regs *regs)
+{
+	int	errcode;
+	extern int	Soft_emulate_8xx (struct pt_regs *regs);
+
+	if (user_mode(regs)) {
+#if 0
+		printk("(user mode)\n");
+		_exception(SIGTRAP, regs);
+#else
+		if (errcode = Soft_emulate_8xx(regs)) {
+printk("Software Emulation 0x%x: 0x%x ",
+			regs->nip, *((uint *)regs->nip));
+print_8xx_pte(current->mm, regs->nip);
+			if (errcode == EFAULT)
+				_exception(SIGBUS, regs);
+			else
+				_exception(SIGILL, regs);
+		}
+#endif
+	}
+	else {
+		printk("(kernel mode)\n");
+		panic("Kernel Mode Software Emulation");
+	}
+}
+#endif
+
+void
+TAUException(struct pt_regs *regs)
+{
+	printk("TAU trap at PC: %lx, SR: %lx, vector=%lx\n",
+	       regs->nip, regs->msr, regs->trap);
 }
diff -u --recursive --new-file v2.1.96/linux/arch/ppc/lib/locks.c linux/arch/ppc/lib/locks.c
--- v2.1.96/linux/arch/ppc/lib/locks.c	Mon Jan 12 15:18:13 1998
+++ linux/arch/ppc/lib/locks.c	Tue Apr 14 17:33:58 1998
@@ -1,5 +1,5 @@
 /*
- * $Id: locks.c,v 1.7 1998/01/06 06:44:59 cort Exp $
+ * $Id: locks.c,v 1.17 1998/03/26 22:19:38 cort Exp $
  *
  * Locks for smp ppc 
  * 
@@ -9,53 +9,78 @@
 
 #include <linux/kernel.h>
 #include <linux/sched.h>
+#include <linux/delay.h>
 #include <asm/processor.h>
 #include <asm/system.h>
 #include <asm/spinlock.h>
+#include <asm/io.h>
 
 #define DEBUG_LOCKS 1
 
 #undef INIT_STUCK
-#define INIT_STUCK 10000
-
-#undef STUCK
-#define STUCK \
-if(!--stuck) { printk("spin_lock(%p) CPU#%d nip %08lx\n", lock, cpu, nip); stuck = INIT_STUCK; }
+#define INIT_STUCK 1000000
 
 void _spin_lock(spinlock_t *lock)
 {
-	unsigned long val, nip = (unsigned long)__builtin_return_address(0);
 	int cpu = smp_processor_id();
+#ifdef DEBUG_LOCKS
 	int stuck = INIT_STUCK;
-	
-again:	
+#endif /* DEBUG_LOCKS */
 	/* try expensive atomic load/store to get lock */
-	__asm__ __volatile__(
-		"10: \n\t"
-		"lwarx %0,0,%1 \n\t"
-		"stwcx. %2,0,%1 \n\t"
-		"bne- 10b \n\t"
-		: "=r" (val)
-		: "r" (&(lock->lock)), "r" ( (cpu&3)|(nip&~3L) ));
-	if(val) {
+	while((unsigned long )xchg_u32((void *)&lock->lock,0xffffffff)) {
 		/* try cheap load until it's free */
 		while(lock->lock) {
-			STUCK;
+#ifdef DEBUG_LOCKS
+			if(!--stuck)
+			{
+				printk("_spin_lock(%p) CPU#%d NIP %p"
+				       " holder: cpu %ld pc %08lX\n",
+				       lock, cpu, __builtin_return_address(0),
+				       lock->owner_cpu,lock->owner_pc);
+				stuck = INIT_STUCK;
+				/* steal the lock */
+				/*xchg_u32((void *)&lock->lock,0);*/
+			}
+#endif /* DEBUG_LOCKS */
 			barrier();
 		}
-		goto again;
 	}
+	lock->owner_pc = (unsigned long)__builtin_return_address(0);
+	lock->owner_cpu = cpu;
+}
+
+int spin_trylock(spinlock_t *lock)
+{
+	unsigned long result;
+
+	result = (unsigned long )xchg_u32((void *)&lock->lock,0xffffffff);
+	if ( !result ) 
+	{ 
+		lock->owner_cpu = smp_processor_id(); 
+		lock->owner_pc = (unsigned long)__builtin_return_address(0);
+	} 
+	return (result == 0);
 }
 
+
+
 void _spin_unlock(spinlock_t *lp)
 {
+#ifdef DEBUG_LOCKS
+	if ( !lp->lock )
+		panic("_spin_unlock(%p): no lock cpu %d %s/%d\n", lp,
+		       smp_processor_id(),current->comm,current->pid);
+	if ( lp->owner_cpu != smp_processor_id() )
+		panic("_spin_unlock(%p): cpu %d trying clear of cpu %d pc %lx val %lx\n",
+		       lp, smp_processor_id(), (int)lp->owner_cpu,
+		       lp->owner_pc,lp->lock);
+#endif /* DEBUG_LOCKS */
+	lp->owner_pc = lp->owner_cpu = 0;
+	eieio();
 	lp->lock = 0;
+	eieio();
 }
 		
-#undef STUCK
-#define STUCK \
-if(!--stuck) { printk("_read_lock(%p) CPU#%d\n", rw, cpu); stuck = INIT_STUCK; }
-
 /*
  * Just like x86, implement read-write locks as a 32-bit counter
  * with the high bit (sign) being the "write" bit.
@@ -63,8 +88,10 @@
  */
 void _read_lock(rwlock_t *rw)
 {
+#ifdef DEBUG_LOCKS
 	unsigned long stuck = INIT_STUCK;
 	int cpu = smp_processor_id();
+#endif /* DEBUG_LOCKS */		  
 
 again:	
 	/* get our read lock in there */
@@ -76,7 +103,13 @@
 		/* wait for the write lock to go away */
 		while ((signed long)((rw)->lock) < 0)
 		{
-			STUCK;
+#ifdef DEBUG_LOCKS
+			if(!--stuck)
+			{
+				printk("_read_lock(%p) CPU#%d\n", rw, cpu);
+				stuck = INIT_STUCK;
+			}
+#endif /* DEBUG_LOCKS */
 		}
 		/* try to get the read lock again */
 		goto again;
@@ -87,33 +120,34 @@
 {
 #ifdef DEBUG_LOCKS
 	if ( rw->lock == 0 )
-	{
-		if ( current)
 		printk("_read_unlock(): %s/%d (nip %08lX) lock %lx",
-		      current->comm,current->pid,current->tss.regs->nip,
+		       current->comm,current->pid,current->tss.regs->nip,
 		      rw->lock);
-		else
-		  printk("no current\n");
-	}
 #endif /* DEBUG_LOCKS */
 	atomic_dec((atomic_t *) &(rw)->lock);
 }
 
-#undef STUCK
-#define STUCK \
-if(!--stuck) { printk("write_lock(%p) CPU#%d lock %lx)\n", rw, cpu,rw->lock); stuck = INIT_STUCK; }
-
 void _write_lock(rwlock_t *rw)
 {
+#ifdef DEBUG_LOCKS
 	unsigned long stuck = INIT_STUCK;
 	int cpu = smp_processor_id();
+#endif /* DEBUG_LOCKS */		  
 
 again:	
 	if ( test_and_set_bit(31,&(rw)->lock) ) /* someone has a write lock */
 	{
 		while ( (rw)->lock & (1<<31) ) /* wait for write lock */
 		{
-			STUCK;
+#ifdef DEBUG_LOCKS
+			if(!--stuck)
+			{
+				printk("write_lock(%p) CPU#%d lock %lx)\n",
+				       rw, cpu,rw->lock);
+				stuck = INIT_STUCK;
+			}
+#endif /* DEBUG_LOCKS */		  
+			barrier();
 		}
 		goto again;
 	}
@@ -124,7 +158,15 @@
 		clear_bit(31,&(rw)->lock);
 		while ( (rw)->lock & ~(1<<31) )
 		{
-			STUCK;
+#ifdef DEBUG_LOCKS
+			if(!--stuck)
+			{
+				printk("write_lock(%p) 2 CPU#%d lock %lx)\n",
+				       rw, cpu,rw->lock);
+				stuck = INIT_STUCK;
+			}
+#endif /* DEBUG_LOCKS */
+			barrier();
 		}
 		goto again;
 	}
@@ -134,14 +176,9 @@
 {
 #ifdef DEBUG_LOCKS
 	if ( !(rw->lock & (1<<31)) )
-	{
-		if ( current)
 		printk("_write_lock(): %s/%d (nip %08lX) lock %lx",
 		      current->comm,current->pid,current->tss.regs->nip,
 		      rw->lock);
-		else
-		  printk("no current\n");
-	}
 #endif /* DEBUG_LOCKS */
 	clear_bit(31,&(rw)->lock);
 }
@@ -149,6 +186,8 @@
 void __lock_kernel(struct task_struct *task)
 {
 #ifdef DEBUG_LOCKS
+	unsigned long stuck = INIT_STUCK;
+	
 	if ( (signed long)(task->lock_depth) < 0 )
 	{
 		printk("__lock_kernel(): %s/%d (nip %08lX) lock depth %x\n",
@@ -156,20 +195,40 @@
 		      task->lock_depth);
 	}
 #endif /* DEBUG_LOCKS */
+
+	if ( atomic_inc_return((atomic_t *) &task->lock_depth) != 1 )
+		return;
 	/* mine! */
-	if ( atomic_inc_return((atomic_t *) &task->lock_depth) == 1 )
-		klock_info.akp = smp_processor_id();
+	while ( xchg_u32( (void *)&klock_info.kernel_flag, KLOCK_HELD) )
+	{
+		/* try cheap load until it's free */
+		while(klock_info.kernel_flag) {
+#ifdef DEBUG_LOCKS
+			if(!--stuck)
+			{
+				printk("_lock_kernel() CPU#%d NIP %p\n",
+				       smp_processor_id(),
+				       __builtin_return_address(0));
+				stuck = INIT_STUCK;
+			}
+#endif /* DEBUG_LOCKS */
+			barrier();
+		}
+	}
+	
+	klock_info.akp = smp_processor_id();
 	/* my kernel mode! mine!!! */
 }
- 
+
 void __unlock_kernel(struct task_struct *task)
 {
 #ifdef DEBUG_LOCKS
- 	if ( task->lock_depth == 0 )
+ 	if ( (task->lock_depth == 0) || (klock_info.kernel_flag != KLOCK_HELD) )
 	{
-		printk("__unlock_kernel(): %s/%d (nip %08lX) lock depth %x\n",
-		      task->comm,task->pid,task->tss.regs->nip,
-		      task->lock_depth);
+		printk("__unlock_kernel(): %s/%d (nip %08lX) "
+		       "lock depth %x flags %lx\n",
+		       task->comm,task->pid,task->tss.regs->nip,
+		       task->lock_depth, klock_info.kernel_flag);
 		klock_info.akp = NO_PROC_ID;		
 		klock_info.kernel_flag = 0;
 		return;
@@ -177,8 +236,8 @@
 #endif /* DEBUG_LOCKS */
 	if ( atomic_dec_and_test((atomic_t *) &task->lock_depth) )
 	{
-		klock_info.akp = NO_PROC_ID;		
-		klock_info.kernel_flag = 0;
+		klock_info.akp = NO_PROC_ID;
+		klock_info.kernel_flag = KLOCK_CLEAR;
 	}
 }	
 
@@ -192,4 +251,3 @@
 		__sti();
        }
 }
-
diff -u --recursive --new-file v2.1.96/linux/arch/ppc/mbx_defconfig linux/arch/ppc/mbx_defconfig
--- v2.1.96/linux/arch/ppc/mbx_defconfig	Wed Dec 31 16:00:00 1969
+++ linux/arch/ppc/mbx_defconfig	Tue Apr 14 17:33:58 1998
@@ -0,0 +1,196 @@
+#
+# Automatically generated by make menuconfig: don't edit
+#
+
+#
+# Platform support
+#
+CONFIG_PPC=y
+# CONFIG_6xx is not set
+CONFIG_8xx=y
+# CONFIG_PMAC is not set
+# CONFIG_PREP is not set
+# CONFIG_CHRP is not set
+# CONFIG_ALL_PPC is not set
+CONFIG_MBX=y
+CONFIG_MACH_SPECIFIC=y
+
+#
+# General setup
+#
+# CONFIG_EXPERIMENTAL is not set
+# CONFIG_MODULES is not set
+CONFIG_PCI=y
+CONFIG_PCI_OLD_PROC=y
+CONFIG_NET=y
+# CONFIG_SYSCTL is not set
+CONFIG_SYSVIPC=y
+# CONFIG_BSD_PROCESS_ACCT is not set
+CONFIG_BINFMT_ELF=y
+CONFIG_KERNEL_ELF=y
+# CONFIG_BINFMT_MISC is not set
+# CONFIG_BINFMT_JAVA is not set
+# CONFIG_PARPORT is not set
+# CONFIG_PMAC_CONSOLE is not set
+# CONFIG_MAC_KEYBOARD is not set
+# CONFIG_MAC_FLOPPY is not set
+# CONFIG_PROC_DEVICETREE is not set
+# CONFIG_XMON is not set
+CONFIG_VGA_CONSOLE=y
+
+#
+# Plug and Play support
+#
+# CONFIG_PNP is not set
+
+#
+# Floppy, IDE, and other block devices
+#
+# CONFIG_BLK_DEV_FD is not set
+# CONFIG_BLK_DEV_IDE is not set
+# CONFIG_BLK_DEV_HD_ONLY is not set
+# CONFIG_BLK_DEV_LOOP is not set
+# CONFIG_BLK_DEV_NBD is not set
+# CONFIG_BLK_DEV_MD is not set
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_INITRD=y
+# CONFIG_BLK_DEV_XD is not set
+CONFIG_PARIDE_PARPORT=y
+# CONFIG_PARIDE is not set
+# CONFIG_BLK_DEV_HD is not set
+
+#
+# NEW devices (io_request, all ALPHA and dangerous)
+#
+# CONFIG_IO_REQUEST is not set
+
+#
+# Networking options
+#
+# CONFIG_PACKET is not set
+# CONFIG_NETLINK is not set
+# CONFIG_FIREWALL is not set
+# CONFIG_NET_ALIAS is not set
+# CONFIG_FILTER is not set
+CONFIG_UNIX=y
+CONFIG_INET=y
+# CONFIG_IP_MULTICAST is not set
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_BOOTP=y
+# CONFIG_IP_PNP_RARP is not set
+# CONFIG_IP_ACCT is not set
+# CONFIG_IP_ROUTER is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_IP_ALIAS is not set
+# CONFIG_SYN_COOKIES is not set
+# CONFIG_INET_RARP is not set
+CONFIG_IP_NOSR=y
+# CONFIG_SKB_LARGE is not set
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+
+#
+# SCSI support
+#
+# CONFIG_SCSI is not set
+
+#
+# Network device support
+#
+CONFIG_NETDEVICES=y
+# CONFIG_ARCNET is not set
+# CONFIG_DUMMY is not set
+# CONFIG_EQUALIZER is not set
+CONFIG_NET_ETHERNET=y
+# CONFIG_NET_VENDOR_3COM is not set
+# CONFIG_LANCE is not set
+# CONFIG_NET_VENDOR_SMC is not set
+# CONFIG_NET_VENDOR_RACAL is not set
+# CONFIG_NET_ISA is not set
+# CONFIG_NET_EISA is not set
+# CONFIG_NET_POCKET is not set
+# CONFIG_FDDI is not set
+# CONFIG_DLCI is not set
+# CONFIG_PPP is not set
+# CONFIG_SLIP is not set
+# CONFIG_NET_RADIO is not set
+# CONFIG_TR is not set
+# CONFIG_WAN_DRIVERS is not set
+# CONFIG_LAPBETHER is not set
+# CONFIG_X25_ASY is not set
+
+#
+# Amateur Radio support
+#
+# CONFIG_HAMRADIO is not set
+
+#
+# ISDN subsystem
+#
+# CONFIG_ISDN is not set
+
+#
+# CD-ROM drivers (not for SCSI or IDE/ATAPI drives)
+#
+# CONFIG_CD_NO_IDESCSI is not set
+# CONFIG_CDROM is not set
+
+#
+# Filesystems
+#
+# CONFIG_QUOTA is not set
+# CONFIG_MINIX_FS is not set
+CONFIG_EXT2_FS=y
+# CONFIG_ISO9660_FS is not set
+# CONFIG_FAT_FS is not set
+# CONFIG_MSDOS_FS is not set
+# CONFIG_UMSDOS_FS is not set
+# CONFIG_VFAT_FS is not set
+# CONFIG_PROC_FS is not set
+CONFIG_NFS_FS=y
+CONFIG_ROOT_NFS=y
+# CONFIG_NFSD is not set
+CONFIG_SUNRPC=y
+CONFIG_LOCKD=y
+# CONFIG_CODA_FS is not set
+# CONFIG_SMB_FS is not set
+# CONFIG_HPFS_FS is not set
+# CONFIG_NTFS_FS is not set
+# CONFIG_SYSV_FS is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_ROMFS_FS is not set
+# CONFIG_AUTOFS_FS is not set
+# CONFIG_UFS_FS is not set
+# CONFIG_MAC_PARTITION is not set
+# CONFIG_NLS is not set
+
+#
+# Character devices
+#
+# CONFIG_VT is not set
+CONFIG_SERIAL=y
+CONFIG_SERIAL_CONSOLE=y
+# CONFIG_SERIAL_EXTENDED is not set
+# CONFIG_SERIAL_NONSTANDARD is not set
+# CONFIG_MOUSE is not set
+# CONFIG_QIC02_TAPE is not set
+# CONFIG_APM is not set
+# CONFIG_WATCHDOG is not set
+# CONFIG_RTC is not set
+# CONFIG_VIDEO_DEV is not set
+# CONFIG_NVRAM is not set
+# CONFIG_JOYSTICK is not set
+# CONFIG_MISC_RADIO is not set
+
+#
+# Ftape, the floppy tape device driver
+#
+# CONFIG_FTAPE is not set
+
+#
+# Sound
+#
+# CONFIG_SOUND is not set
diff -u --recursive --new-file v2.1.96/linux/arch/ppc/mm/fault.c linux/arch/ppc/mm/fault.c
--- v2.1.96/linux/arch/ppc/mm/fault.c	Mon Jan 12 15:18:13 1998
+++ linux/arch/ppc/mm/fault.c	Tue Apr 14 17:33:59 1998
@@ -35,11 +35,11 @@
 #include <asm/system.h>
 #include <asm/uaccess.h>
 
-#ifdef CONFIG_XMON
-extern void xmon(struct pt_regs *);
-extern void (*xmon_fault_handler)(struct pt_regs *);
-extern int xmon_dabr_match(struct pt_regs *);
-int xmon_kernel_faults = 0;
+#if defined(CONFIG_XMON) || defined(CONFIG_KGDB)
+extern void (*debugger)(struct pt_regs *);
+extern void (*debugger_fault_handler)(struct pt_regs *);
+extern int (*debugger_dabr_match)(struct pt_regs *);
+int debugger_kernel_faults = 0;
 #endif
 
 unsigned long htab_reloads = 0; /* updated by head.S:hash_page() */
@@ -72,14 +72,14 @@
 	       (regs->trap == 0x400)?"instr":"data"
 	       );*/
 	       
-#ifdef CONFIG_XMON
-	if (xmon_fault_handler && regs->trap == 0x300) {
-		xmon_fault_handler(regs);
+#if defined(CONFIG_XMON) || defined(CONFIG_KGDB)
+	if (debugger_fault_handler && regs->trap == 0x300) {
+		debugger_fault_handler(regs);
 		return;
 	}
 	if (error_code & 0x00400000) {
 		/* DABR match */
-		if (xmon_dabr_match(regs))
+		if (debugger_dabr_match(regs))
 			return;
 	}
 #endif
@@ -90,9 +90,9 @@
 			printk("page fault in interrupt handler, addr=%lx\n",
 			       address);
 			show_regs(regs);
-#ifdef CONFIG_XMON
-			if (xmon_kernel_faults)
-				xmon(regs);
+#if defined(CONFIG_XMON) || defined(CONFIG_KGDB)
+			if (debugger_kernel_faults)
+				debugger(regs);
 #endif
 		}
 	}
@@ -112,11 +112,22 @@
 		goto bad_area;
 
 good_area:
+#ifdef CONFIG_6xx
 	if (error_code & 0x95700000)
 		/* an error such as lwarx to I/O controller space,
 		   address matching DABR, eciwx, etc. */
+#endif /* CONFIG_6xx */
+#ifdef CONFIG_8xx
+        /* The MPC8xx seems to always set 0x80000000, which is
+         * "undefined".  Of those that can be set, this is the only
+         * one which seems bad.
+         */
+	if (error_code & 0x10000000)
+                /* Guarded storage error. */
+#endif /* CONFIG_8xx */
 		goto bad_area;
 	
+	
 	/* a write */
 	if (error_code & 0x02000000) {
 		if (!(vma->vm_flags & VM_WRITE))
@@ -190,14 +201,47 @@
 	/* kernel has accessed a bad area */
 	show_regs(regs);
 	print_backtrace( (unsigned long *)regs->gpr[1] );
-#ifdef CONFIG_XMON
-	if (xmon_kernel_faults)
-		xmon(regs);
+#if defined(CONFIG_XMON) || defined(CONFIG_KGDB)
+	if (debugger_kernel_faults)
+		debugger(regs);
 #endif
 	panic("kernel access of bad area pc %lx lr %lx address %lX tsk %s/%d",
 	      regs->nip,regs->link,address,current->comm,current->pid);
 }
 
+/*
+ * I need a va to pte function for the MPC8xx so I can set the cache
+ * attributes on individual pages used by the Communication Processor
+ * Module.
+ */
+pte_t *va_to_pte(struct task_struct *tsk, unsigned long address)
+{
+        pgd_t *dir;
+        pmd_t *pmd;
+        pte_t *pte;
+        
+        dir = pgd_offset(tsk->mm, address & PAGE_MASK);
+        if (dir)
+        {
+                pmd = pmd_offset(dir, address & PAGE_MASK);
+                if (pmd && pmd_present(*pmd))
+                {
+                        pte = pte_offset(pmd, address & PAGE_MASK);
+                        if (pte && pte_present(*pte))
+                        {
+                                return(pte);
+                        }
+                } else
+                {
+                        return (0);
+                }
+        } else
+        {
+                return (0);
+        }
+        return (0);
+}
+
 unsigned long va_to_phys(unsigned long address)
 {
 	pgd_t *dir;
@@ -224,6 +268,57 @@
 		return (0);
 	}
 	return (0);
+}
+
+void
+print_8xx_pte(struct mm_struct *mm, unsigned long addr)
+{
+        pgd_t * pgd;
+        pmd_t * pmd;
+        pte_t * pte;
+
+        printk(" pte @ 0x%8lx: ", addr);
+        pgd = pgd_offset(mm, addr & PAGE_MASK);
+        if (pgd) {
+                pmd = pmd_offset(pgd, addr & PAGE_MASK);
+                if (pmd && pmd_present(*pmd)) {
+                        pte = pte_offset(pmd, addr & PAGE_MASK);
+                        if (pte) {
+                                printk(" (0x%08lx)->(0x%08lx)->0x%08lx\n",
+                                        (long)pgd, (long)pte, (long)pte_val(*pte));
+                        }
+                        else {
+                                printk("no pte\n");
+                        }
+                }
+                else {
+                        printk("no pmd\n");
+                }
+        }
+        else {
+                printk("no pgd\n");
+        }
+}
+
+int
+get_8xx_pte(struct mm_struct *mm, unsigned long addr)
+{
+        pgd_t * pgd;
+        pmd_t * pmd;
+        pte_t * pte;
+        int     retval = 0;
+
+        pgd = pgd_offset(mm, addr & PAGE_MASK);
+        if (pgd) {
+                pmd = pmd_offset(pgd, addr & PAGE_MASK);
+                if (pmd && pmd_present(*pmd)) {
+                        pte = pte_offset(pmd, addr & PAGE_MASK);
+                        if (pte) {
+                                        retval = (int)pte_val(*pte);
+                        }
+                }
+        }
+        return(retval);
 }
 
 #if 0
diff -u --recursive --new-file v2.1.96/linux/arch/ppc/mm/init.c linux/arch/ppc/mm/init.c
--- v2.1.96/linux/arch/ppc/mm/init.c	Mon Jan 12 15:18:13 1998
+++ linux/arch/ppc/mm/init.c	Tue Apr 14 17:33:59 1998
@@ -7,6 +7,7 @@
  *  Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au)
  *  and Cort Dougan (PReP) (cort@cs.nmt.edu)
  *    Copyright (C) 1996 Paul Mackerras
+ *  Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk).
  *
  *  Derived from "arch/i386/mm/init.c"
  *    Copyright (C) 1991, 1992, 1993, 1994  Linus Torvalds
@@ -31,6 +32,7 @@
 #include <linux/mm.h>
 #include <linux/swap.h>
 #include <linux/stddef.h>
+#include <linux/vmalloc.h>
 #include <asm/prom.h>
 #include <asm/io.h>
 #include <asm/mmu_context.h>
@@ -40,12 +42,27 @@
 #ifdef CONFIG_BLK_DEV_INITRD
 #include <linux/blk.h>		/* for initrd_* */
 #endif
+#ifdef CONFIG_8xx
+#include <asm/8xx_immap.h>
+#endif
+#ifdef CONFIG_MBX
+#include <asm/mbx.h>
+#endif
 
-int prom_trashed;
-int next_mmu_context;
+#ifndef CONFIG_8xx
 unsigned long _SDR1;
 PTE *Hash, *Hash_end;
 unsigned long Hash_size, Hash_mask;
+#endif /* CONFIG_8xx */
+
+/* ifdef APUS specific stuff until the merge is completed. -jskov */
+#ifdef CONFIG_APUS
+#include <asm/setup.h>
+#include <asm/amigahw.h>
+#endif
+
+int prom_trashed;
+int next_mmu_context;
 unsigned long *end_of_DRAM;
 int mem_init_done;
 extern pgd_t swapper_pg_dir[];
@@ -55,10 +72,16 @@
 extern RESIDUAL res;
 char *klimit = _end;
 struct device_node *memory_node;
+unsigned long ioremap_base;
+unsigned long ioremap_bot;
+#ifndef __SMP__
+struct pgtable_cache_struct quicklists;
+#endif
 
-void *find_mem_piece(unsigned, unsigned);
-static void mapin_ram(void);
+#ifndef CONFIG_8xx
 static void hash_init(void);
+#endif /* CONFIG_8xx */
+static void mapin_ram(void);
 static void *MMU_get_page(void);
 void map_page(struct task_struct *, unsigned long va,
 		     unsigned long pa, int flags);
@@ -68,6 +91,38 @@
 
 extern struct task_struct *current_set[NR_CPUS];
 
+#ifdef CONFIG_MBX
+/* This is a big hack that may not yet work correctly.
+ * The MBX8xx boards have a single DIMM socket for additional memory.
+ * Although it appears you can set magical locations in the serial
+ * EEPROM to get EPPC-Bug to configure this memory, there are no tools
+ * (i.e. commands) to make this easy.  If you screw up, you will most
+ * likely end up with a board that will not boot until you find a
+ * way to program the EEPROM correctly.  I decided to simply program
+ * the memory controller here to add the additional memory.
+ * The reason this may not work correctly is that depending upon the
+ * on-board and DIMM memory size, there may be holes in the physical
+ * address space.  This is the case for me, I have a 4 MB local memory
+ * and a 32 MB DIMM.
+ * The DIMM is 64 bits wide, and we see it as two banks of 32 bit
+ * memory.  The holes are caused by the requirement to map the
+ * memory on a natural alignment, that is a 16 MB bank must begin on
+ * a 16 MB boundary.  The DIMM_SIZE below represents the size of the
+ * bank, which is the total size divided by two.
+ * Although I may not have all of this working, the intention is to
+ * mark all of the page maps in the "hole" as reserved, and adjust
+ * num_physpages accordingly.  In the current implementation, this
+ * seems to work, but there are some assumptions about contiguous
+ * memory.  The correct solution is to modify the memory allocators
+ * to know about holes, but that will have to wait for another day.
+ *
+ * define DIMM_8xx to enable this feature.
+ * define DIMM_SIZE to reflect the bank size (DIMM size divided by two).
+ */
+/*#define DIMM_8xx      1 */
+#define DIMM_SIZE       (16 * 1024 * 1024)
+#endif /* CONFIG_MBX */
+
 /*
  * this tells the system to map all of ram with the segregs
  * (i.e. page tables) instead of the bats.
@@ -77,6 +132,34 @@
 /* optimization for 603 to load the tlb directly from the linux table */
 #define NO_RELOAD_HTAB 1 /* change in kernel/head.S too! */
 
+void __bad_pte(pmd_t *pmd)
+{
+	printk("Bad pmd in pte_alloc: %08lx\n", pmd_val(*pmd));
+	pmd_val(*pmd) = (unsigned long) BAD_PAGETABLE;
+}
+
+pte_t *get_pte_slow(pmd_t *pmd, unsigned long offset)
+{
+        pte_t *pte;
+
+        pte = (pte_t *) __get_free_page(GFP_KERNEL);
+        if (pmd_none(*pmd)) {
+                if (pte) {
+                        clear_page((unsigned long)pte);
+                        pmd_val(*pmd) = (unsigned long)pte;
+                        return pte + offset;
+                }
+		pmd_val(*pmd) = (unsigned long)BAD_PAGETABLE;
+                return NULL;
+        }
+        free_page((unsigned long)pte);
+        if (pmd_bad(*pmd)) {
+                __bad_pte(pmd);
+                return NULL;
+        }
+        return (pte_t *) pmd_page(*pmd) + offset;
+}
+
 /*
  * BAD_PAGE is the page that is used for page faults when linux
  * is out-of-memory. Older versions of linux just did a
@@ -121,65 +204,32 @@
 struct mem_pieces phys_avail;
 struct mem_pieces prom_mem;
 
-static void get_mem_prop(char *, struct mem_pieces *);
-static void sort_mem_pieces(struct mem_pieces *);
-static void coalesce_mem_pieces(struct mem_pieces *);
-static void append_mem_piece(struct mem_pieces *, unsigned, unsigned);
 static void remove_mem_piece(struct mem_pieces *, unsigned, unsigned, int);
+void *find_mem_piece(unsigned, unsigned);
 static void print_mem_pieces(struct mem_pieces *);
 
-static void
-sort_mem_pieces(struct mem_pieces *mp)
-{
-	unsigned long a, s;
-	int i, j;
-
-	for (i = 1; i < mp->n_regions; ++i) {
-		a = mp->regions[i].address;
-		s = mp->regions[i].size;
-		for (j = i - 1; j >= 0; --j) {
-			if (a >= mp->regions[j].address)
-				break;
-			mp->regions[j+1] = mp->regions[j];
-		}
-		mp->regions[j+1].address = a;
-		mp->regions[j+1].size = s;
-	}
-}
-
-static void
-coalesce_mem_pieces(struct mem_pieces *mp)
+/*
+ * Scan a region for a piece of a given size with the required alignment.
+ */
+void *
+find_mem_piece(unsigned size, unsigned align)
 {
-	unsigned long a, e;
-	int i, j, d;
+	int i;
+	unsigned a, e;
+	struct mem_pieces *mp = &phys_avail;
 
-	d = 0;
-	for (i = 0; i < mp->n_regions; i = j) {
+	for (i = 0; i < mp->n_regions; ++i) {
 		a = mp->regions[i].address;
 		e = a + mp->regions[i].size;
-		for (j = i + 1; j < mp->n_regions
-			     && mp->regions[j].address <= e; ++j)
-			e = mp->regions[j].address + mp->regions[j].size;
-		mp->regions[d].address = a;
-		mp->regions[d].size = e - a;
-		++d;
+		a = (a + align - 1) & -align;
+		if (a + size <= e) {
+			remove_mem_piece(mp, a, size, 1);
+			return __va(a);
+		}
 	}
-	mp->n_regions = d;
-}
-
-/*
- * Add some memory to an array of pieces
- */
-static void
-append_mem_piece(struct mem_pieces *mp, unsigned start, unsigned size)
-{
-	struct reg_property *rp;
-
-	if (mp->n_regions >= MAX_MEM_REGIONS)
-		return;
-	rp = &mp->regions[mp->n_regions++];
-	rp->address = start;
-	rp->size = size;
+	printk("Couldn't find %u bytes at %u alignment\n", size, align);
+	abort();
+	return NULL;
 }
 
 /*
@@ -252,33 +302,73 @@
 	printk("\n");
 }
 
-/*
- * Scan a region for a piece of a given size with the required alignment.
- */
-void *
-find_mem_piece(unsigned size, unsigned align)
+
+
+#ifndef CONFIG_8xx
+static void hash_init(void);
+static void get_mem_prop(char *, struct mem_pieces *);
+static void sort_mem_pieces(struct mem_pieces *);
+static void coalesce_mem_pieces(struct mem_pieces *);
+static void append_mem_piece(struct mem_pieces *, unsigned, unsigned);
+
+static void
+sort_mem_pieces(struct mem_pieces *mp)
 {
-	int i;
-	unsigned a, e;
-	struct mem_pieces *mp = &phys_avail;
+	unsigned long a, s;
+	int i, j;
 
-	for (i = 0; i < mp->n_regions; ++i) {
+	for (i = 1; i < mp->n_regions; ++i) {
 		a = mp->regions[i].address;
-		e = a + mp->regions[i].size;
-		a = (a + align - 1) & -align;
-		if (a + size <= e) {
-			remove_mem_piece(mp, a, size, 1);
-			return __va(a);
+		s = mp->regions[i].size;
+		for (j = i - 1; j >= 0; --j) {
+			if (a >= mp->regions[j].address)
+				break;
+			mp->regions[j+1] = mp->regions[j];
 		}
+		mp->regions[j+1].address = a;
+		mp->regions[j+1].size = s;
 	}
-	printk("Couldn't find %u bytes at %u alignment\n", size, align);
-	abort();
-	return NULL;
+}
+
+static void
+coalesce_mem_pieces(struct mem_pieces *mp)
+{
+	unsigned long a, e;
+	int i, j, d;
+
+	d = 0;
+	for (i = 0; i < mp->n_regions; i = j) {
+		a = mp->regions[i].address;
+		e = a + mp->regions[i].size;
+		for (j = i + 1; j < mp->n_regions
+			     && mp->regions[j].address <= e; ++j)
+			e = mp->regions[j].address + mp->regions[j].size;
+		mp->regions[d].address = a;
+		mp->regions[d].size = e - a;
+		++d;
+	}
+	mp->n_regions = d;
+}
+
+/*
+ * Add some memory to an array of pieces
+ */
+static void
+append_mem_piece(struct mem_pieces *mp, unsigned start, unsigned size)
+{
+	struct reg_property *rp;
+
+	if (mp->n_regions >= MAX_MEM_REGIONS)
+		return;
+	rp = &mp->regions[mp->n_regions++];
+	rp->address = start;
+	rp->size = size;
 }
 
 /*
  * Read in a property describing some pieces of memory.
  */
+
 static void
 get_mem_prop(char *name, struct mem_pieces *mp)
 {
@@ -365,6 +455,41 @@
 
 	return __va(total);
 }
+#endif /* CONFIG_8xx */
+
+#ifdef CONFIG_APUS
+#define HARDWARE_MAPPED_SIZE (512*1024)
+unsigned long *apus_find_end_of_memory(void)
+{
+	unsigned long kstart, ksize;
+
+	/* Add the chunk that ADOS does not see. Removed again below. */
+	m68k_memory[0].size += HARDWARE_MAPPED_SIZE;
+
+	append_mem_piece(&phys_mem, m68k_memory[0].addr, m68k_memory[0].size);
+
+	phys_avail = phys_mem;
+	kstart = __pa(_stext);
+	ksize = PAGE_ALIGN(klimit - _stext);
+	remove_mem_piece(&phys_avail, kstart, ksize, 1);
+
+	/* Remove the upper HARDWARE_MAPPED_SIZE bytes where the address
+	 * range 0xfff00000-0xfffx0000 is mapped to.
+	 * We do it this way to ensure that the memory registered in the
+	 * system has a power-of-two size.
+	 */
+	remove_mem_piece(&phys_avail, 
+			 (m68k_memory[0].addr + m68k_memory[0].size 
+			  - HARDWARE_MAPPED_SIZE),
+			 HARDWARE_MAPPED_SIZE, 1);
+
+	/* FIXME:APUS: Only handles one block of memory! Problem is
+	 * that the VTOP/PTOV code in head.S would be a mess if it had
+	 * to handle more than one block.
+	 */
+	return __va(m68k_memory[0].addr + m68k_memory[0].size);
+}
+#endif
 
 /*
  * Find some memory for setup_arch to return.
@@ -381,6 +506,16 @@
 	unsigned long start, end;
 
 	free = 0;
+	if (_machine == _MACH_mbx) {
+		/* Return the first, not the last region, because we
+                 * may not yet have properly initialized the additonal
+                 * memory DIMM.
+                 */
+                a = PAGE_ALIGN(phys_avail.regions[0].address);
+                avail_start = (unsigned long) __va(a);
+                return avail_start;
+        }
+	
 	for (i = 0; i < phys_avail.n_regions - 1; ++i) {
 		start = phys_avail.regions[i].address;
 		end = start + phys_avail.regions[i].size;
@@ -396,7 +531,7 @@
 void show_mem(void)
 {
 	int i,free = 0,total = 0,reserved = 0;
-	int shared = 0;
+	int shared = 0, cached = 0;
 	struct task_struct *p;
 
 	printk("Mem-info:\n");
@@ -407,6 +542,8 @@
 		total++;
 		if (PageReserved(mem_map+i))
 			reserved++;
+		else if (PageSwapCache(mem_map+i))
+			cached++;
 		else if (!atomic_read(&mem_map[i].count))
 			free++;
 		else
@@ -416,6 +553,8 @@
 	printk("%d free pages\n",free);
 	printk("%d reserved pages\n",reserved);
 	printk("%d pages shared\n",shared);
+	printk("%d pages swap cached\n",cached);
+	printk("%d pages in page table cache\n",(int)pgtable_cache_size);
 	show_buffers();
 #ifdef CONFIG_NET
 	show_net_buffers();
@@ -487,6 +626,7 @@
 	int codepages = 0;
 	int datapages = 0;
 	int initpages = 0;
+	extern unsigned int rtas_data, rtas_size;
 
 	end_mem &= PAGE_MASK;
 	high_memory = (void *) end_mem;
@@ -496,6 +636,7 @@
 	/* mark usable pages in the mem_map[] */
 	start_mem = PAGE_ALIGN(start_mem);
 
+#ifndef CONFIG_8xx
 	remove_mem_piece(&phys_avail, __pa(avail_start),
 			 start_mem - avail_start, 1);
 
@@ -520,7 +661,52 @@
 			clear_bit(PG_reserved, &mem_map[MAP_NR(a)].flags);
 	}
 	prom_trashed = 1;
-
+#else /* CONFIG_8xx */
+	/* When we get here, all of the page maps have been set up and
+         * Linux thinks we have contiguous memory.  Since the MBX can
+         * have memory holes, we need to compensate for that here.
+         * The memory holes are currently pages marked reserved (all
+         * pages right now are marked reserved).
+         * All of the memory allocated by the kernel up to this point
+         * had to come from region 0.
+         */
+
+        /* First, unreserve all memory from the page following start_mem
+         * to the end of region 0.
+         */
+        for (addr = start_mem + PAGE_SIZE ;
+              addr < (ulong) __va(phys_mem.regions[0].size);
+                addr += PAGE_SIZE) {
+                        clear_bit(PG_reserved, &mem_map[MAP_NR(addr)].flags);
+        }
+
+        /* Now add any additional regions to the system.
+        */
+        for (i = 1; i < phys_avail.n_regions; ++i) {
+                a = (unsigned long) __va(phys_avail.regions[i].address);
+                lim = a + phys_avail.regions[i].size;
+                a = PAGE_ALIGN(a);
+                for (; a < lim; a += PAGE_SIZE)
+                        clear_bit(PG_reserved, &mem_map[MAP_NR(a)].flags);
+        }
+        phys_avail.n_regions = 0;       /* Nothing available, kernel owns */
+        /* Count up the size of the holes.  We look for the space
+         * between the end of one region and the start of the next.
+         */
+        lim = 0;
+        for (i = 0; i < phys_mem.n_regions-1; ++i) {
+                a = (unsigned long) phys_mem.regions[i].address;
+                a += phys_mem.regions[i].size;
+                lim += phys_mem.regions[i+1].address - a;
+        }
+
+        /* It appears that num_physpages is only used for quota checking,
+         * when pages are locked down.  We subtract the size of the holes
+         * from it now.
+         */
+        num_physpages -= lim/PAGE_SIZE;
+#endif /* CONFIG_8xx */
+	
 	for (addr = PAGE_OFFSET; addr < end_mem; addr += PAGE_SIZE) {
 		if (PageReserved(mem_map + MAP_NR(addr))) {
 			if (addr < (ulong) etext)
@@ -537,7 +723,12 @@
 		if (!initrd_start ||
 		    addr < (initrd_start & PAGE_MASK) || addr >= initrd_end)
 #endif /* CONFIG_BLK_DEV_INITRD */
-			free_page(addr);
+#ifndef CONFIG_8xx		  
+			if ( !rtas_data ||
+			     addr < (rtas_data & PAGE_MASK) ||
+			     addr >= (rtas_data+rtas_size))
+#endif /* CONFIG_8xx */
+				free_page(addr);
 	}
 
         printk("Memory: %luk available (%dk kernel code, %dk data, %dk init) [%08x,%08lx]\n",
@@ -594,6 +785,7 @@
 	return;
 }
 
+#ifndef CONFIG_8xx
 union ubat {			/* BAT register values to be loaded */
 	BAT	bat;
 	P601_BAT bat_601;
@@ -692,7 +884,7 @@
 
 	return (__va(total));
 }
-
+#endif /* CONFIG_8xx */
 
 /*
  * Map in all of physical memory starting at KERNELBASE.
@@ -702,8 +894,9 @@
 static void mapin_ram()
 {
 	int i;
-	unsigned long tot, bl, done;
 	unsigned long v, p, s, f;
+#ifndef CONFIG_8xx
+	unsigned long tot, mem_base, bl, done;
 
 #ifndef MAP_RAM_WITH_SEGREGS
 	/* Set up BAT2 and if necessary BAT3 to cover RAM. */
@@ -711,15 +904,17 @@
 	for (bl = 128<<10; bl < 256<<20; bl <<= 1)
 		if (bl * 2 > tot)
 			break;
-	setbat(2, KERNELBASE, 0, bl, RAM_PAGE);
-	done = __pa(bat_addrs[2].limit) + 1;
+
+	mem_base = __pa(KERNELBASE);
+	setbat(2, KERNELBASE, mem_base, bl, RAM_PAGE);
+	done = (unsigned long)bat_addrs[2].limit - KERNELBASE + 1;
 	if (done < tot) {
 		/* use BAT3 to cover a bit more */
 		tot -= done;
 		for (bl = 128<<10; bl < 256<<20; bl <<= 1)
 			if (bl * 2 > tot)
 				break;
-		setbat(3, KERNELBASE+done, done, bl, RAM_PAGE);
+		setbat(3, KERNELBASE+done, mem_base+done, bl, RAM_PAGE);
 	}
 #endif
 
@@ -734,6 +929,27 @@
 				/* On the powerpc, no user access
 				   forces R/W kernel access */
 				f |= _PAGE_USER;
+#else	/* CONFIG_8xx */
+            for (i = 0; i < phys_mem.n_regions; ++i) {
+                    v = (ulong)__va(phys_mem.regions[i].address);
+                    p = phys_mem.regions[i].address;
+                    for (s = 0; s < phys_mem.regions[i].size; s += PAGE_SIZE) {
+                        /* On the MPC8xx, we want the page shared so we
+                         * don't get ASID compares on kernel space.
+                         */
+                            f = _PAGE_PRESENT | _PAGE_ACCESSED | _PAGE_SHARED;
+
+                        /* I don't really need the rest of this code, but
+                         * I grabbed it because I think the line:
+                         *      f |= _PAGE_USER
+                         * is incorrect.  It needs to be set to bits we
+                         * don't define to cause a kernel read-only.  On
+                         * the MPC8xx, the PAGE_DIRTY takes care of that
+                         * for us (along with the RW software state).
+                         */
+                            if ((char *) v < _stext || (char *) v >= etext)
+                                    f |= _PAGE_RW | _PAGE_DIRTY | _PAGE_HWWRITE;
+#endif /* CONFIG_8xx */
 			map_page(&init_task, v, p, f);
 			v += PAGE_SIZE;
 			p += PAGE_SIZE;
@@ -741,6 +957,7 @@
 	}	    
 }
 
+#ifndef CONFIG_8xx
 /*
  * Initialize the hash table and patch the instructions in head.S.
  */
@@ -820,7 +1037,7 @@
 		Hash_end = 0;
 
 }
-
+#endif /* CONFIG_8xx */
 
 /*
  * Do very early mm setup such as finding the size of memory
@@ -832,13 +1049,19 @@
 void
 MMU_init(void)
 {
+#ifndef CONFIG_8xx
 	if (have_of)
 		end_of_DRAM = pmac_find_end_of_memory();
+#ifdef CONFIG_APUS
+	else if (_machine == _MACH_apus )
+		end_of_DRAM = apus_find_end_of_memory();
+#endif
 	else /* prep */
 		end_of_DRAM = prep_find_end_of_memory();
 
         hash_init();
         _SDR1 = __pa(Hash) | (Hash_mask >> 10);
+	ioremap_base = 0xf8000000;
 
 	/* Map in all of RAM starting at KERNELBASE */
 	mapin_ram();
@@ -856,16 +1079,40 @@
 		       IO_PAGE + ((_prep_type == _PREP_IBM)? _PAGE_USER: 0));
 		break;
 	case _MACH_chrp:
-		setbat(0, 0xc0000000, 0xc0000000, 0x10000000, IO_PAGE);
-		setbat(1, 0xf8000000, 0xf8000000, 0x20000, IO_PAGE);
-		setbat(3, 0x80000000, 0x80000000, 0x10000000, IO_PAGE);
+		setbat(0, 0xf8000000, 0xf8000000, 0x20000, IO_PAGE);
 		break;
 	case _MACH_Pmac:
 		setbat(0, 0xf3000000, 0xf3000000, 0x100000, IO_PAGE);
-		/* this is used to cover registers used by smp boards -- Cort */
-		setbat(3, 0xf8000000, 0xf8000000, 0x100000, IO_PAGE);
+		ioremap_base = 0xf0000000;
+		break;
+#ifdef CONFIG_APUS
+	case _MACH_apus:
+		/* Map Cyberstorm PPC registers. */
+		/* FIXME:APUS: Performance penalty here. Restrict it
+		 *             to the Cyberstorm registers.
+		 */
+		setbat(0, 0xfff00000, 0xfff00000, 0x00080000, IO_PAGE);
+		/* Map chip and ZorroII memory */
+		setbat(1, zTwoBase,   0x00000000, 0x01000000, IO_PAGE);
 		break;
+#endif
 	}
+	ioremap_bot = ioremap_base;
+#else /* CONFIG_8xx */
+
+        /* Map in all of RAM starting at KERNELBASE */
+        mapin_ram();
+
+        /* Now map in some of the I/O space that is generically needed
+         * or shared with multiple devices.
+         * All of this fits into the same 4Mbyte region, so it only
+         * requires one page table page.
+         */
+        ioremap(NVRAM_ADDR, NVRAM_SIZE);
+        ioremap(MBX_CSR_ADDR, MBX_CSR_SIZE);
+        ioremap(MBX_IMAP_ADDR, MBX_IMAP_SIZE);
+        ioremap(PCI_CSR_ADDR, PCI_CSR_SIZE);
+#endif /* CONFIG_8xx */
 }
 
 static void *
@@ -887,27 +1134,98 @@
 void *
 ioremap(unsigned long addr, unsigned long size)
 {
-	unsigned long p, end = addr + size;
+	return __ioremap(addr, size, _PAGE_NO_CACHE);
+}
+
+void *
+__ioremap(unsigned long addr, unsigned long size, unsigned long flags)
+{
+	unsigned long p, v, i;
+
+	/*
+	 * Choose an address to map it to.
+	 * Once the vmalloc system is running, we use it.
+	 * Before then, we map addresses >= ioremap_base
+	 * virt == phys; for addresses below this we use
+	 * space going down from ioremap_base (ioremap_bot
+	 * records where we're up to).
+	 *
+	 * We should also look out for a frame buffer and
+	 * map it with a free BAT register, if there is one.
+	 */
+	p = addr & PAGE_MASK;
+	size = PAGE_ALIGN(addr + size) - p;
+	if (size == 0)
+		return NULL;
+
+	if (mem_init_done) {
+		struct vm_struct *area;
+		area = get_vm_area(size);
+		if (area == 0)
+			return NULL;
+		v = VMALLOC_VMADDR(area->addr);
+	} else {
+		if (p >= ioremap_base)
+			v = p;
+		else
+			v = (ioremap_bot -= size);
+	}
 
-	for (p = addr & PAGE_MASK; p < end; p += PAGE_SIZE)
-		map_page(&init_task, p, p,
-			 pgprot_val(PAGE_KERNEL_CI) | _PAGE_GUARDED);
-	return (void *) addr;
+	flags |= pgprot_val(PAGE_KERNEL);
+	if (flags & (_PAGE_NO_CACHE | _PAGE_WRITETHRU))
+		flags |= _PAGE_GUARDED;
+	for (i = 0; i < size; i += PAGE_SIZE)
+		map_page(&init_task, v+i, p+i, flags);
+
+	return (void *) (v + (addr & ~PAGE_MASK));
 }
 
-void iounmap(unsigned long *addr)
+void iounmap(void *addr)
 {
 	/* XXX todo */
 }
 
+unsigned long iopa(unsigned long addr)
+{
+	unsigned long idx;
+	pmd_t *pd;
+	pte_t *pg;
+#ifndef CONFIG_8xx
+	int b;
+#endif
+	idx = addr & ~PAGE_MASK;
+	addr = addr & PAGE_MASK;
+
+#ifndef CONFIG_8xx
+	/* Check the BATs */
+	for (b = 0; b < 4; ++b)
+		if (addr >= bat_addrs[b].start && addr <= bat_addrs[b].limit)
+			return bat_addrs[b].phys | idx;
+#endif /* CONFIG_8xx */
+	/* Do we have a page table? */
+	if (init_task.mm->pgd == NULL)
+		return 0;
+
+	/* Use upper 10 bits of addr to index the first level map */
+	pd = (pmd_t *) (init_task.mm->pgd + (addr >> PGDIR_SHIFT));
+	if (pmd_none(*pd))
+		return 0;
+
+	/* Use middle 10 bits of addr to index the second-level map */
+	pg = pte_offset(pd, addr);
+	return (pte_val(*pg) & PAGE_MASK) | idx;
+}
+
 void
 map_page(struct task_struct *tsk, unsigned long va,
 	 unsigned long pa, int flags)
 {
 	pmd_t *pd;
 	pte_t *pg;
+#ifndef CONFIG_8xx
 	int b;
-
+#endif
+	
 	if (tsk->mm->pgd == NULL) {
 		/* Allocate upper level page map */
 		tsk->mm->pgd = (pgd_t *) MMU_get_page();
@@ -915,6 +1233,7 @@
 	/* Use upper 10 bits of VA to index the first level map */
 	pd = (pmd_t *) (tsk->mm->pgd + (va >> PGDIR_SHIFT));
 	if (pmd_none(*pd)) {
+#ifndef CONFIG_8xx
 		/*
 		 * Need to allocate second-level table, but first
 		 * check whether this address is already mapped by
@@ -927,14 +1246,16 @@
 				return;
 			}
 		}
+#endif /* CONFIG_8xx */
 		pg = (pte_t *) MMU_get_page();
 		pmd_val(*pd) = (unsigned long) pg;
 	}
 	/* Use middle 10 bits of VA to index the second-level map */
 	pg = pte_offset(pd, va);
 	set_pte(pg, mk_pte_phys(pa & PAGE_MASK, __pgprot(flags)));
-	/*flush_hash_page(va >> 28, va);*/
+#ifndef CONFIG_8xx
 	flush_hash_page(0, va);
+#endif	
 }
 
 /*
@@ -957,11 +1278,14 @@
 void
 local_flush_tlb_all(void)
 {
+#ifndef CONFIG_8xx
 	memset(Hash, 0, Hash_size);
 	_tlbia();
+#else
+	asm volatile ("tlbia" : : );
+#endif
 }
 
-
 /*
  * Flush all the (user) entries for the address space described
  * by mm.  We can't rely on mm->mmap describing all the entries
@@ -970,33 +1294,43 @@
 void
 local_flush_tlb_mm(struct mm_struct *mm)
 {
+#ifndef CONFIG_8xx
 	mm->context = NO_CONTEXT;
 	if (mm == current->mm) {
 		get_mmu_context(current);
 		/* done by get_mmu_context() now -- Cort */
 		/*set_context(current->mm->context);*/
 	}
+#else
+	asm volatile ("tlbia" : : );
+#endif
 }
 
 void
 local_flush_tlb_page(struct vm_area_struct *vma, unsigned long vmaddr)
 {
+#ifndef CONFIG_8xx
 	if (vmaddr < TASK_SIZE)
 		flush_hash_page(vma->vm_mm->context, vmaddr);
 	else
 		flush_hash_page(0, vmaddr);
+#else
+	asm volatile ("tlbia" : : );
+#endif
 }
 
 
-/* for each page addr in the range, call MMU_invalidate_page()
-   if the range is very large and the hash table is small it might be faster to
-   do a search of the hash table and just invalidate pages that are in the range
-   but that's for study later.
-        -- Cort
-   */
+/*
+ * for each page addr in the range, call MMU_invalidate_page()
+ * if the range is very large and the hash table is small it might be
+ * faster to do a search of the hash table and just invalidate pages
+ * that are in the range but that's for study later.
+ * -- Cort
+ */
 void
 local_flush_tlb_range(struct mm_struct *mm, unsigned long start, unsigned long end)
 {
+#ifndef CONFIG_8xx
 	start &= PAGE_MASK;
 
 	if (end - start > 20 * PAGE_SIZE)
@@ -1009,6 +1343,9 @@
 	{
 		flush_hash_page(mm->context, start);
 	}
+#else
+	asm volatile ("tlbia" : : );
+#endif
 }
 
 /*
@@ -1020,6 +1357,7 @@
 void
 mmu_context_overflow(void)
 {
+#ifndef CONFIG_8xx
 	struct task_struct *tsk;
 
 	printk(KERN_DEBUG "mmu_context_overflow\n");
@@ -1034,6 +1372,12 @@
 	/* make sure current always has a context */
 	current->mm->context = MUNGE_CONTEXT(++next_mmu_context);
 	set_context(current->mm->context);
+#else
+	/* We set the value to -1 because it is pre-incremented before
+	 * before use.
+	 */
+	next_mmu_context = -1;
+#endif
 }
 
 #if 0
@@ -1090,3 +1434,69 @@
 }
 #endif
 
+#ifdef CONFIG_MBX
+/*
+ * This is a big hack right now, but it may turn into something real
+ * someday.
+ *
+ * For the MBX860 (at this time anyway), there is nothing to initialize
+ * associated the the PROM.  Rather than include all of the prom.c
+ * functions in the image just to get prom_init, all we really need right
+ * now is the initialization of the physical memory region.
+ */
+void
+set_mbx_memory(void)
+{
+	unsigned long kstart, ksize;
+	bd_t	*binfo;
+#ifdef DIMM_8xx
+	volatile memctl8xx_t	*mcp;
+#endif
+
+	binfo = (bd_t *)&res;
+
+	/* The MBX can have up to three memory regions, the on-board
+	 * DRAM plus two more banks of DIMM socket memory.  The DIMM is
+	 * 64 bits, seen from the processor as two 32 bit banks.
+	 * The on-board DRAM is reflected in the board information
+	 * structure, and is either 4 Mbytes or 16 Mbytes.
+	 * I think there is a way to program the serial EEPROM information
+	 * so EPPC-Bug will initialize this memory, but I have not
+	 * done that and it may not be a wise thing to do.  If you
+	 * remove the DIMM without reprogramming the EEPROM, bad things
+	 * could happen since EPPC-Bug tries to use the upper 128K of
+	 * memory.
+	 */
+	phys_mem.n_regions = 1;
+	phys_mem.regions[0].address = 0;
+	phys_mem.regions[0].size = binfo->bi_memsize;
+	end_of_DRAM = __va(binfo->bi_memsize);
+
+#ifdef DIMM_8xx
+	/* This is a big hack.  It assumes my 32 Mbyte DIMM in a 40 MHz
+	 * MPC860.  Don't do this (or change this) if you are running
+	 * something else.
+	 */
+	mcp = (memctl8xx_t *)(&(((immap_t *)MBX_IMAP_ADDR)->im_memctl));
+
+	mcp->memc_or2 = (~(DIMM_SIZE-1) | 0x00000400);
+	mcp->memc_br2 = DIMM_SIZE | 0x00000081;
+	mcp->memc_or3 = (~((2*DIMM_SIZE)-1) | 0x00000400);
+	mcp->memc_br3 = 2*DIMM_SIZE | 0x00000081;
+
+
+	phys_mem.regions[phys_mem.n_regions].address = DIMM_SIZE;
+	phys_mem.regions[phys_mem.n_regions++].size = DIMM_SIZE;
+	phys_mem.regions[phys_mem.n_regions].address = 2 * DIMM_SIZE;
+	phys_mem.regions[phys_mem.n_regions++].size = DIMM_SIZE;
+
+	end_of_DRAM = __va(3 * DIMM_SIZE);
+#endif
+
+	phys_avail = phys_mem;
+
+	kstart = __pa(_stext);	/* should be 0 */
+	ksize = PAGE_ALIGN(_end - _stext);
+	remove_mem_piece(&phys_avail, kstart, ksize, 0);
+}
+#endif
diff -u --recursive --new-file v2.1.96/linux/arch/ppc/pmac_defconfig linux/arch/ppc/pmac_defconfig
--- v2.1.96/linux/arch/ppc/pmac_defconfig	Tue Mar 17 22:18:14 1998
+++ linux/arch/ppc/pmac_defconfig	Tue Apr 14 17:33:59 1998
@@ -1,5 +1,5 @@
 #
-# Automatically generated by make menuconfig: don't edit
+# Automatically generated make config: don't edit
 #
 
 #
@@ -7,11 +7,12 @@
 #
 CONFIG_PPC=y
 CONFIG_NATIVE=y
+CONFIG_PPC6XX=y
+# CONFIG_PPC8XX is not set
 CONFIG_MACH_SPECIFIC=y
 CONFIG_PMAC=y
 # CONFIG_PREP is not set
 # CONFIG_CHRP is not set
-CONFIG_COMMON=y
 
 #
 # General setup
@@ -19,8 +20,9 @@
 CONFIG_EXPERIMENTAL=y
 CONFIG_MODULES=y
 # CONFIG_MODVERSIONS is not set
-CONFIG_KMOD=y
+CONFIG_KERNELD=y
 CONFIG_PCI=y
+CONFIG_PCI_OLD_PROC=y
 CONFIG_NET=y
 CONFIG_SYSCTL=y
 CONFIG_SYSVIPC=y
@@ -28,14 +30,20 @@
 CONFIG_BINFMT_ELF=y
 CONFIG_KERNEL_ELF=y
 CONFIG_BINFMT_MISC=m
-CONFIG_BINFMT_JAVA=m
+# CONFIG_BINFMT_JAVA is not set
+# CONFIG_ABSTRACT_CONSOLE is not set
 CONFIG_PMAC_CONSOLE=y
 CONFIG_MAC_KEYBOARD=y
 CONFIG_MAC_FLOPPY=y
+CONFIG_MACMOUSE=y
 CONFIG_PROC_DEVICETREE=y
 # CONFIG_XMON is not set
+CONFIG_CONTROL_VIDEO=y
+CONFIG_PLATINUM_VIDEO=y
+CONFIG_VALKYRIE_VIDEO=y
 CONFIG_ATY_VIDEO=y
 CONFIG_IMSTT_VIDEO=y
+CONFIG_CHIPS_VIDEO=y
 
 #
 # Plug and Play support
@@ -47,6 +55,10 @@
 #
 # CONFIG_BLK_DEV_FD is not set
 CONFIG_BLK_DEV_IDE=y
+
+#
+# Please see Documentation/ide.txt for help/info on IDE drives
+#
 # CONFIG_BLK_DEV_HD_IDE is not set
 CONFIG_BLK_DEV_IDEDISK=y
 CONFIG_BLK_DEV_IDECD=y
@@ -55,14 +67,20 @@
 # CONFIG_BLK_DEV_IDESCSI is not set
 # CONFIG_BLK_DEV_CMD640 is not set
 # CONFIG_BLK_DEV_RZ1000 is not set
-# CONFIG_BLK_DEV_TRITON is not set
+# CONFIG_BLK_DEV_IDEPCI is not set
 # CONFIG_IDE_CHIPSETS is not set
+
+#
+# Additional Block Devices
+#
 CONFIG_BLK_DEV_LOOP=m
+# CONFIG_BLK_DEV_NBD is not set
 # CONFIG_BLK_DEV_MD is not set
 CONFIG_BLK_DEV_RAM=y
 CONFIG_BLK_DEV_INITRD=y
 # CONFIG_BLK_DEV_XD is not set
-# CONFIG_BLK_DEV_EZ is not set
+CONFIG_PARIDE_PARPORT=m
+# CONFIG_PARIDE is not set
 # CONFIG_BLK_DEV_HD is not set
 
 #
@@ -77,6 +95,7 @@
 # CONFIG_NETLINK is not set
 # CONFIG_FIREWALL is not set
 CONFIG_NET_ALIAS=y
+# CONFIG_FILTER is not set
 CONFIG_UNIX=y
 CONFIG_INET=y
 CONFIG_IP_MULTICAST=y
@@ -85,37 +104,56 @@
 # CONFIG_IP_ACCT is not set
 # CONFIG_IP_MASQUERADE is not set
 # CONFIG_IP_ROUTER is not set
-CONFIG_NET_IPIP=m
+# CONFIG_NET_IPIP is not set
 # CONFIG_NET_IPGRE is not set
 # CONFIG_IP_MROUTE is not set
 CONFIG_IP_ALIAS=y
 # CONFIG_SYN_COOKIES is not set
+
+#
+# (it is safe to leave these untouched)
+#
 CONFIG_INET_RARP=y
 CONFIG_IP_NOSR=y
 CONFIG_SKB_LARGE=y
 # CONFIG_IPV6 is not set
+
+#
+#  
+#
 # CONFIG_IPX is not set
 # CONFIG_ATALK is not set
-# CONFIG_AX25 is not set
 # CONFIG_X25 is not set
 # CONFIG_LAPB is not set
 # CONFIG_BRIDGE is not set
 # CONFIG_LLC is not set
 # CONFIG_WAN_ROUTER is not set
+# CONFIG_NET_FASTROUTE is not set
+# CONFIG_NET_HW_FLOWCONTROL is not set
 # CONFIG_CPU_IS_SLOW is not set
 # CONFIG_NET_SCHED is not set
+# CONFIG_NET_PROFILE is not set
 
 #
 # SCSI support
 #
 CONFIG_SCSI=y
+
+#
+# SCSI support type (disk, tape, CD-ROM)
+#
 CONFIG_BLK_DEV_SD=y
 CONFIG_CHR_DEV_ST=y
 CONFIG_BLK_DEV_SR=y
 CONFIG_BLK_DEV_SR_VENDOR=y
 CONFIG_CHR_DEV_SG=y
+
+#
+# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
+#
 # CONFIG_SCSI_MULTI_LUN is not set
 CONFIG_SCSI_CONSTANTS=y
+# CONFIG_SCSI_LOGGING is not set
 
 #
 # SCSI low-level drivers
@@ -124,7 +162,12 @@
 # CONFIG_SCSI_AHA152X is not set
 # CONFIG_SCSI_AHA1542 is not set
 # CONFIG_SCSI_AHA1740 is not set
-# CONFIG_SCSI_AIC7XXX is not set
+CONFIG_SCSI_AIC7XXX=m
+# CONFIG_AIC7XXX_TAGGED_QUEUEING is not set
+# CONFIG_OVERRIDE_CMDS is not set
+# CONFIG_AIC7XXX_PAGE_ENABLE is not set
+CONFIG_AIC7XXX_PROC_STATS=y
+CONFIG_AIC7XXX_RESET_DELAY=15
 # CONFIG_SCSI_ADVANSYS is not set
 # CONFIG_SCSI_IN2000 is not set
 # CONFIG_SCSI_AM53C974 is not set
@@ -134,12 +177,16 @@
 # CONFIG_SCSI_EATA_PIO is not set
 # CONFIG_SCSI_EATA is not set
 # CONFIG_SCSI_FUTURE_DOMAIN is not set
+# CONFIG_SCSI_GDTH is not set
 # CONFIG_SCSI_GENERIC_NCR5380 is not set
 # CONFIG_SCSI_NCR53C406A is not set
 # CONFIG_SCSI_NCR53C7xx is not set
 # CONFIG_SCSI_NCR53C8XX is not set
 # CONFIG_SCSI_PPA is not set
 # CONFIG_SCSI_PAS16 is not set
+# CONFIG_SCSI_PCI2000 is not set
+# CONFIG_SCSI_PCI2220I is not set
+# CONFIG_SCSI_PSI240I is not set
 # CONFIG_SCSI_QLOGIC_FAS is not set
 # CONFIG_SCSI_QLOGIC_ISP is not set
 # CONFIG_SCSI_SEAGATE is not set
@@ -158,27 +205,47 @@
 # CONFIG_ARCNET is not set
 # CONFIG_DUMMY is not set
 # CONFIG_EQUALIZER is not set
-# CONFIG_ETHERTAP is not set
 CONFIG_NET_ETHERNET=y
 CONFIG_MACE=y
-CONFIG_DEC_ELCP=m
 # CONFIG_NET_VENDOR_3COM is not set
 # CONFIG_LANCE is not set
 # CONFIG_NET_VENDOR_SMC is not set
 # CONFIG_NET_VENDOR_RACAL is not set
+# CONFIG_RTL8139 is not set
+# CONFIG_YELLOWFIN is not set
 # CONFIG_NET_ISA is not set
-# CONFIG_NET_EISA is not set
+CONFIG_NET_EISA=y
+# CONFIG_PCNET32 is not set
+# CONFIG_AC3200 is not set
+# CONFIG_APRICOT is not set
+# CONFIG_CS89x0 is not set
+CONFIG_DE4X5=m
+CONFIG_DEC_ELCP=m
+# CONFIG_DGRS is not set
+# CONFIG_EEXPRESS_PRO100 is not set
+# CONFIG_TLAN is not set
+# CONFIG_ES3210 is not set
+# CONFIG_ZNET is not set
 # CONFIG_NET_POCKET is not set
 # CONFIG_FDDI is not set
 # CONFIG_DLCI is not set
 # CONFIG_PLIP is not set
 CONFIG_PPP=m
-# CONFIG_NET_RADIO is not set
+
+#
+# CCP compressors for PPP are only built as modules.
+#
 # CONFIG_SLIP is not set
+# CONFIG_NET_RADIO is not set
 # CONFIG_TR is not set
 # CONFIG_SHAPER is not set
 
 #
+# Amateur Radio support
+#
+# CONFIG_HAMRADIO is not set
+
+#
 # ISDN subsystem
 #
 # CONFIG_ISDN is not set
@@ -187,28 +254,68 @@
 # CD-ROM drivers (not for SCSI or IDE/ATAPI drives)
 #
 # CONFIG_CD_NO_IDESCSI is not set
+CONFIG_CDROM=y
 
 #
 # Filesystems
 #
 # CONFIG_QUOTA is not set
-CONFIG_MINIX_FS=m
+# CONFIG_MINIX_FS is not set
 CONFIG_EXT2_FS=y
 CONFIG_ISO9660_FS=y
-# CONFIG_NLS is not set
+# CONFIG_JOLIET is not set
+CONFIG_FAT_FS=m
+CONFIG_MSDOS_FS=m
+# CONFIG_UMSDOS_FS is not set
+CONFIG_VFAT_FS=m
 CONFIG_PROC_FS=y
 CONFIG_NFS_FS=y
-# CONFIG_NFSD is not set
+CONFIG_NFSD=y
 CONFIG_SUNRPC=y
 CONFIG_LOCKD=y
+# CONFIG_CODA_FS is not set
 # CONFIG_SMB_FS is not set
 # CONFIG_HPFS_FS is not set
+# CONFIG_NTFS_FS is not set
 # CONFIG_SYSV_FS is not set
 # CONFIG_AFFS_FS is not set
+CONFIG_HFS_FS=m
 # CONFIG_ROMFS_FS is not set
 CONFIG_AUTOFS_FS=y
 # CONFIG_UFS_FS is not set
+# CONFIG_ADFS_FS is not set
 CONFIG_MAC_PARTITION=y
+CONFIG_NLS=y
+
+#
+# Native Language Support
+#
+# CONFIG_NLS_CODEPAGE_437 is not set
+# CONFIG_NLS_CODEPAGE_737 is not set
+# CONFIG_NLS_CODEPAGE_775 is not set
+# CONFIG_NLS_CODEPAGE_850 is not set
+# CONFIG_NLS_CODEPAGE_852 is not set
+# CONFIG_NLS_CODEPAGE_855 is not set
+# CONFIG_NLS_CODEPAGE_857 is not set
+# CONFIG_NLS_CODEPAGE_860 is not set
+# CONFIG_NLS_CODEPAGE_861 is not set
+# CONFIG_NLS_CODEPAGE_862 is not set
+# CONFIG_NLS_CODEPAGE_863 is not set
+# CONFIG_NLS_CODEPAGE_864 is not set
+# CONFIG_NLS_CODEPAGE_865 is not set
+# CONFIG_NLS_CODEPAGE_866 is not set
+# CONFIG_NLS_CODEPAGE_869 is not set
+# CONFIG_NLS_CODEPAGE_874 is not set
+# CONFIG_NLS_ISO8859_1 is not set
+# CONFIG_NLS_ISO8859_2 is not set
+# CONFIG_NLS_ISO8859_3 is not set
+# CONFIG_NLS_ISO8859_4 is not set
+# CONFIG_NLS_ISO8859_5 is not set
+# CONFIG_NLS_ISO8859_6 is not set
+# CONFIG_NLS_ISO8859_7 is not set
+# CONFIG_NLS_ISO8859_8 is not set
+# CONFIG_NLS_ISO8859_9 is not set
+# CONFIG_NLS_KOI8_R is not set
 
 #
 # Character devices
@@ -217,20 +324,25 @@
 CONFIG_VT_CONSOLE=y
 # CONFIG_SOFTCURSOR is not set
 CONFIG_SERIAL=y
+# CONFIG_SERIAL_CONSOLE is not set
 # CONFIG_SERIAL_EXTENDED is not set
 # CONFIG_SERIAL_NONSTANDARD is not set
 # CONFIG_PRINTER is not set
 # CONFIG_MOUSE is not set
 # CONFIG_UMISC is not set
 # CONFIG_QIC02_TAPE is not set
-# CONFIG_FTAPE is not set
 # CONFIG_APM is not set
 # CONFIG_WATCHDOG is not set
 # CONFIG_RTC is not set
 # CONFIG_VIDEO_DEV is not set
-# CONFIG_VIDEO_BT848 is not set
 CONFIG_NVRAM=y
 # CONFIG_JOYSTICK is not set
+# CONFIG_MISC_RADIO is not set
+
+#
+# Ftape, the floppy tape device driver
+#
+# CONFIG_FTAPE is not set
 
 #
 # Sound
diff -u --recursive --new-file v2.1.96/linux/arch/ppc/prep_defconfig linux/arch/ppc/prep_defconfig
--- v2.1.96/linux/arch/ppc/prep_defconfig	Tue Mar 17 22:18:14 1998
+++ linux/arch/ppc/prep_defconfig	Tue Apr 14 17:33:59 1998
@@ -6,12 +6,14 @@
 # Platform support
 #
 CONFIG_PPC=y
-CONFIG_NATIVE=y
-CONFIG_MACH_SPECIFIC=y
+CONFIG_6xx=y
+# CONFIG_8xx is not set
 # CONFIG_PMAC is not set
 CONFIG_PREP=y
 # CONFIG_CHRP is not set
-CONFIG_COMMON=y
+# CONFIG_ALL_PPC is not set
+# CONFIG_MBX is not set
+CONFIG_MACH_SPECIFIC=y
 
 #
 # General setup
@@ -19,20 +21,24 @@
 CONFIG_EXPERIMENTAL=y
 CONFIG_MODULES=y
 CONFIG_MODVERSIONS=y
-CONFIG_KMOD=y
+CONFIG_KERNELD=y
 CONFIG_PCI=y
 # CONFIG_PCI_OPTIMIZE is not set
+CONFIG_PCI_OLD_PROC=y
 CONFIG_NET=y
-# CONFIG_SYSCTL is not set
+CONFIG_SYSCTL=y
 CONFIG_SYSVIPC=y
 # CONFIG_BSD_PROCESS_ACCT is not set
 CONFIG_BINFMT_ELF=y
 CONFIG_KERNEL_ELF=y
 # CONFIG_BINFMT_MISC is not set
 # CONFIG_BINFMT_JAVA is not set
+# CONFIG_PARPORT is not set
+# CONFIG_ABSTRACT_CONSOLE is not set
 # CONFIG_PMAC_CONSOLE is not set
 # CONFIG_MAC_KEYBOARD is not set
 # CONFIG_MAC_FLOPPY is not set
+# CONFIG_MACMOUSE is not set
 # CONFIG_PROC_DEVICETREE is not set
 # CONFIG_XMON is not set
 CONFIG_VGA_CONSOLE=y
@@ -55,14 +61,16 @@
 # CONFIG_BLK_DEV_IDESCSI is not set
 # CONFIG_BLK_DEV_CMD640 is not set
 # CONFIG_BLK_DEV_RZ1000 is not set
-# CONFIG_BLK_DEV_TRITON is not set
+# CONFIG_BLK_DEV_IDEPCI is not set
 # CONFIG_IDE_CHIPSETS is not set
-# CONFIG_BLK_DEV_LOOP is not set
+CONFIG_BLK_DEV_LOOP=y
+# CONFIG_BLK_DEV_NBD is not set
 # CONFIG_BLK_DEV_MD is not set
 CONFIG_BLK_DEV_RAM=y
 CONFIG_BLK_DEV_INITRD=y
 # CONFIG_BLK_DEV_XD is not set
-# CONFIG_BLK_DEV_EZ is not set
+CONFIG_PARIDE_PARPORT=y
+# CONFIG_PARIDE is not set
 # CONFIG_BLK_DEV_HD is not set
 
 #
@@ -77,32 +85,34 @@
 # CONFIG_NETLINK is not set
 # CONFIG_FIREWALL is not set
 # CONFIG_NET_ALIAS is not set
+# CONFIG_FILTER is not set
 CONFIG_UNIX=y
 CONFIG_INET=y
 # CONFIG_IP_MULTICAST is not set
 # CONFIG_IP_ADVANCED_ROUTER is not set
 # CONFIG_IP_PNP is not set
-# CONFIG_IP_ACCT is not set
-# CONFIG_IP_MASQUERADE is not set
+CONFIG_IP_ACCT=y
 # CONFIG_IP_ROUTER is not set
 # CONFIG_NET_IPIP is not set
 # CONFIG_NET_IPGRE is not set
 # CONFIG_IP_ALIAS is not set
-# CONFIG_SYN_COOKIES is not set
+CONFIG_SYN_COOKIES=y
 CONFIG_INET_RARP=y
 # CONFIG_IP_NOSR is not set
 CONFIG_SKB_LARGE=y
 # CONFIG_IPV6 is not set
 # CONFIG_IPX is not set
 # CONFIG_ATALK is not set
-# CONFIG_AX25 is not set
 # CONFIG_X25 is not set
 # CONFIG_LAPB is not set
 # CONFIG_BRIDGE is not set
 # CONFIG_LLC is not set
 # CONFIG_WAN_ROUTER is not set
+# CONFIG_NET_FASTROUTE is not set
+# CONFIG_NET_HW_FLOWCONTROL is not set
 # CONFIG_CPU_IS_SLOW is not set
 # CONFIG_NET_SCHED is not set
+# CONFIG_NET_PROFILE is not set
 
 #
 # SCSI support
@@ -115,6 +125,7 @@
 # CONFIG_CHR_DEV_SG is not set
 # CONFIG_SCSI_MULTI_LUN is not set
 # CONFIG_SCSI_CONSTANTS is not set
+# CONFIG_SCSI_LOGGING is not set
 
 #
 # SCSI low-level drivers
@@ -133,6 +144,7 @@
 # CONFIG_SCSI_EATA_PIO is not set
 # CONFIG_SCSI_EATA is not set
 # CONFIG_SCSI_FUTURE_DOMAIN is not set
+# CONFIG_SCSI_GDTH is not set
 # CONFIG_SCSI_GENERIC_NCR5380 is not set
 # CONFIG_SCSI_NCR53C406A is not set
 # CONFIG_SCSI_NCR53C7xx is not set
@@ -143,8 +155,10 @@
 CONFIG_SCSI_NCR53C8XX_MAX_TAGS=4
 CONFIG_SCSI_NCR53C8XX_SYNC=5
 # CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT is not set
-# CONFIG_SCSI_PPA is not set
 # CONFIG_SCSI_PAS16 is not set
+# CONFIG_SCSI_PCI2000 is not set
+# CONFIG_SCSI_PCI2220I is not set
+# CONFIG_SCSI_PSI240I is not set
 # CONFIG_SCSI_QLOGIC_FAS is not set
 # CONFIG_SCSI_QLOGIC_ISP is not set
 # CONFIG_SCSI_SEAGATE is not set
@@ -152,6 +166,7 @@
 # CONFIG_SCSI_T128 is not set
 # CONFIG_SCSI_U14_34F is not set
 # CONFIG_SCSI_ULTRASTOR is not set
+# CONFIG_SCSI_DEBUG is not set
 # CONFIG_SCSI_MESH is not set
 # CONFIG_SCSI_MAC53C94 is not set
 
@@ -162,12 +177,13 @@
 # CONFIG_ARCNET is not set
 # CONFIG_DUMMY is not set
 # CONFIG_EQUALIZER is not set
-# CONFIG_ETHERTAP is not set
 CONFIG_NET_ETHERNET=y
 # CONFIG_NET_VENDOR_3COM is not set
 CONFIG_LANCE=y
 # CONFIG_NET_VENDOR_SMC is not set
 # CONFIG_NET_VENDOR_RACAL is not set
+# CONFIG_RTL8139 is not set
+# CONFIG_YELLOWFIN is not set
 # CONFIG_NET_ISA is not set
 CONFIG_NET_EISA=y
 CONFIG_PCNET32=y
@@ -184,14 +200,18 @@
 # CONFIG_NET_POCKET is not set
 # CONFIG_FDDI is not set
 # CONFIG_DLCI is not set
-# CONFIG_PLIP is not set
-CONFIG_PPP=m
-# CONFIG_NET_RADIO is not set
+CONFIG_PPP=y
 # CONFIG_SLIP is not set
+# CONFIG_NET_RADIO is not set
 # CONFIG_TR is not set
 # CONFIG_SHAPER is not set
 
 #
+# Amateur Radio support
+#
+# CONFIG_HAMRADIO is not set
+
+#
 # ISDN subsystem
 #
 # CONFIG_ISDN is not set
@@ -200,6 +220,7 @@
 # CD-ROM drivers (not for SCSI or IDE/ATAPI drives)
 #
 # CONFIG_CD_NO_IDESCSI is not set
+CONFIG_CDROM=y
 
 #
 # Filesystems
@@ -208,29 +229,59 @@
 # CONFIG_MINIX_FS is not set
 CONFIG_EXT2_FS=y
 CONFIG_ISO9660_FS=y
-# CONFIG_JOLIET is not set
-# CONFIG_FAT_FS is not set
-# CONFIG_MSDOS_FS is not set
+CONFIG_JOLIET=y
+CONFIG_FAT_FS=y
+CONFIG_MSDOS_FS=y
 # CONFIG_UMSDOS_FS is not set
-# CONFIG_VFAT_FS is not set
+CONFIG_VFAT_FS=y
 CONFIG_PROC_FS=y
 CONFIG_NFS_FS=y
-# CONFIG_NFSD is not set
+CONFIG_NFSD=y
 CONFIG_SUNRPC=y
 CONFIG_LOCKD=y
+# CONFIG_CODA_FS is not set
 # CONFIG_SMB_FS is not set
 # CONFIG_HPFS_FS is not set
+# CONFIG_NTFS_FS is not set
 # CONFIG_SYSV_FS is not set
 # CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
 # CONFIG_ROMFS_FS is not set
 # CONFIG_AUTOFS_FS is not set
 # CONFIG_UFS_FS is not set
+# CONFIG_ADFS_FS is not set
 # CONFIG_MAC_PARTITION is not set
+CONFIG_NLS=y
 
 #
 # Native Language Support
 #
-# CONFIG_NLS is not set
+# CONFIG_NLS_CODEPAGE_437 is not set
+# CONFIG_NLS_CODEPAGE_737 is not set
+# CONFIG_NLS_CODEPAGE_775 is not set
+# CONFIG_NLS_CODEPAGE_850 is not set
+# CONFIG_NLS_CODEPAGE_852 is not set
+# CONFIG_NLS_CODEPAGE_855 is not set
+# CONFIG_NLS_CODEPAGE_857 is not set
+# CONFIG_NLS_CODEPAGE_860 is not set
+# CONFIG_NLS_CODEPAGE_861 is not set
+# CONFIG_NLS_CODEPAGE_862 is not set
+# CONFIG_NLS_CODEPAGE_863 is not set
+# CONFIG_NLS_CODEPAGE_864 is not set
+# CONFIG_NLS_CODEPAGE_865 is not set
+# CONFIG_NLS_CODEPAGE_866 is not set
+# CONFIG_NLS_CODEPAGE_869 is not set
+# CONFIG_NLS_CODEPAGE_874 is not set
+# CONFIG_NLS_ISO8859_1 is not set
+# CONFIG_NLS_ISO8859_2 is not set
+# CONFIG_NLS_ISO8859_3 is not set
+# CONFIG_NLS_ISO8859_4 is not set
+# CONFIG_NLS_ISO8859_5 is not set
+# CONFIG_NLS_ISO8859_6 is not set
+# CONFIG_NLS_ISO8859_7 is not set
+# CONFIG_NLS_ISO8859_8 is not set
+# CONFIG_NLS_ISO8859_9 is not set
+# CONFIG_NLS_KOI8_R is not set
 
 #
 # Character devices
@@ -239,14 +290,9 @@
 CONFIG_VT_CONSOLE=y
 # CONFIG_SOFTCURSOR is not set
 CONFIG_SERIAL=y
-CONFIG_SERIAL_EXTENDED=y
-# CONFIG_SERIAL_MANY_PORTS is not set
-# CONFIG_SERIAL_SHARE_IRQ is not set
-# CONFIG_SERIAL_MULTIPORT is not set
-# CONFIG_HUB6 is not set
 CONFIG_SERIAL_CONSOLE=y
+# CONFIG_SERIAL_EXTENDED is not set
 # CONFIG_SERIAL_NONSTANDARD is not set
-# CONFIG_PRINTER is not set
 CONFIG_MOUSE=y
 # CONFIG_ATIXL_BUSMOUSE is not set
 # CONFIG_BUSMOUSE is not set
@@ -256,14 +302,18 @@
 # CONFIG_PC110_PAD is not set
 # CONFIG_UMISC is not set
 # CONFIG_QIC02_TAPE is not set
-# CONFIG_FTAPE is not set
 # CONFIG_APM is not set
 # CONFIG_WATCHDOG is not set
 # CONFIG_RTC is not set
 # CONFIG_VIDEO_DEV is not set
-# CONFIG_VIDEO_BT848 is not set
 # CONFIG_NVRAM is not set
 # CONFIG_JOYSTICK is not set
+# CONFIG_MISC_RADIO is not set
+
+#
+# Ftape, the floppy tape device driver
+#
+# CONFIG_FTAPE is not set
 
 #
 # Sound
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/Makefile linux/arch/sparc/Makefile
--- v2.1.96/linux/arch/sparc/Makefile	Wed Jul 16 19:22:50 1997
+++ linux/arch/sparc/Makefile	Tue Apr 14 17:44:18 1998
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.29 1997/07/11 11:05:23 jj Exp $
+# $Id: Makefile,v 1.34 1998/04/06 16:09:34 jj Exp $
 # sparc/Makefile
 #
 # Makefile for the architecture dependent flags and dependencies on the
@@ -23,14 +23,18 @@
 
 HEAD := arch/sparc/kernel/head.o arch/sparc/kernel/init_task.o
 
-SUBDIRS := $(SUBDIRS) arch/sparc/kernel arch/sparc/lib arch/sparc/mm \
-	arch/sparc/prom
+# Note arch/sparc/mm has to be the last subdir
+SUBDIRS := $(SUBDIRS) arch/sparc/kernel arch/sparc/lib arch/sparc/prom \
+	arch/sparc/mm
 
 CORE_FILES := arch/sparc/kernel/kernel.o arch/sparc/mm/mm.o $(CORE_FILES)
 
 LIBS := $(TOPDIR)/lib/lib.a $(LIBS) $(TOPDIR)/arch/sparc/prom/promlib.a \
 	$(TOPDIR)/arch/sparc/lib/lib.a
 
+SUBDIRS += arch/sparc/math-emu
+CORE_FILES += arch/sparc/math-emu/math-emu.o
+
 ifdef CONFIG_AP1000
 SUBDIRS := $(SUBDIRS) arch/sparc/ap1000 mpp
 CORE_FILES := $(TOPDIR)/arch/sparc/ap1000/ap1000lib.o \
@@ -40,11 +44,30 @@
 endif
 
 archclean:
+	-$(MAKE) -C arch/sparc/boot archclean
+	-$(MAKE) -C arch/sparc/math-emu cleansymlinks
 
 archdep:
+	-$(MAKE) -C arch/sparc/math-emu symlinks
 
 check_asm:
 	$(MAKE) -C arch/sparc/kernel check_asm
 
 tftpboot.img:
 	$(MAKE) -C arch/sparc/boot tftpboot.img
+
+vmlinux.o: $(CONFIGURATION) init/main.o init/version.o linuxsubdirs
+	$(LD) -r $(VMLINUX.OBJS) -o vmlinux.o
+
+arch/sparc/boot/btfix.s: arch/sparc/boot/btfixupprep vmlinux.o
+	$(OBJDUMP) -x vmlinux.o | arch/sparc/boot/btfixupprep > arch/sparc/boot/btfix.s
+
+arch/sparc/boot/btfix.o: arch/sparc/boot/btfix.s
+	$(CC) -c -o arch/sparc/boot/btfix.o arch/sparc/boot/btfix.s
+
+arch/sparc/boot/btfixupprep: arch/sparc/boot/btfixupprep.c
+	$(MAKE) -C arch/sparc/boot btfixupprep
+
+vmlinux: arch/sparc/boot/btfix.o
+	$(LD) $(LINKFLAGS) vmlinux.o arch/sparc/boot/btfix.o -o vmlinux
+	$(NM) vmlinux | grep -v '\(compiled\)\|\(\.o$$\)\|\( [aU] \)\|\(\.\.ng$$\)\|\(LASH[RL]DI\)' | sort > System.map
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/ap1000/apmmu.c linux/arch/sparc/ap1000/apmmu.c
--- v2.1.96/linux/arch/sparc/ap1000/apmmu.c	Mon Feb 23 18:12:03 1998
+++ linux/arch/sparc/ap1000/apmmu.c	Tue Apr 14 17:44:18 1998
@@ -36,16 +36,10 @@
 #include <asm/viking.h>
 
 
-static unsigned long (*mmu_getpage)(void);
-static void (*ctxd_set)(ctxd_t *ctxp, pgd_t *pgdp);
-static void (*pmd_set)(pmd_t *pmdp, pte_t *ptep);
-
-static void (*flush_page_for_dma)(unsigned long page);
-static void (*flush_cache_page_to_uncache)(unsigned long page);
-static void (*flush_tlb_page_for_cbit)(unsigned long page);
-
 extern void mc_tlb_flush_all(void);
 
+static void poke_viking(void);
+static void viking_flush_tlb_page_for_cbit)(unsigned long page);
 
 static struct apmmu_stats {
 	int invall;
@@ -103,11 +97,6 @@
 static unsigned int apmmu_pmd_align(unsigned int addr) { return APMMU_PMD_ALIGN(addr); }
 static unsigned int apmmu_pgdir_align(unsigned int addr) { return APMMU_PGDIR_ALIGN(addr); }
 
-static unsigned long apmmu_vmalloc_start(void)
-{
-	return APMMU_VMALLOC_START;
-}
-
 static inline int apmmu_device_memory(unsigned long x) 
 {
 	return ((x & 0xF0000000) != 0);
@@ -152,13 +141,6 @@
 
 static void apmmu_pgd_clear(pgd_t * pgdp)     { set_pte((pte_t *)pgdp, __pte(0)); }
 
-static int apmmu_pte_write(pte_t pte)         { return pte_val(pte) & APMMU_WRITE; }
-static int apmmu_pte_dirty(pte_t pte)         { return pte_val(pte) & APMMU_DIRTY; }
-static int apmmu_pte_young(pte_t pte)         { return pte_val(pte) & APMMU_REF; }
-
-static pte_t apmmu_pte_wrprotect(pte_t pte)   { return __pte(pte_val(pte) & ~APMMU_WRITE);}
-static pte_t apmmu_pte_mkclean(pte_t pte)     { return __pte(pte_val(pte) & ~APMMU_DIRTY);}
-static pte_t apmmu_pte_mkold(pte_t pte)       { return __pte(pte_val(pte) & ~APMMU_REF);}
 static pte_t apmmu_pte_mkwrite(pte_t pte)     { return __pte(pte_val(pte) | APMMU_WRITE);}
 static pte_t apmmu_pte_mkdirty(pte_t pte)     { return __pte(pte_val(pte) | APMMU_DIRTY);}
 static pte_t apmmu_pte_mkyoung(pte_t pte)     { return __pte(pte_val(pte) | APMMU_REF);}
@@ -221,7 +203,7 @@
 {
 	if(tsk->mm->context != NO_CONTEXT) {
 		flush_cache_mm(current->mm);
-		ctxd_set(&apmmu_context_table[tsk->mm->context], pgdp);
+		apmmu_ctxd_set(&apmmu_context_table[tsk->mm->context], pgdp);
 		flush_tlb_mm(current->mm);
 	}
 }
@@ -311,8 +293,6 @@
 	return retval;
 }
 
-
-
 static inline void apmmu_uncache_page(unsigned long addr)
 {
 	pgd_t *pgdp = apmmu_pgd_offset(init_task.mm, addr);
@@ -330,9 +310,8 @@
 		}
 	}
 
-	flush_cache_page_to_uncache(addr);
 	set_pte(ptep, __pte((pte_val(*ptep) & ~APMMU_CACHE)));
-	flush_tlb_page_for_cbit(addr);
+	viking_flush_tlb_page_for_cbit(addr);
 }
 
 static inline void apmmu_recache_page(unsigned long addr)
@@ -352,10 +331,10 @@
 		}
 	}
 	set_pte(ptep, __pte((pte_val(*ptep) | APMMU_CACHE)));
-	flush_tlb_page_for_cbit(addr);
+	viking_flush_tlb_page_for_cbit(addr);
 }
 
-static unsigned long apmmu_getpage(void)
+static inline unsigned long apmmu_getpage(void)
 {
 	unsigned long page = get_free_page(GFP_KERNEL);
 
@@ -368,13 +347,44 @@
 }
 
 /* The easy versions. */
-#define NEW_PGD() (pgd_t *) mmu_getpage()
-#define NEW_PMD() (pmd_t *) mmu_getpage()
-#define NEW_PTE() (pte_t *) mmu_getpage()
+#define NEW_PGD() (pgd_t *) apmmu_getpage()
+#define NEW_PMD() (pmd_t *) apmmu_getpage()
+#define NEW_PTE() (pte_t *) apmmu_getpage()
 #define FREE_PGD(chunk) apmmu_putpage((unsigned long)(chunk))
 #define FREE_PMD(chunk) apmmu_putpage((unsigned long)(chunk))
 #define FREE_PTE(chunk) apmmu_putpage((unsigned long)(chunk))
 
+static pte_t *apmmu_get_pte_fast(void)
+{
+	return (pte_t *)0;
+}
+
+static pmd_t *apmmu_get_pmd_fast(void)
+{
+	return (pmd_t *)0;
+}
+
+static pgd_t *apmmu_get_pgd_fast(void)
+{
+	return (pgd_t *)0;
+}
+
+static void apmmu_free_pte_slow(pte_t *pte)
+{
+/* TBD */
+}
+
+static void apmmu_free_pmd_slow(pmd_t *pmd)
+{
+/* TBD */
+}
+
+static void apmmu_free_pgd_slow(pgd_t *pgd)
+{
+/* TBD */
+}
+
+
 /*
  * Allocate and free page tables. The xxx_kernel() versions are
  * used to allocate a kernel page table - this turns on ASN bits
@@ -392,17 +402,17 @@
 		pte_t *page = NEW_PTE();
 		if(apmmu_pmd_none(*pmd)) {
 			if(page) {
-				pmd_set(pmd, page);
+				apmmu_pmd_set(pmd, page);
 				return page + address;
 			}
-			pmd_set(pmd, BAD_PAGETABLE);
+			apmmu_pmd_set(pmd, BAD_PAGETABLE);
 			return NULL;
 		}
 		FREE_PTE(page);
 	}
 	if(apmmu_pmd_bad(*pmd)) {
 		printk("Bad pmd in pte_alloc: %08lx\n", pmd_val(*pmd));
-		pmd_set(pmd, BAD_PAGETABLE);
+		apmmu_pmd_set(pmd, BAD_PAGETABLE);
 		return NULL;
 	}
 	return (pte_t *) apmmu_pmd_page(*pmd) + address;
@@ -449,17 +459,17 @@
 		pte_t *page = NEW_PTE();
 		if(apmmu_pmd_none(*pmd)) {
 			if(page) {
-				pmd_set(pmd, page);
+				apmmu_pmd_set(pmd, page);
 				return page + address;
 			}
-			pmd_set(pmd, BAD_PAGETABLE);
+			apmmu_pmd_set(pmd, BAD_PAGETABLE);
 			return NULL;
 		}
 		FREE_PTE(page);
 	}
 	if(apmmu_pmd_bad(*pmd)) {
 		printk("Bad pmd in pte_alloc: %08lx\n", pmd_val(*pmd));
-		pmd_set(pmd, BAD_PAGETABLE);
+		apmmu_pmd_set(pmd, BAD_PAGETABLE);
 		return NULL;
 	}
 	return ((pte_t *) apmmu_pmd_page(*pmd)) + address;
@@ -525,7 +535,7 @@
 	struct mm_struct *mm = tsk->mm;
 	struct ctx_list *ctxp;
 
-        if (tsk->taskid >= MPP_TASK_BASE) {
+	if (tsk->taskid >= MPP_TASK_BASE) {
 		mm->context = MPP_CONTEXT_BASE + (tsk->taskid - MPP_TASK_BASE);
 		return;
 	}
@@ -570,7 +580,7 @@
 	if(tsk->mm->context == NO_CONTEXT) {
 		alloc_context(tsk);
 		flush_cache_mm(current->mm);
-		ctxd_set(&apmmu_context_table[tsk->mm->context], tsk->mm->pgd);
+		apmmu_ctxd_set(&apmmu_context_table[tsk->mm->context], tsk->mm->pgd);
 		flush_tlb_mm(current->mm);
 	}
 	apmmu_set_context(tsk->mm->context);
@@ -590,29 +600,11 @@
 	return (struct task_struct *) kmalloc(sizeof(struct task_struct), GFP_KERNEL);
 }
 
-static unsigned long apmmu_alloc_kernel_stack(struct task_struct *tsk)
-{
-	unsigned long kstk = __get_free_pages(GFP_KERNEL, 1);
-
-	if(!kstk)
-		kstk = (unsigned long) vmalloc(PAGE_SIZE << 1);
-
-	return kstk;
-}
-
 static void apmmu_free_task_struct(struct task_struct *tsk)
 {
 	kfree(tsk);
 }
 
-static void apmmu_free_kernel_stack(unsigned long stack)
-{
-	if(stack < VMALLOC_START)
-		free_pages(stack, 1);
-	else
-		vfree((char *)stack);
-}
-
 static void apmmu_null_func(void)
 {
 }
@@ -925,22 +917,20 @@
 extern int physmem_mapped_contig;
 extern int linux_num_cpus;
 
-void (*poke_apmmu)(void);
-
 __initfunc(unsigned long apmmu_paging_init(unsigned long start_mem, unsigned long end_mem))
 {
 	int i;
 
 	physmem_mapped_contig = 1;   /* for init.c:taint_real_pages()   */
 
-        num_contexts = AP_NUM_CONTEXTS;
+	num_contexts = AP_NUM_CONTEXTS;
 	mempool = PAGE_ALIGN(start_mem);
 	memset(swapper_pg_dir, 0, PAGE_SIZE);
 
 	apmmu_allocate_ptable_skeleton(KERNBASE, end_mem);
 	mempool = PAGE_ALIGN(mempool);
 	map_kernel();
-        ap_setup_mappings();
+	ap_setup_mappings();
 
 	/* the MSC wants this aligned on a 16k boundary */
 	apmmu_context_table = 
@@ -950,14 +940,14 @@
 			   num_contexts*sizeof(ctxd_t));
 	apmmu_ctx_table_phys = (ctxd_t *) apmmu_v2p((unsigned long) apmmu_context_table);
 	for(i = 0; i < num_contexts; i++)
-		ctxd_set(&apmmu_context_table[i], swapper_pg_dir);
+		apmmu_ctxd_set(&apmmu_context_table[i], swapper_pg_dir);
 
 	start_mem = PAGE_ALIGN(mempool);
 
 	flush_cache_all();
 	apmmu_set_ctable_ptr((unsigned long) apmmu_ctx_table_phys);
 	flush_tlb_all();
-	poke_apmmu();
+	poke_viking();
 
 	/* on the AP we don't put the top few contexts into the free
 	   context list as these are reserved for parallel tasks */
@@ -967,11 +957,10 @@
 	return PAGE_ALIGN(start_mem);
 }
 
-static char apmmuinfo[512];
-
-static char *apmmu_mmu_info(void)
+static int apmmu_mmu_info(char *buf)
 {
-	sprintf(apmmuinfo, "MMU type\t: %s\n"
+	return sprintf(buf, 
+		"MMU type\t: %s\n"
 		"invall\t\t: %d\n"
 		"invmm\t\t: %d\n"
 		"invrnge\t\t: %d\n"
@@ -984,35 +973,12 @@
 		module_stats.invpg,
 		num_contexts
 		);
-	return apmmuinfo;
 }
 
 static void apmmu_update_mmu_cache(struct vm_area_struct * vma, unsigned long address, pte_t pte)
 {
 }
 
-static void apmmu_exit_hook(void)
-{
-	struct mm_struct *mm = current->mm;
-
-	if(mm->context != NO_CONTEXT && mm->count == 1) {
-		ctxd_set(&apmmu_context_table[mm->context], swapper_pg_dir);
-		viking_flush_tlb_mm(mm);
-		free_context(mm->context);
-		mm->context = NO_CONTEXT;
-	}
-}
-
-static void apmmu_flush_hook(void)
-{
-	if(current->tss.flags & SPARC_FLAG_KTHREAD) {
-		alloc_context(current);
-		ctxd_set(&apmmu_context_table[current->mm->context], current->mm->pgd);
-		viking_flush_tlb_mm(current->mm);
-		apmmu_set_context(current->mm->context);
-	}
-}
-
 __initfunc(static void poke_viking(void))
 {
 	unsigned long mreg = apmmu_get_mmureg();
@@ -1020,7 +986,7 @@
 	mreg |= VIKING_SPENABLE;
 	mreg |= (VIKING_ICENABLE | VIKING_DCENABLE);
 	mreg &= ~VIKING_ACENABLE;
-        mreg &= ~VIKING_SBENABLE;
+	mreg &= ~VIKING_SBENABLE;
 	mreg |= VIKING_TCENABLE;
 	apmmu_set_mmureg(mreg);
 }
@@ -1029,24 +995,18 @@
 {
 	apmmu_name = "TI Viking/AP1000";
 
-	flush_cache_page_to_uncache = apmmu_null_func;
-	flush_page_for_dma = apmmu_null_func;
-
-	flush_cache_all = apmmu_null_func;
-	flush_cache_mm = apmmu_null_func;
-	flush_cache_page = apmmu_null_func;
-	flush_cache_range = apmmu_null_func;
-
-	flush_tlb_all = viking_flush_tlb_all;
-	flush_tlb_mm = viking_flush_tlb_mm;
-	flush_tlb_page = viking_flush_tlb_page;
-	flush_tlb_range = viking_flush_tlb_range;
-
-	flush_page_to_ram = apmmu_null_func;
-	flush_sig_insns = apmmu_null_func;
-	flush_tlb_page_for_cbit = viking_flush_tlb_page_for_cbit;
+	BTFIXUPSET_CALL(flush_cache_all, apmmu_null_func, BTFIXUPCALL_NOP);
+	BTFIXUPSET_CALL(flush_cache_mm, apmmu_null_func, BTFIXUPCALL_NOP);
+	BTFIXUPSET_CALL(flush_cache_page, apmmu_null_func, BTFIXUPCALL_NOP);
+	BTFIXUPSET_CALL(flush_cache_range, apmmu_null_func, BTFIXUPCALL_NOP);
+
+	BTFIXUPSET_CALL(flush_tlb_all, viking_flush_tlb_all, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_tlb_mm, viking_flush_tlb_mm, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_tlb_page, viking_flush_tlb_page, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_tlb_range, viking_flush_tlb_range, BTFIXUPCALL_NORM);
 
-	poke_apmmu = poke_viking;
+	BTFIXUPSET_CALL(flush_page_to_ram, apmmu_null_func, BTFIXUPCALL_NOP);
+	BTFIXUPSET_CALL(flush_sig_insns, apmmu_null_func, BTFIXUPCALL_NOP);
 }
 
 
@@ -1062,7 +1022,7 @@
 		iaddr = &(insn); \
 		daddr = &(dest); \
 		*iaddr = SPARC_BRANCH((unsigned long) daddr, (unsigned long) iaddr); \
-        } while(0);
+	} while(0);
 
 __initfunc(static void patch_window_trap_handlers(void))
 {
@@ -1077,113 +1037,109 @@
 	PATCH_BRANCH(sparc_ttable[SP_TRAP_DACC].inst_three, srmmu_fault);
 }
 
-/* Load up routines and constants for sun4m mmu */
+/* Load up routines and constants for apmmu */
 __initfunc(void ld_mmu_apmmu(void))
 {
 	/* First the constants */
-	pmd_shift = APMMU_PMD_SHIFT;
-	pmd_size = APMMU_PMD_SIZE;
-	pmd_mask = APMMU_PMD_MASK;
-	pgdir_shift = APMMU_PGDIR_SHIFT;
-	pgdir_size = APMMU_PGDIR_SIZE;
-	pgdir_mask = APMMU_PGDIR_MASK;
-
-	ptrs_per_pte = APMMU_PTRS_PER_PTE;
-	ptrs_per_pmd = APMMU_PTRS_PER_PMD;
-	ptrs_per_pgd = APMMU_PTRS_PER_PGD;
-
-	page_none = APMMU_PAGE_NONE;
-	page_shared = APMMU_PAGE_SHARED;
-	page_copy = APMMU_PAGE_COPY;
-	page_readonly = APMMU_PAGE_RDONLY;
-	page_kernel = APMMU_PAGE_KERNEL;
+	BTFIXUPSET_SIMM13(pmd_shift, APMMU_PMD_SHIFT);
+	BTFIXUPSET_SETHI(pmd_size, APMMU_PMD_SIZE);
+	BTFIXUPSET_SETHI(pmd_mask, APMMU_PMD_MASK);
+	BTFIXUPSET_SIMM13(pgdir_shift, APMMU_PGDIR_SHIFT);
+	BTFIXUPSET_SETHI(pgdir_size, APMMU_PGDIR_SIZE);
+	BTFIXUPSET_SETHI(pgdir_mask, APMMU_PGDIR_MASK);
+
+	BTFIXUPSET_SIMM13(ptrs_per_pte, APMMU_PTRS_PER_PTE);
+	BTFIXUPSET_SIMM13(ptrs_per_pmd, APMMU_PTRS_PER_PMD);
+	BTFIXUPSET_SIMM13(ptrs_per_pgd, APMMU_PTRS_PER_PGD);
+
+	BTFIXUPSET_INT(page_none, pgprot_val(APMMU_PAGE_NONE));
+	BTFIXUPSET_INT(page_shared, pgprot_val(APMMU_PAGE_SHARED));
+	BTFIXUPSET_INT(page_copy, pgprot_val(APMMU_PAGE_COPY));
+	BTFIXUPSET_INT(page_readonly, pgprot_val(APMMU_PAGE_RDONLY));
+	BTFIXUPSET_INT(page_kernel, pgprot_val(APMMU_PAGE_KERNEL));
 	pg_iobits = APMMU_VALID | APMMU_WRITE | APMMU_REF;
 	    
 	/* Functions */
-	mmu_getpage = apmmu_getpage;
-	set_pte = apmmu_set_pte_cacheable;
-	switch_to_context = apmmu_switch_to_context;
-	pmd_align = apmmu_pmd_align;
-	pgdir_align = apmmu_pgdir_align;
-	vmalloc_start = apmmu_vmalloc_start;
-
-	pte_page = apmmu_pte_page;
-	pmd_page = apmmu_pmd_page;
-	pgd_page = apmmu_pgd_page;
-
-	sparc_update_rootmmu_dir = apmmu_update_rootmmu_dir;
-
-	pte_none = apmmu_pte_none;
-	pte_present = apmmu_pte_present;
-	pte_clear = apmmu_pte_clear;
-
-	pmd_none = apmmu_pmd_none;
-	pmd_bad = apmmu_pmd_bad;
-	pmd_present = apmmu_pmd_present;
-	pmd_clear = apmmu_pmd_clear;
-
-	pgd_none = apmmu_pgd_none;
-	pgd_bad = apmmu_pgd_bad;
-	pgd_present = apmmu_pgd_present;
-	pgd_clear = apmmu_pgd_clear;
-
-	mk_pte = apmmu_mk_pte;
-	mk_pte_phys = apmmu_mk_pte_phys;
-	pgd_set = apmmu_pgd_set;
-	mk_pte_io = apmmu_mk_pte_io;
-	pte_modify = apmmu_pte_modify;
-	pgd_offset = apmmu_pgd_offset;
-	pmd_offset = apmmu_pmd_offset;
-	pte_offset = apmmu_pte_offset;
-	pte_free_kernel = apmmu_pte_free_kernel;
-	pmd_free_kernel = apmmu_pmd_free_kernel;
-	pte_alloc_kernel = apmmu_pte_alloc_kernel;
-	pmd_alloc_kernel = apmmu_pmd_alloc_kernel;
-	pte_free = apmmu_pte_free;
-	pte_alloc = apmmu_pte_alloc;
-	pmd_free = apmmu_pmd_free;
-	pmd_alloc = apmmu_pmd_alloc;
-	pgd_free = apmmu_pgd_free;
-	pgd_alloc = apmmu_pgd_alloc;
-	pgd_flush = apmmu_pgd_flush;
-
-	pte_write = apmmu_pte_write;
-	pte_dirty = apmmu_pte_dirty;
-	pte_young = apmmu_pte_young;
-	pte_wrprotect = apmmu_pte_wrprotect;
-	pte_mkclean = apmmu_pte_mkclean;
-	pte_mkold = apmmu_pte_mkold;
-	pte_mkwrite = apmmu_pte_mkwrite;
-	pte_mkdirty = apmmu_pte_mkdirty;
-	pte_mkyoung = apmmu_pte_mkyoung;
-	update_mmu_cache = apmmu_update_mmu_cache;
-	mmu_exit_hook = apmmu_exit_hook;
-	mmu_flush_hook = apmmu_flush_hook;
-	mmu_lockarea = apmmu_lockarea;
-	mmu_unlockarea = apmmu_unlockarea;
-
-	mmu_get_scsi_one = NULL;
-	mmu_get_scsi_sgl = NULL;
-	mmu_release_scsi_one = NULL;
-	mmu_release_scsi_sgl = NULL;
-
-	mmu_info = apmmu_mmu_info;
-        mmu_v2p = apmmu_v2p;
-        mmu_p2v = apmmu_p2v;
+	BTFIXUPSET_CALL(get_pte_fast, apmmu_get_pte_fast, BTFIXUPCALL_RETINT(0));
+	BTFIXUPSET_CALL(get_pmd_fast, apmmu_get_pmd_fast, BTFIXUPCALL_RETINT(0));
+	BTFIXUPSET_CALL(get_pgd_fast, apmmu_get_pgd_fast, BTFIXUPCALL_RETINT(0));
+	BTFIXUPSET_CALL(free_pte_slow, apmmu_free_pte_slow, BTFIXUPCALL_NOP);
+	BTFIXUPSET_CALL(free_pmd_slow, apmmu_free_pmd_slow, BTFIXUPCALL_NOP);
+	BTFIXUPSET_CALL(free_pgd_slow, apmmu_free_pgd_slow, BTFIXUPCALL_NOP);
+
+	BTFIXUPSET_CALL(set_pte, apmmu_set_pte_cacheable, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(switch_to_context, apmmu_switch_to_context, BTFIXUPCALL_NORM);
+
+	BTFIXUPSET_CALL(pte_page, apmmu_pte_page, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pmd_page, apmmu_pmd_page, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pgd_page, apmmu_pgd_page, BTFIXUPCALL_NORM);
+
+	BTFIXUPSET_CALL(sparc_update_rootmmu_dir, apmmu_update_rootmmu_dir, BTFIXUPCALL_NORM);
+
+	BTFIXUPSET_SETHI(none_mask, 0xF0000000);
+
+	BTFIXUPSET_CALL(pte_present, apmmu_pte_present, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pte_clear, apmmu_pte_clear, BTFIXUPCALL_NORM);
+
+	BTFIXUPSET_CALL(pmd_bad, apmmu_pmd_bad, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pmd_present, apmmu_pmd_present, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pmd_clear, apmmu_pmd_clear, BTFIXUPCALL_NORM);
+
+	BTFIXUPSET_CALL(pgd_none, apmmu_pgd_none, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pgd_bad, apmmu_pgd_bad, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pgd_present, apmmu_pgd_present, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pgd_clear, apmmu_pgd_clear, BTFIXUPCALL_NORM);
+
+	BTFIXUPSET_CALL(mk_pte, apmmu_mk_pte, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(mk_pte_phys, apmmu_mk_pte_phys, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(mk_pte_io, apmmu_mk_pte_io, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pgd_set, apmmu_pgd_set, BTFIXUPCALL_NORM);
+	
+	BTFIXUPSET_INT(pte_modify_mask, APMMU_CHG_MASK);
+	BTFIXUPSET_CALL(pgd_offset, apmmu_pgd_offset, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pmd_offset, apmmu_pmd_offset, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pte_offset, apmmu_pte_offset, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pte_free_kernel, apmmu_pte_free_kernel, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pmd_free_kernel, apmmu_pmd_free_kernel, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pte_alloc_kernel, apmmu_pte_alloc_kernel, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pmd_alloc_kernel, apmmu_pmd_alloc_kernel, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pte_free, apmmu_pte_free, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pte_alloc, apmmu_pte_alloc, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pmd_free, apmmu_pmd_free, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pmd_alloc, apmmu_pmd_alloc, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pgd_free, apmmu_pgd_free, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pgd_alloc, apmmu_pgd_alloc, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pgd_flush, apmmu_pgd_flush, BTFIXUPCALL_NORM);
+
+	BTFIXUPSET_HALF(pte_writei, APMMU_WRITE);
+	BTFIXUPSET_HALF(pte_dirtyi, APMMU_DIRTY);
+	BTFIXUPSET_HALF(pte_youngi, APMMU_REF);
+	BTFIXUPSET_HALF(pte_wrprotecti, APMMU_WRITE);
+	BTFIXUPSET_HALF(pte_mkcleani, APMMU_DIRTY);
+	BTFIXUPSET_HALF(pte_mkoldi, APMMU_REF);
+	BTFIXUPSET_CALL(pte_mkwrite, apmmu_pte_mkwrite, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pte_mkdirty, apmmu_pte_mkdirty, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pte_mkyoung, apmmu_pte_mkyoung, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(update_mmu_cache, apmmu_update_mmu_cache, BTFIXUPCALL_NOP);
+
+	BTFIXUPSET_CALL(mmu_lockarea, apmmu_lockarea, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(mmu_unlockarea, apmmu_unlockarea, BTFIXUPCALL_NORM);
+
+	BTFIXUPSET_CALL(mmu_get_scsi_one, apmmu_null_func, BTFIXUPCALL_RETO0);
+	BTFIXUPSET_CALL(mmu_get_scsi_sgl, apmmu_null_func, BTFIXUPCALL_NOP);
+	BTFIXUPSET_CALL(mmu_release_scsi_one, apmmu_null_func, BTFIXUPCALL_NOP);
+	BTFIXUPSET_CALL(mmu_release_scsi_sgl, apmmu_null_func, BTFIXUPCALL_NOP);
+
+	BTFIXUPSET_CALL(mmu_info, apmmu_mmu_info, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(mmu_v2p, apmmu_v2p, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(mmu_p2v, apmmu_p2v, BTFIXUPCALL_NORM);
 
 	/* Task struct and kernel stack allocating/freeing. */
-	alloc_kernel_stack = apmmu_alloc_kernel_stack;
-	alloc_task_struct = apmmu_alloc_task_struct;
-	free_kernel_stack = apmmu_free_kernel_stack;
-	free_task_struct = apmmu_free_task_struct;
+	BTFIXUPSET_CALL(alloc_task_struct, apmmu_alloc_task_struct, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(free_task_struct, apmmu_free_task_struct, BTFIXUPCALL_NORM);
 
-	quick_kernel_fault = apmmu_quick_kernel_fault;
-
-	ctxd_set = apmmu_ctxd_set;
-	pmd_set = apmmu_pmd_set;
+	BTFIXUPSET_CALL(quick_kernel_fault, apmmu_quick_kernel_fault, BTFIXUPCALL_NORM);
 
 	init_viking();
 	patch_window_trap_handlers();
 }
-
-
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/boot/Makefile linux/arch/sparc/boot/Makefile
--- v2.1.96/linux/arch/sparc/boot/Makefile	Wed Jul 16 19:22:50 1997
+++ linux/arch/sparc/boot/Makefile	Tue Apr 14 17:44:18 1998
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.4 1997/07/11 11:05:18 jj Exp $
+# $Id: Makefile,v 1.6 1998/02/23 01:44:39 rth Exp $
 # Makefile for the Sparc boot stuff.
 #
 # Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -18,6 +18,12 @@
 
 piggyback: piggyback.c
 	$(HOSTCC) $(HOSTCFLAGS) -o piggyback piggyback.c
+
+btfixupprep: btfixupprep.c
+	$(HOSTCC) $(HOSTCFLAGS) -o btfixupprep btfixupprep.c
+
+archclean:
+	rm -f btfixupprep piggyback tftpboot.img
 
 dep:
 
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/boot/btfixupprep.c linux/arch/sparc/boot/btfixupprep.c
--- v2.1.96/linux/arch/sparc/boot/btfixupprep.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc/boot/btfixupprep.c	Tue Apr 14 17:44:18 1998
@@ -0,0 +1,345 @@
+/* $Id: btfixupprep.c,v 1.3 1998/03/09 14:03:10 jj Exp $
+   Simple utility to prepare vmlinux image for sparc.
+   Resolves all BTFIXUP uses and settings and creates
+   a special .s object to link to the image.
+   
+   Copyright (C) 1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+   
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
+   
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <malloc.h>
+
+#define MAXSYMS 1024
+
+static char *relrec = "RELOCATION RECORDS FOR [";
+static int rellen;
+
+struct _btfixup;
+
+typedef struct _btfixuprel {
+	char *sect;
+	unsigned long offset;
+	struct _btfixup *f;
+	int frel;
+	struct _btfixuprel *next;
+} btfixuprel;
+
+typedef struct _btfixup {
+	int type;
+	int setinitval;
+	unsigned int initval;
+	char *initvalstr;
+	char *name;
+	btfixuprel *rel;
+} btfixup;
+
+btfixup array[MAXSYMS];
+int last = 0;
+char buffer[1024];
+unsigned long lastfoffset = -1;
+unsigned long lastfrelno;
+btfixup *lastf;
+
+void fatal(void) __attribute__((noreturn));
+void fatal(void)
+{
+	fprintf(stderr, "Malformed output from objdump\n%s\n", buffer);
+	exit(1);
+}
+
+btfixup *find(int type, char *name)
+{
+	int i;
+	for (i = 0; i < last; i++) {
+		if (array[i].type == type && !strcmp(array[i].name, name))
+			return array + i;
+	}
+	array[last].type = type;
+	array[last].name = strdup(name);
+	array[last].setinitval = 0;
+	if (!array[last].name) fatal();
+	array[last].rel = NULL;
+	last++;
+	if (last >= MAXSYMS) {
+		fprintf(stderr, "Ugh. Something strange. More than %d different BTFIXUP symbols\n", MAXSYMS);
+		exit(1);
+	}
+	return array + last - 1;
+}
+
+int main(int argc,char **argv)
+{
+	char *p, *q;
+	char *sect;
+	int i, j, k;
+	unsigned int initval;
+	int shift;
+	btfixup *f;
+	btfixuprel *r, **rr;
+	unsigned long offset;
+	char *initvalstr;
+
+	rellen = strlen(relrec);
+	while (fgets (buffer, 1024, stdin) != NULL)
+		if (!strncmp (buffer, relrec, rellen))
+			goto main1;
+	fatal();
+main1:
+	sect = malloc(strlen (buffer + rellen) + 1);
+	if (!sect) fatal();
+	strcpy (sect, buffer + rellen);
+	p = strchr (sect, ']');
+	if (!p) fatal();
+	*p = 0;
+	if (fgets (buffer, 1024, stdin) == NULL)
+		fatal();
+	while (fgets (buffer, 1024, stdin) != NULL) {
+		if (!strncmp (buffer, relrec, rellen))
+			goto main1;
+		p = strchr (buffer, '\n');
+		if (p) *p = 0;
+		if (strlen (buffer) < 30)
+			continue;
+		if (strncmp (buffer + 8, " R_SPARC_", 9))
+			continue;
+		if (buffer[27] != '_' || buffer[28] != '_' || buffer[29] != '_')
+			continue;
+		switch (buffer[30]) {
+			case 'f':	/* CALL */
+			case 'b':	/* BLACKBOX */
+			case 's':	/* SIMM13 */
+			case 'a':	/* HALF */
+			case 'h':	/* SETHI */
+			case 'i':	/* INT */
+				break;
+			default:
+				continue;
+		}
+		p = strchr (buffer + 32, '+');
+		if (p) *p = 0;
+		shift = 32;
+		if (buffer[31] == 's' && buffer[32] == '_') {
+			shift = 33;
+			if (strcmp (sect, ".text.init")) {
+				fprintf(stderr, "Wrong use of '%s' BTFIXUPSET.\nBTFIXUPSET_CALL can be used only in __init sections\n", buffer+shift);
+				exit(1);
+			}
+		} else if (buffer[31] != '_')
+			continue;
+		if (strcmp (sect, ".text") && strcmp (sect, ".text.init") && (strcmp (sect, "__ksymtab") || buffer[30] != 'f')) {
+			if (buffer[30] == 'f')
+				fprintf(stderr, "Wrong use of '%s' in '%s' section. It can be only used in .text, .text.init and __ksymtab\n", buffer + shift, sect);
+			else
+				fprintf(stderr, "Wrong use of '%s' in '%s' section. It can be only used in .text and .text.init\n", buffer + shift, sect);
+			exit(1);
+		}
+		p = strstr (buffer + shift, "__btset_");
+		if (p && buffer[31] == 's') {
+			fprintf(stderr, "__btset_ in BTFIXUP name can only be used when defining the variable, not for setting\n%s\n", buffer);
+			exit(1);
+		}
+		initval = 0;
+		initvalstr = NULL;
+		if (p) {
+			if (p[8] != '0' || p[9] != 'x') {
+				fprintf(stderr, "Pre-initialized values can be only initialized with hexadecimal constants starting 0x\n%s\n", buffer);
+				exit(1);
+			}
+			initval = strtoul(p + 10, &q, 16);
+			if (*q || !initval) {
+				fprintf(stderr, "Pre-initialized values can be only in the form name__btset_0xXXXXXXXX where X are hex digits.\nThey cannot be name__btset_0x00000000 though. Use BTFIXUPDEF_XX instead of BTFIXUPDEF_XX_INIT then.\n%s\n", buffer);
+				exit(1);
+			}
+			initvalstr = p + 10;
+			*p = 0;
+		}
+		f = find(buffer[30], buffer + shift);
+		if (buffer[31] == 's')
+			continue;
+		switch (buffer[30]) {
+		case 'f':
+			if (initval) {
+				fprintf(stderr, "Cannot use pre-initalized fixups for calls\n%s\n", buffer);
+				exit(1);
+			}
+			if (!strcmp (sect, "__ksymtab")) {
+				if (strncmp (buffer + 17, "32        ", 10)) {
+					fprintf(stderr, "BTFIXUP_CALL in EXPORT_SYMBOL results in relocation other than R_SPARC_32\n\%s\n", buffer);
+					exit(1);
+				}
+			} else if (strncmp (buffer + 17, "WDISP30   ", 10) &&
+				   strncmp (buffer + 17, "HI22      ", 10) &&
+				   strncmp (buffer + 17, "LO10      ", 10)) {
+				fprintf(stderr, "BTFIXUP_CALL results in relocation other than R_SPARC_WDISP30, R_SPARC_HI22 or R_SPARC_LO10\n%s\n", buffer);
+				exit(1);
+			}
+			break;
+		case 'b':
+			if (initval) {
+				fprintf(stderr, "Cannot use pre-initialized fixups for blackboxes\n%s\n", buffer);
+				exit(1);
+			}
+			if (strncmp (buffer + 17, "HI22      ", 10)) {
+				fprintf(stderr, "BTFIXUP_BLACKBOX results in relocation other than R_SPARC_HI22\n%s\n", buffer);
+				exit(1);
+			}
+			break;
+		case 's':
+			if (initval + 0x1000 >= 0x2000) {
+				fprintf(stderr, "Wrong initializer for SIMM13. Has to be from $fffff000 to $00000fff\n%s\n", buffer);
+				exit(1);
+			}
+			if (strncmp (buffer + 17, "13        ", 10)) {
+				fprintf(stderr, "BTFIXUP_SIMM13 results in relocation other than R_SPARC_13\n%s\n", buffer);
+				exit(1);
+			}
+			break;
+		case 'a':
+			if (initval + 0x1000 >= 0x2000 && (initval & 0x3ff)) {
+				fprintf(stderr, "Wrong initializer for HALF.\n%s\n", buffer);
+				exit(1);
+			}
+			if (strncmp (buffer + 17, "13        ", 10)) {
+				fprintf(stderr, "BTFIXUP_HALF results in relocation other than R_SPARC_13\n%s\n", buffer);
+				exit(1);
+			}
+			break;
+		case 'h':
+			if (initval & 0x3ff) {
+				fprintf(stderr, "Wrong initializer for SETHI. Cannot have set low 10 bits\n%s\n", buffer);
+				exit(1);
+			}
+			if (strncmp (buffer + 17, "HI22      ", 10)) {
+				fprintf(stderr, "BTFIXUP_SETHI results in relocation other than R_SPARC_HI22\n%s\n", buffer);
+				exit(1);
+			}
+			break;
+		case 'i':
+			if (initval) {
+				fprintf(stderr, "Cannot use pre-initalized fixups for INT\n%s\n", buffer);
+				exit(1);
+			}
+			if (strncmp (buffer + 17, "HI22      ", 10) && strncmp (buffer + 17, "LO10      ", 10)) {
+				fprintf(stderr, "BTFIXUP_INT results in relocation other than R_SPARC_HI22 and R_SPARC_LO10\n%s\n", buffer);
+				exit(1);
+			}
+			break;
+		}
+		if (!f->setinitval) {
+			f->initval = initval;
+			if (initvalstr) {
+				f->initvalstr = strdup(initvalstr);
+				if (!f->initvalstr) fatal();
+			}
+			f->setinitval = 1;
+		} else if (f->initval != initval) {
+			fprintf(stderr, "Btfixup %s previously used with initializer %s which doesn't match with current initializer\n%s\n",
+					f->name, f->initvalstr ? : "0x00000000", buffer);
+			exit(1);
+		} else if (initval && strcmp(f->initvalstr, initvalstr)) {
+			fprintf(stderr, "Btfixup %s previously used with initializer %s which doesn't match with current initializer.\n"
+					"Initializers have to match literally as well.\n%s\n",
+					f->name, f->initvalstr, buffer);
+			exit(1);
+		}
+		offset = strtoul(buffer, &q, 16);
+		if (q != buffer + 8 || (!offset && strncmp (buffer, "00000000 ", 9))) {
+			fprintf(stderr, "Malformed relocation address in\n%s\n", buffer);
+			exit(1);
+		}
+		for (k = 0, r = f->rel, rr = &f->rel; r; rr = &r->next, r = r->next, k++)
+			if (r->offset == offset && !strcmp(r->sect, sect)) {
+				fprintf(stderr, "Ugh. One address has two relocation records\n");
+				exit(1);
+			}
+		*rr = malloc(sizeof(btfixuprel));
+		if (!*rr) fatal();
+		(*rr)->offset = offset;
+		(*rr)->f = NULL;
+		if (buffer[30] == 'f') {
+			lastf = f;
+			lastfoffset = offset;
+			lastfrelno = k;
+		} else if (lastfoffset + 4 == offset) {
+			(*rr)->f = lastf;
+			(*rr)->frel = lastfrelno;
+		}
+		(*rr)->sect = sect;
+		(*rr)->next = NULL;
+	}
+	printf("! Generated by btfixupprep. Do not edit.\n\n");
+	printf("\t.section\t\".data.init\",#alloc,#write\n\t.align\t4\n\n");
+	printf("\t.global\t___btfixup_start\n___btfixup_start:\n\n");
+	for (i = 0; i < last; i++) {
+		f = array + i;
+		printf("\t.global\t___%cs_%s\n", f->type, f->name);
+		if (f->type == 'f')
+			printf("___%cs_%s:\n\t.word 0x%08x,0,0,", f->type, f->name, f->type << 24);
+		else
+			printf("___%cs_%s:\n\t.word 0x%08x,0,", f->type, f->name, f->type << 24);
+		for (j = 0, r = f->rel; r != NULL; j++, r = r->next);
+		if (j)
+			printf("%d\n\t.word\t", j * 2);
+		else
+			printf("0\n");
+		for (r = f->rel, j--; r != NULL; j--, r = r->next) {
+			if (!strcmp (r->sect, ".text"))
+				printf ("_stext+0x%08x", r->offset);
+			else if (!strcmp (r->sect, ".text.init"))
+				printf ("__init_begin+0x%08x", r->offset);
+			else if (!strcmp (r->sect, "__ksymtab"))
+				printf ("__start___ksymtab+0x%08x", r->offset);
+			else
+				fatal();
+			if (f->type == 'f' || !r->f)
+				printf (",0");
+			else
+				printf (",___fs_%s+0x%08x", r->f->name, (4 + r->frel*2)*4 + 4);
+			if (j) printf (",");
+			else printf ("\n");
+		}
+		printf("\n");
+	}
+	printf("\n\t.global\t___btfixup_end\n___btfixup_end:\n");
+	printf("\n\n! Define undefined references\n\n");
+	for (i = 0; i < last; i++) {
+		f = array + i;
+		if (f->type == 'f') {
+			printf("\t.global\t___f_%s\n", f->name);
+			printf("___f_%s:\n", f->name);
+		}
+	}
+	printf("\tretl\n\t nop\n\n");
+	for (i = 0; i < last; i++) {
+		f = array + i;
+		if (f->type != 'f') {
+			if (!f->initval) {
+				printf("\t.global\t___%c_%s\n", f->type, f->name);
+				printf("___%c_%s = 0\n", f->type, f->name);
+			} else {
+				printf("\t.global\t___%c_%s__btset_0x%s\n", f->type, f->name, f->initvalstr);
+				printf("___%c_%s__btset_0x%s = 0x%08x\n", f->type, f->name, f->initvalstr, f->initval);
+			}
+		}
+	}
+	printf("\n\n");
+    	exit(0);
+}
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/config.in linux/arch/sparc/config.in
--- v2.1.96/linux/arch/sparc/config.in	Wed Apr  1 20:11:48 1998
+++ linux/arch/sparc/config.in	Tue Apr 14 17:44:18 1998
@@ -1,4 +1,4 @@
-# $Id: config.in,v 1.51 1998/01/08 04:16:54 baccala Exp $
+# $Id: config.in,v 1.54 1998/03/27 06:59:39 davem Exp $
 # For a description of the syntax of this configuration file,
 # see the Configure script.
 #
@@ -34,6 +34,8 @@
 	define_bool CONFIG_APBIF y
 	tristate 'OPIU DDV Driver' CONFIG_DDV
 else
+	bool 'Support for SUN4 machines (disables SUN4[CDM] support)' CONFIG_SUN4
+
 	# Global things across all Sun machines.
 	define_bool CONFIG_SBUS y
 	define_bool CONFIG_SBUSCHAR y
@@ -45,8 +47,13 @@
 	define_bool CONFIG_SUN_CONSOLE y
 	define_bool CONFIG_SUN_AUXIO y
 	define_bool CONFIG_SUN_IO y
-	source drivers/sbus/char/Config.in
-	source drivers/sbus/audio/Config.in
+	if [ "$CONFIG_SUN4" = "y" ]; then
+		bool 'Sun FB drivers appear in PROCFS' SUN_FBS_IN_PROCFS
+		bool 'bwtwo support' SUN_FB_BWTWO
+	else
+		source drivers/sbus/char/Config.in
+		source drivers/sbus/audio/Config.in
+	fi
 fi
 
 tristate 'Openprom tree appears in /proc/openprom (EXPERIMENTAL)' CONFIG_SUN_OPENPROMFS
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/defconfig linux/arch/sparc/defconfig
--- v2.1.96/linux/arch/sparc/defconfig	Tue Mar 17 22:18:14 1998
+++ linux/arch/sparc/defconfig	Tue Apr 14 17:44:18 1998
@@ -20,11 +20,13 @@
 CONFIG_VT=y
 CONFIG_VT_CONSOLE=y
 # CONFIG_AP1000 is not set
+# CONFIG_SUN4 is not set
 CONFIG_SBUS=y
 CONFIG_SBUSCHAR=y
 CONFIG_SUN_MOUSE=y
 CONFIG_SERIAL=y
 CONFIG_SUN_SERIAL=y
+CONFIG_SERIAL_CONSOLE=y
 CONFIG_SUN_KEYBOARD=y
 CONFIG_SUN_CONSOLE=y
 CONFIG_SUN_AUXIO=y
@@ -80,6 +82,7 @@
 CONFIG_BLK_DEV_RAM=y
 CONFIG_BLK_DEV_INITRD=y
 CONFIG_BLK_DEV_LOOP=m
+# CONFIG_BLK_DEV_NBD is not set
 
 #
 # Networking options
@@ -89,8 +92,8 @@
 CONFIG_RTNETLINK=y
 # CONFIG_NETLINK_DEV is not set
 CONFIG_FIREWALL=y
-# CONFIG_NET_SECURITY is not set
 CONFIG_NET_ALIAS=y
+# CONFIG_FILTER is not set
 CONFIG_UNIX=y
 CONFIG_INET=y
 CONFIG_IP_MULTICAST=y
@@ -107,11 +110,18 @@
 #
 # Protocol-specific masquerading support will be built as modules.
 #
+# CONFIG_IP_MASQUERADE_ICMP is not set
+
+#
+# Protocol-specific masquerading support will be built as modules.
+#
+# CONFIG_IP_MASQUERADE_IPAUTOFW is not set
+# CONFIG_IP_MASQUERADE_IPPORTFW is not set
 # CONFIG_IP_ROUTER is not set
 # CONFIG_NET_IPIP is not set
 # CONFIG_NET_IPGRE is not set
 # CONFIG_IP_MROUTE is not set
-CONFIG_IP_ALIAS=m
+CONFIG_IP_ALIAS=y
 # CONFIG_ARPD is not set
 # CONFIG_SYN_COOKIES is not set
 
@@ -123,31 +133,39 @@
 CONFIG_SKB_LARGE=y
 CONFIG_IPV6=m
 # CONFIG_IPV6_EUI64 is not set
-# CONFIG_IPV6_NO_PB is not set
 
 #
 #  
 #
 CONFIG_IPX=m
+
+#
+# IPX options
+#
 # CONFIG_IPX_INTERN is not set
-# CONFIG_IPX_PPROP_ROUTING is not set
 CONFIG_ATALK=m
-# CONFIG_AX25 is not set
 CONFIG_X25=m
 # CONFIG_LAPB is not set
 # CONFIG_BRIDGE is not set
 # CONFIG_LLC is not set
 # CONFIG_WAN_ROUTER is not set
+# CONFIG_NET_FASTROUTE is not set
+# CONFIG_NET_HW_FLOWCONTROL is not set
 # CONFIG_CPU_IS_SLOW is not set
 CONFIG_NET_SCHED=y
 CONFIG_NET_SCH_CBQ=m
 CONFIG_NET_SCH_CSZ=m
-CONFIG_NET_SCH_HFQ=m
 CONFIG_NET_SCH_RED=m
 CONFIG_NET_SCH_SFQ=m
 CONFIG_NET_SCH_TBF=y
 CONFIG_NET_SCH_PFIFO=y
 CONFIG_NET_SCH_PRIO=y
+# CONFIG_NET_PROFILE is not set
+
+#
+# ISDN subsystem
+#
+# CONFIG_ISDN is not set
 
 #
 # SCSI support
@@ -176,6 +194,21 @@
 CONFIG_SCSI_QLOGICPTI=m
 
 #
+# Fibre Channel support
+#
+CONFIG_FC4=m
+
+#
+# FC4 drivers
+#
+CONFIG_FC4_SOC=m
+
+#
+# FC4 targets
+#
+CONFIG_SCSI_PLUTO=m
+
+#
 # Network device support
 #
 CONFIG_NETDEVICES=y
@@ -193,6 +226,7 @@
 CONFIG_HAPPYMEAL=m
 CONFIG_SUNQE=m
 CONFIG_MYRI_SBUS=m
+CONFIG_CDROM=y
 
 #
 # Filesystems
@@ -211,24 +245,35 @@
 CONFIG_NFSD=m
 CONFIG_SUNRPC=y
 CONFIG_LOCKD=y
+# CONFIG_CODA_FS is not set
 CONFIG_SMB_FS=m
 CONFIG_SMB_WIN95=y
 CONFIG_NCP_FS=m
+# CONFIG_NCPFS_PACKET_SIGNING is not set
+# CONFIG_NCPFS_IOCTL_LOCKING is not set
+# CONFIG_NCPFS_STRONG is not set
+# CONFIG_NCPFS_NFS_NS is not set
+# CONFIG_NCPFS_OS2_NS is not set
+# CONFIG_NCPFS_MOUNT_SUBDIR is not set
 CONFIG_HPFS_FS=m
+# CONFIG_NTFS_FS is not set
 CONFIG_SYSV_FS=m
 CONFIG_AFFS_FS=m
+# CONFIG_HFS_FS is not set
 CONFIG_ROMFS_FS=m
 CONFIG_AUTOFS_FS=m
 CONFIG_AMIGA_PARTITION=y
 CONFIG_UFS_FS=y
 CONFIG_BSD_DISKLABEL=y
 CONFIG_SMD_DISKLABEL=y
+# CONFIG_SOLARIS_X86_PARTITION is not set
+# CONFIG_ADFS_FS is not set
 # CONFIG_MAC_PARTITION is not set
+CONFIG_NLS=y
 
 #
 # Native Language Support
 #
-CONFIG_NLS=y
 # CONFIG_NLS_CODEPAGE_437 is not set
 # CONFIG_NLS_CODEPAGE_737 is not set
 # CONFIG_NLS_CODEPAGE_775 is not set
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/kernel/Makefile linux/arch/sparc/kernel/Makefile
--- v2.1.96/linux/arch/sparc/kernel/Makefile	Mon Jan 12 15:15:43 1998
+++ linux/arch/sparc/kernel/Makefile	Tue Apr 14 17:44:18 1998
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.41 1997/11/19 15:11:59 jj Exp $
+# $Id: Makefile,v 1.43 1998/03/09 14:03:34 jj Exp $
 # Makefile for the linux kernel.
 #
 # Note! Dependencies are done automagically by 'make dep', which also
@@ -15,8 +15,6 @@
 .S.o:
 	$(CC) -D__ASSEMBLY__ $(AFLAGS) -ansi -c $< -o $*.o
 
-CHECKASM_CC	=	$(CC) -D__SMP__
-
 else
 
 .S.s:
@@ -25,7 +23,6 @@
 .S.o:
 	$(CC) -D__ASSEMBLY__ -ansi -c $< -o $*.o
 
-CHECKASM_CC	=	$(CC)
 endif
 
 all: kernel.o head.o init_task.o
@@ -42,7 +39,7 @@
 OX_OBJS  := sparc_ksyms.o
 
 ifdef SMP
-O_OBJS += trampoline.o smp.o
+O_OBJS += trampoline.o smp.o sun4m_smp.o sun4d_smp.o
 endif
 
 ifdef CONFIG_SUN_AUXIO
@@ -62,18 +59,61 @@
 endif
 
 check_asm: dummy
+	@echo "/* Automatically generated. Do not edit. */" > asm_offsets.h
+	@echo "#ifndef __ASM_OFFSETS_H__" >> asm_offsets.h
+	@echo "#define __ASM_OFFSETS_H__" >> asm_offsets.h
+	@echo "" >> asm_offsets.h
+	@echo "#ifndef __SMP__" >> asm_offsets.h
+	@echo "" >> asm_offsets.h
+	@echo "#include <linux/sched.h>" > tmp.c
+	$(CC) -E tmp.c -o tmp.i
+	@echo "/* Automatically generated. Do not edit. */" > check_asm.c
+	@echo "#include <linux/sched.h>" >> check_asm.c
+	@echo 'struct task_struct _task;' >> check_asm.c
+	@echo 'struct mm_struct _mm;' >> check_asm.c
+	@echo 'struct thread_struct _thread;' >> check_asm.c
+	@echo 'int main(void) {' >> check_asm.c
+	$(SH) ./check_asm.sh task tmp.i check_asm.c
+	$(SH) ./check_asm.sh mm tmp.i check_asm.c
+	$(SH) ./check_asm.sh thread tmp.i check_asm.c
+	@echo 'return 0; }' >> check_asm.c
+	@rm -f tmp.[ci]
+	$(CC) -o check_asm check_asm.c
+	./check_asm >> asm_offsets.h
+	@rm -f check_asm check_asm.c
+	@echo "" >> asm_offsets.h
+	@echo "#else /* __SMP__ */" >> asm_offsets.h
+	@echo "" >> asm_offsets.h
 	@echo "#include <linux/sched.h>" > tmp.c
-	$(CHECKASM_CC) -E tmp.c -o tmp.i
-	@echo "/* Automatically generated. Do not edit. */" > check_asm.c; echo "#include <linux/sched.h>" >> check_asm.c; echo 'struct task_struct _task; struct mm_struct _mm; struct thread_struct _thread; int main(void) { printf ("/* Automatically generated. Do not edit. */\n#ifndef __ASM_OFFSETS_H__\n#define __ASM_OFFSETS_H__\n\n");' >> check_asm.c
+	$(CC) -D__SMP__ -E tmp.c -o tmp.i
+	@echo "/* Automatically generated. Do not edit. */" > check_asm.c
+	@echo "#include <linux/sched.h>" >> check_asm.c
+	@echo 'struct task_struct _task;' >> check_asm.c
+	@echo 'struct mm_struct _mm;' >> check_asm.c
+	@echo 'struct thread_struct _thread;' >> check_asm.c
+	@echo 'int main(void) {' >> check_asm.c
 	$(SH) ./check_asm.sh task tmp.i check_asm.c
 	$(SH) ./check_asm.sh mm tmp.i check_asm.c
 	$(SH) ./check_asm.sh thread tmp.i check_asm.c
-	@echo 'printf ("\n#endif /* __ASM_OFFSETS_H__ */\n"); return 0; }' >> check_asm.c
+	@echo 'return 0; }' >> check_asm.c
 	@rm -f tmp.[ci]
-	$(CHECKASM_CC) -o check_asm check_asm.c
-	./check_asm > asm_offsets.h
-	@if test -r $(HPATH)/asm/asm_offsets.h; then if cmp -s asm_offsets.h $(HPATH)/asm/asm_offsets.h; then echo $(HPATH)/asm/asm_offsets.h is unchanged; rm -f asm_offsets.h; else mv -f asm_offsets.h $(HPATH)/asm/asm_offsets.h; fi; else mv -f asm_offsets.h $(HPATH)/asm/asm_offsets.h; fi
+	$(CC) -D__SMP__ -o check_asm check_asm.c
+	./check_asm >> asm_offsets.h
 	@rm -f check_asm check_asm.c
+	@echo "" >> asm_offsets.h
+	@echo "#endif /* __SMP__ */" >> asm_offsets.h
+	@echo "" >> asm_offsets.h
+	@echo "#endif /* __ASM_OFFSETS_H__ */" >> asm_offsets.h
+	@if test -r $(HPATH)/asm/asm_offsets.h; then \
+	  if cmp -s asm_offsets.h $(HPATH)/asm/asm_offsets.h; then \
+	    echo $(HPATH)/asm/asm_offsets.h is unchanged; \
+	    rm -f asm_offsets.h; \
+	  else \
+	    mv -f asm_offsets.h $(HPATH)/asm/asm_offsets.h; \
+	  fi; \
+	else \
+	  mv -f asm_offsets.h $(HPATH)/asm/asm_offsets.h; \
+	fi
 
 
 include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/kernel/auxio.c linux/arch/sparc/kernel/auxio.c
--- v2.1.96/linux/arch/sparc/kernel/auxio.c	Mon Mar 17 14:54:20 1997
+++ linux/arch/sparc/kernel/auxio.c	Tue Apr 14 17:44:18 1998
@@ -17,9 +17,13 @@
 	int node, auxio_nd;
 	struct linux_prom_registers auxregs[1];
 
-	if (sparc_cpu_model == sun4d) {
+	switch (sparc_cpu_model) {
+	case sun4d:
+	case sun4:
 		auxio_register = 0;
 		return;
+	default:
+		break;
 	}
 	node = prom_getchild(prom_root_node);
 	auxio_nd = prom_searchsiblings(node, "auxiliary-io");
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/kernel/cpu.c linux/arch/sparc/kernel/cpu.c
--- v2.1.96/linux/arch/sparc/kernel/cpu.c	Mon Jul  7 08:18:54 1997
+++ linux/arch/sparc/kernel/cpu.c	Tue Apr 14 17:44:18 1998
@@ -6,6 +6,8 @@
 
 #include <linux/kernel.h>
 #include <linux/init.h>
+#include <linux/smp.h>
+#include <linux/tasks.h>
 #include <asm/oplib.h>
 #include <asm/page.h>
 #include <asm/head.h>
@@ -116,22 +118,25 @@
 
 #define NSPARCCHIPS  (sizeof(linux_sparc_chips)/sizeof(struct cpu_iu_info))
 
-char *sparc_cpu_type[NCPUS] = { "cpu-oops", "cpu-oops1", "cpu-oops2", "cpu-oops3" };
-char *sparc_fpu_type[NCPUS] = { "fpu-oops", "fpu-oops1", "fpu-oops2", "fpu-oops3" };
+char *sparc_cpu_type[NR_CPUS] = { 0 };
+char *sparc_fpu_type[NR_CPUS] = { 0 };
 
 unsigned int fsr_storage;
 
 __initfunc(void cpu_probe(void))
 {
 	int psr_impl, psr_vers, fpu_vers;
-	int i, cpuid;
+	int i, cpuid, psr;
 
-	cpuid = get_cpuid();
+	cpuid = hard_smp_processor_id();
 
 	psr_impl = ((get_psr()>>28)&0xf);
 	psr_vers = ((get_psr()>>24)&0xf);
 
+	psr = get_psr();
+	put_psr(psr | PSR_EF);
 	fpu_vers = ((get_fsr()>>17)&0x7);
+	put_psr(psr);
 
 	for(i = 0; i<NSPARCCHIPS; i++) {
 		if(linux_sparc_chips[i].psr_impl == psr_impl)
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/kernel/devices.c linux/arch/sparc/kernel/devices.c
--- v2.1.96/linux/arch/sparc/kernel/devices.c	Thu Sep  4 12:54:48 1997
+++ linux/arch/sparc/kernel/devices.c	Tue Apr 14 17:44:18 1998
@@ -14,7 +14,7 @@
 #include <asm/smp.h>
 #include <asm/system.h>
 
-struct prom_cpuinfo linux_cpus[NCPUS];
+struct prom_cpuinfo linux_cpus[NR_CPUS];
 int linux_num_cpus;
 
 extern void cpu_probe(void);
@@ -26,7 +26,7 @@
 {
 	char node_str[128];
 	int nd, prom_node_cpu, thismid;
-	int cpu_nds[NCPUS];  /* One node for each cpu */
+	int cpu_nds[NR_CPUS];  /* One node for each cpu */
 	int cpu_ctr = 0;
 
 	prom_getstring(prom_root_node, "device_type", node_str, sizeof(node_str));
@@ -62,11 +62,9 @@
 					prom_getstring(node, "device_type", node_str, sizeof(node_str));
 					if (strcmp(node_str, "cpu") == 0) {
 						prom_getproperty(node, "cpu-id", (char *) &thismid, sizeof(thismid));
-						if (cpu_ctr < NCPUS) {
-							cpu_nds[cpu_ctr] = node;
-							linux_cpus[cpu_ctr].prom_node = node;
-							linux_cpus[cpu_ctr].mid = thismid;
-						}
+						cpu_nds[cpu_ctr] = node;
+						linux_cpus[cpu_ctr].prom_node = node;
+						linux_cpus[cpu_ctr].mid = thismid;
 						prom_printf("Found CPU %d <node=%08lx,mid=%d>\n",
 							    cpu_ctr, (unsigned long) node,
 							    thismid);
@@ -74,8 +72,6 @@
 					}
 				}
 			}
-			if (cpu_ctr > NCPUS)
-				cpu_ctr = NCPUS;
 		}
 		if(cpu_ctr == 0) {
 			printk("No CPU nodes found, cannot continue.\n");
@@ -99,7 +95,7 @@
 #endif
 	clock_stop_probe();
 
-	if (sparc_cpu_model == sun4c)
+	if (ARCH_SUN4C_SUN4)
 		sun4c_probe_memerr_reg();
 
 	return mem_start;
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/kernel/entry.S linux/arch/sparc/kernel/entry.S
--- v2.1.96/linux/arch/sparc/kernel/entry.S	Mon Jan 12 15:15:43 1998
+++ linux/arch/sparc/kernel/entry.S	Tue Apr 14 17:44:18 1998
@@ -1,10 +1,11 @@
-/* $Id: entry.S,v 1.142 1998/01/07 06:33:47 baccala Exp $
+/* $Id: entry.S,v 1.149 1998/03/19 15:36:30 jj Exp $
  * arch/sparc/kernel/entry.S:  Sparc trap low-level entry points.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
  * Copyright (C) 1996 Eddie C. Dost   (ecd@skynet.be)
  * Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
- * Copyright (C) 1996 Jakub Jelinek   (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1996,1998 Jakub Jelinek   (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1997 Anton Blanchard (anton@progsoc.uts.edu.au)
  */
 
 #include <linux/config.h>
@@ -21,9 +22,15 @@
 #include <asm/vaddrs.h>
 #include <asm/memreg.h>
 #include <asm/page.h>
+#ifdef CONFIG_SUN4
+#include <asm/pgtsun4.h>
+#else
 #include <asm/pgtsun4c.h>
+#endif
 #include <asm/winmacro.h>
 #include <asm/signal.h>
+#include <asm/obio.h>
+#include <asm/mxcc.h>
 
 #include <asm/asmmacro.h>
 
@@ -288,10 +295,14 @@
 	SAVE_ALL
 
 #ifdef __SMP__
+	.globl	patchme_maybe_smp_msg
+
 	cmp	%l7, 12
-	bgu	maybe_smp_msg
+patchme_maybe_smp_msg:
+	bgu	maybe_smp4m_msg
 	 nop
 #endif
+
 real_irq_continue:
 	or	%l0, PSR_PIL, %g2
 	wr	%g2, 0x0, %psr
@@ -309,14 +320,14 @@
 
 #ifdef __SMP__
 	/* SMP per-cpu ticker interrupts are handled specially. */
-smp_ticker:
-	bne	real_irq_continue
+smp4m_ticker:
+	bne	real_irq_continue+4
 	 or	%l0, PSR_PIL, %g2
 	wr	%g2, 0x0, %psr
 	WRITE_PAUSE
 	wr	%g2, PSR_ET, %psr
 	WRITE_PAUSE
-	call	C_LABEL(smp_percpu_timer_interrupt)
+	call	C_LABEL(smp4m_percpu_timer_interrupt)
 	 add	%sp, REGWIN_SZ, %o0
 	wr	%l0, PSR_ET, %psr
 	WRITE_PAUSE
@@ -326,7 +337,7 @@
 	 * on some level other than 15 which is the NMI and only used
 	 * for cross calls.  That has a seperate entry point below.
 	 */
-maybe_smp_msg:
+maybe_smp4m_msg:
 	GET_PROCESSOR_MID(o3, o2)
 	set	C_LABEL(sun4m_interrupts), %l5
 	ld	[%l5], %o5
@@ -334,7 +345,7 @@
 	sll	%o3, 12, %o3
 	ld	[%o5 + %o3], %o1
 	andcc	%o1, %o4, %g0
-	be,a	smp_ticker
+	be,a	smp4m_ticker
 	 cmp	%l7, 14
 	cmp	%l7, 13
 	add	%o5, %o3, %o5
@@ -383,7 +394,7 @@
 	WRITE_PAUSE
 	wr	%l4, PSR_ET, %psr
 	WRITE_PAUSE
-	call	C_LABEL(smp_cross_call_irq)
+	call	C_LABEL(smp4m_cross_call_irq)
 	 nop
 	b	ret_trap_lockless_ipi
 	 clr	%l6
@@ -409,6 +420,64 @@
 	ld	[%l5], %g0
 	WRITE_PAUSE
 	RESTORE_ALL
+
+	.globl	smp4d_ticker
+	/* SMP per-cpu ticker interrupts are handled specially. */
+smp4d_ticker:
+	SAVE_ALL
+	or	%l0, PSR_PIL, %g2
+	sethi	%hi(CC_ICLR), %o0
+	sethi	%hi(1 << 14), %o1
+	or	%o0, %lo(CC_ICLR), %o0
+	stha	%o1, [%o0] ASI_M_MXCC	/* Clear PIL 14 in MXCC's ICLR */
+	wr	%g2, 0x0, %psr
+	WRITE_PAUSE
+	wr	%g2, PSR_ET, %psr
+	WRITE_PAUSE
+	call	C_LABEL(smp4d_percpu_timer_interrupt)
+	 add	%sp, REGWIN_SZ, %o0
+	wr	%l0, PSR_ET, %psr
+	WRITE_PAUSE
+	RESTORE_ALL
+
+	.align	4
+	.globl	linux_trap_ipi15_sun4d
+linux_trap_ipi15_sun4d:
+	SAVE_ALL
+	sethi	%hi(CC_BASE), %o4
+	sethi	%hi(MXCC_ERR_ME|MXCC_ERR_PEW|MXCC_ERR_ASE|MXCC_ERR_PEE), %o2
+	or	%o4, (CC_EREG - CC_BASE), %o0
+	ldda	[%o0] ASI_M_MXCC, %o0
+	andcc	%o0, %o2, %g0
+	bne	1f
+	 sethi	%hi(BB_STAT2), %o2
+	lduba	[%o2] ASI_M_CTL, %o2
+	andcc	%o2, BB_STAT2_MASK, %g0
+	bne	2f
+	 or	%o4, (CC_ICLR - CC_BASE), %o0
+	sethi	%hi(1 << 15), %o1
+	stha	%o1, [%o0] ASI_M_MXCC	/* Clear PIL 15 in MXCC's ICLR */
+	or	%l0, PSR_PIL, %l4
+	wr	%l4, 0x0, %psr
+	WRITE_PAUSE
+	wr	%l4, PSR_ET, %psr
+	WRITE_PAUSE
+	call	C_LABEL(smp4d_cross_call_irq)
+	 nop
+	b	ret_trap_lockless_ipi
+	 clr	%l6
+
+1:	/* MXCC error */
+2:	/* BB error */
+	/* Disable PIL 15 */
+	set	CC_IMSK, %l4
+	lduha	[%l4] ASI_M_MXCC, %l5
+	sethi	%hi(1 << 15), %l7
+	or	%l5, %l7, %l5
+	stha	%l5, [%l4] ASI_M_MXCC
+	/* FIXME */
+1:	b,a	1b
+
 #endif /* __SMP__ */
 
 	/* This routine handles illegal instructions and privileged
@@ -417,6 +486,12 @@
 	.align	4
 	.globl	bad_instruction
 bad_instruction:
+	sethi	%hi(0xc1f80000), %l4
+	ld	[%l1], %l5
+	sethi	%hi(0x81d80000), %l7
+	and	%l5, %l4, %l5
+	cmp	%l5, %l7
+	be	1f
 	SAVE_ALL
 
 	wr	%l0, PSR_ET, %psr		! re-enable traps
@@ -430,6 +505,10 @@
 
 	RESTORE_ALL
 
+1:	/* unimplemented flush - just skip */
+	jmpl	%l2, %g0
+	 rett	%l2 + 4
+
 	.align	4
 	.globl	priv_instruction
 priv_instruction:
@@ -601,23 +680,6 @@
 
 	RESTORE_ALL
 
-	/* This routine handles Unimplemented FLUSH Exceptions. */
-	.align	4
-	.globl	do_bad_flush
-do_bad_flush:
-	SAVE_ALL
-
-	wr	%l0, PSR_ET, %psr		! re-enable traps
-	WRITE_PAUSE
-
-	add	%sp, REGWIN_SZ, %o0
-	mov	%l1, %o1
-	mov	%l2, %o2
-	call	C_LABEL(handle_bad_flush)
-	 mov	%l0, %o3
-
-	RESTORE_ALL
-
 	/* This routine handles Co-Processor Exceptions. */
 	.align	4
 	.globl	do_cp_exception
@@ -766,6 +828,12 @@
 C_LABEL(invalid_segment_patch2_ff):	mov	0xff, %l4
 
 	.align	4
+	.globl	C_LABEL(invalid_segment_patch1_1ff)
+	.globl	C_LABEL(invalid_segment_patch2_1ff)
+C_LABEL(invalid_segment_patch1_1ff):	cmp	%l4, 0x1ff
+C_LABEL(invalid_segment_patch2_1ff):	mov	0x1ff, %l4
+
+	.align	4
 	.globl	C_LABEL(num_context_patch1_16), C_LABEL(num_context_patch2_16)
 C_LABEL(num_context_patch1_16):		mov	0x10, %l7
 C_LABEL(num_context_patch2_16):		mov	0x10, %l7
@@ -776,7 +844,17 @@
 
 	.align	4
 	.globl	C_LABEL(vac_hwflush_patch1_on), C_LABEL(vac_hwflush_patch2_on)
+
+/*
+ * Ugly, but we cant use hardware flushing on the sun4 and we'd require
+ * two instructions (Anton)
+ */
+#ifdef CONFIG_SUN4
+C_LABEL(vac_hwflush_patch1_on):		nop
+#else
 C_LABEL(vac_hwflush_patch1_on):		subcc	%l7, (PAGE_SIZE - 4), %l7
+#endif
+
 C_LABEL(vac_hwflush_patch2_on):		sta	%g0, [%l3 + %l7] ASI_HWFLUSHSEG
 
 	.globl	C_LABEL(invalid_segment_patch1), C_LABEL(invalid_segment_patch2)
@@ -786,11 +864,50 @@
 
 	.align	4
 	.globl	sun4c_fault
+
+! %l0 = %psr
+! %l1 = %pc
+! %l2 = %npc
+! %l3 = %wim
+! %l7 = 1 for textfault
+! We want error in %l5, vaddr in %l6
 sun4c_fault:
+#ifdef CONFIG_SUN4
+	sethi	C_LABEL(sun4c_memerr_reg), %l4
+	ld	[%l4+%lo(C_LABEL(sun4c_memerr_reg))], %l4  ! memerr ctrl reg addr
+	ld	[%l4], %l6		! memerr ctrl reg
+	ld	[%l4 + 4], %l5		! memerr vaddr reg
+	andcc	%l6, 0x80, %g0		! check for error type
+	st	%g0, [%l4 + 4]		! clear the error
+	be	0f			! normal error
+	 sethi	%hi(AC_BUS_ERROR), %l4	! bus err reg addr
+
+	call	C_LABEL(prom_halt)	! something weird happened
+					! what exactly did happen?
+					! what should we do here?
+
+0:	or	%l4, %lo(AC_BUS_ERROR), %l4	! bus err reg addr
+	lduba	[%l4] ASI_CONTROL, %l6	! bus err reg
+
+	cmp    %l7, 1			! text fault?
+	be	1f			! yes
+	 nop
+
+	ld     [%l1], %l4		! load instruction that caused fault
+	srl	%l4, 21, %l4
+	andcc	%l4, 1, %g0		! store instruction?
+
+	be	1f			! no
+	 sethi	%hi(SUN4C_SYNC_BADWRITE), %l4 ! yep
+					! %lo(SUN4C_SYNC_BADWRITE) = 0
+	or	%l4, %l6, %l6		! set write bit to emulate sun4c
+1:
+#else
 	sethi	%hi(AC_SYNC_ERR), %l4
 	add	%l4, 0x4, %l6			! AC_SYNC_VA in %l6
 	lda	[%l6] ASI_CONTROL, %l5		! Address
 	lda	[%l4] ASI_CONTROL, %l6		! Error, retained for a bit
+#endif
 
 	andn	%l5, 0xfff, %l5			! Encode all info into l7
 	srl	%l6, 14, %l4
@@ -830,17 +947,21 @@
 	sethi   %hi(SUN4C_VMALLOC_START), %l4
 	cmp     %l5, %l4
 	blu,a   C_LABEL(invalid_segment_patch1)
-	 lduba  [%l5] ASI_SEGMAP, %l4
+	 lduXa	[%l5] ASI_SEGMAP, %l4
 
-	srl     %l5, SUN4C_PGDIR_SHIFT, %l6
 	sethi   %hi(C_LABEL(swapper_pg_dir)), %l4
+	srl     %l5, SUN4C_PGDIR_SHIFT, %l6
 	or      %l4, %lo(C_LABEL(swapper_pg_dir)), %l4
 	sll     %l6, 2, %l6
 	ld      [%l4 + %l6], %l4
+#ifdef CONFIG_SUN4
+	sethi	PAGE_MASK, %l6
+	andcc	%l4, %l6, %g0
+#else
 	andcc   %l4, PAGE_MASK, %g0
-
+#endif
 	be      sun4c_fault_fromuser
-	 lduba  [%l5] ASI_SEGMAP, %l4
+	 lduXa  [%l5] ASI_SEGMAP, %l4
 
 C_LABEL(invalid_segment_patch1):
 	cmp	%l4, 0x7f
@@ -889,7 +1010,11 @@
 	ld	[%l6 + 0x08], %l3	! tmp = entry->vaddr
 
 	! Flush segment from the cache.
+#ifdef CONFIG_SUN4
+	sethi	%hi((128 * 1024)), %l7
+#else
 	sethi	%hi((64 * 1024)), %l7
+#endif
 9:
 C_LABEL(vac_hwflush_patch1):
 C_LABEL(vac_linesize_patch):
@@ -928,7 +1053,7 @@
 	deccc	%l7
 	stba	%l7, [%l3] ASI_CONTROL
 	bne	3b
-	 stba	%l4, [%l5] ASI_SEGMAP
+	 stXa	%l4, [%l5] ASI_SEGMAP
 
 	stba	%l6, [%l3] ASI_CONTROL
 
@@ -952,7 +1077,7 @@
 	deccc	%l7
 	stba	%l7, [%l3] ASI_CONTROL
 	bne	3b
-	 stba	%l4, [%l5] ASI_SEGMAP
+	 stXa	%l4, [%l5] ASI_SEGMAP
 
 	stba	%l6, [%l3] ASI_CONTROL
 
@@ -988,7 +1113,12 @@
 	or	%l4, %lo(C_LABEL(swapper_pg_dir)), %l4
 	sll	%l3, 2, %l3
 	ld	[%l4 + %l3], %l4
+#ifndef CONFIG_SUN4
 	and	%l4, PAGE_MASK, %l4
+#else
+	sethi	PAGE_MASK, %l6
+	and	%l4, %l6, %l4
+#endif
 
 	srl	%l5, (PAGE_SHIFT - 2), %l6
 	and	%l6, ((SUN4C_PTRS_PER_PTE - 1) << 2), %l6
@@ -1640,9 +1770,8 @@
 	call	.umul
 	 ld	[%o3 + %lo(C_LABEL(loops_per_sec))], %o1
 #else
-	GET_PROCESSOR_OFFSET(o4)
+	GET_PROCESSOR_OFFSET(o4, o2)
 	set	C_LABEL(cpu_data), %o3
-	sll	%o4, 1, %o4
 	call	.umul
 	 ld	[%o3 + %o4], %o1
 #endif
@@ -1717,5 +1846,12 @@
 3:
 	retl					! return
 	 st	%g0, [%g6 + AOFF_task_tss + AOFF_thread_w_saved]	! no windows saved
+
+	.align	4
+	.globl	C_LABEL(restore_current)
+C_LABEL(restore_current):
+	LOAD_CURRENT(g6, o0)
+	retl
+	 nop
 
 /* End of entry.S */
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/kernel/etrap.S linux/arch/sparc/kernel/etrap.S
--- v2.1.96/linux/arch/sparc/kernel/etrap.S	Tue May 13 22:41:03 1997
+++ linux/arch/sparc/kernel/etrap.S	Tue Apr 14 17:44:18 1998
@@ -1,4 +1,4 @@
-/* $Id: etrap.S,v 1.26 1997/05/01 08:53:32 davem Exp $
+/* $Id: etrap.S,v 1.29 1998/02/09 13:48:40 jj Exp $
  * etrap.S: Sparc trap window preparation for entry into the
  *          Linux kernel.
  *
@@ -13,6 +13,7 @@
 #include <asm/psr.h>
 #include <asm/ptrace.h>
 #include <asm/winmacro.h>
+#include <asm/asmmacro.h>
 
 /* Registers to not touch at all. */
 #define t_psr        l0 /* Set by caller */
@@ -126,13 +127,13 @@
 	jmpl	%t_retpc + 0x8, %g0	! return to caller
 	 mov	%t_kstack, %sp		! and onto new kernel stack
 
+#define STACK_OFFSET (TASK_UNION_SIZE - (TRACEREG_SZ + REGWIN_SZ))
 trap_setup_from_user:
 	/* We can't use %curptr yet. */
 	LOAD_CURRENT(t_kstack, t_twinmask)
 
-	mov	1, %t_twinmask
-	sll	%t_twinmask, (PAGE_SHIFT + 1), %t_twinmask
-	sub	%t_twinmask, (TRACEREG_SZ + REGWIN_SZ), %t_twinmask
+	sethi	%hi(STACK_OFFSET), %t_twinmask
+	or	%t_twinmask, %lo(STACK_OFFSET), %t_twinmask
 	add	%t_kstack, %t_twinmask, %t_kstack
 
 	mov	1, %t_twinmask
@@ -141,11 +142,18 @@
 	/* Build pt_regs frame. */
 	STORE_PT_ALL(t_kstack, t_psr, t_pc, t_npc, g2)
 
-	/* Clear current->tss.w_saved */
-	mov	1, %curptr
-	sll	%curptr, (PAGE_SHIFT + 1), %curptr
-	sub	%curptr, (TRACEREG_SZ + REGWIN_SZ), %curptr
+#if 0
+	/* If we're sure every task_struct is TASK_UNION_SIZE aligned,
+	   we can speed this up. */
+	sethi	%hi(STACK_OFFSET), %curptr
+	or	%curptr, %lo(STACK_OFFSET), %curptr
 	sub	%t_kstack, %curptr, %curptr
+#else
+	sethi	%hi(~(TASK_UNION_SIZE - 1)), %curptr
+	and	%t_kstack, %curptr, %curptr
+#endif
+
+	/* Clear current->tss.w_saved */
 	st	%g0, [%curptr + AOFF_task_tss + AOFF_thread_w_saved]
 
 	/* See if we are in the trap window. */
@@ -269,9 +277,8 @@
 	.globl	C_LABEL(tsetup_srmmu_stackchk)
 C_LABEL(tsetup_srmmu_stackchk):
 	/* Check results of callers andcc %sp, 0x7, %g0 */
-	sethi	%hi(C_LABEL(page_offset)), %glob_tmp
 	bne	trap_setup_user_stack_is_bolixed
-	 ld	[%glob_tmp + %lo(C_LABEL(page_offset))], %glob_tmp
+	 GET_PAGE_OFFSET(glob_tmp)
 
 	cmp	%glob_tmp, %sp
 	bleu,a	1f
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/kernel/head.S linux/arch/sparc/kernel/head.S
--- v2.1.96/linux/arch/sparc/kernel/head.S	Mon Jan 12 15:15:43 1998
+++ linux/arch/sparc/kernel/head.S	Tue Apr 14 17:44:18 1998
@@ -1,10 +1,11 @@
-/* $Id: head.S,v 1.84 1997/11/19 15:12:01 jj Exp $
+/* $Id: head.S,v 1.90 1998/03/24 18:12:05 jj Exp $
  * head.S: The initial boot code for the Sparc port of Linux.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
  * Copyright (C) 1995 Peter Zaitcev   (Zaitcev@ipmce.su)
  * Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
  * Copyright (C) 1997 Jakub Jelinek   (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1997 Michael A. Griffith (grif@acm.org)
  */
 
 #include <linux/version.h>
@@ -60,9 +61,16 @@
 	.asciz "compatible"
 
 	.align 4
+
+#ifndef CONFIG_SUN4
 sun4_notsup:
-	.asciz  "Sparc-Linux sun4 support not implemented yet\n\n"
+	.asciz	"Sparc-Linux sun4 needs a specially compiled kernel, turn CONFIG_SUN4 on.\n\n"
+	.align 4
+#else
+sun4cdm_notsup:
+	.asciz	"Kernel compiled with CONFIG_SUN4 cannot run on SUN4C/SUN4M/SUN4D\nTurn CONFIG_SUN4 off.\n\n"
 	.align 4
+#endif
 
 sun4e_notsup:
         .asciz  "Sparc-Linux sun4e support does not exist\n\n"
@@ -111,13 +119,14 @@
 #ifndef __SMP__
 t_nmi:	NMI_TRAP                            /* Level 15 (NMI)                */
 #else
-	TRAP_ENTRY(0x1f, linux_trap_ipi15_sun4m)
+	.globl	t_nmi
+t_nmi:	TRAP_ENTRY(0x1f, linux_trap_ipi15_sun4m)
 #endif
 t_racc:	TRAP_ENTRY(0x20, do_reg_access)     /* General Register Access Error */
 t_iacce:BAD_TRAP(0x21)                      /* Instr Access Error            */
 t_bad22:BAD_TRAP(0x22) BAD_TRAP(0x23)
 t_cpdis:TRAP_ENTRY(0x24, do_cp_disabled)    /* Co-Processor Disabled         */
-t_uflsh:TRAP_ENTRY(0x25, do_bad_flush)      /* Unimplemented FLUSH inst.     */
+t_uflsh:SKIP_TRAP(0x25, unimp_flush)        /* Unimplemented FLUSH inst.     */
 t_bad26:BAD_TRAP(0x26) BAD_TRAP(0x27)
 t_cpexc:TRAP_ENTRY(0x28, do_cp_exception)   /* Co-Processor Exception        */
 t_dacce:SPARC_DFAULT                        /* Data Access Error             */
@@ -205,7 +214,7 @@
 	TRAP_ENTRY_INTERRUPT(13) TRAP_ENTRY_INTERRUPT(14)
 	TRAP_ENTRY(0x1f, linux_trap_ipi15_sun4m)
 	TRAP_ENTRY(0x20, do_reg_access) BAD_TRAP(0x21) BAD_TRAP(0x22)
-	BAD_TRAP(0x23) TRAP_ENTRY(0x24, do_cp_disabled) TRAP_ENTRY(0x25, do_bad_flush)
+	BAD_TRAP(0x23) TRAP_ENTRY(0x24, do_cp_disabled) SKIP_TRAP(0x25, unimp_flush)
 	BAD_TRAP(0x26) BAD_TRAP(0x27) TRAP_ENTRY(0x28, do_cp_exception)
 	SRMMU_DFAULT TRAP_ENTRY(0x2a, do_hw_divzero) BAD_TRAP(0x2b) BAD_TRAP(0x2c)
 	BAD_TRAP(0x2d) BAD_TRAP(0x2e) BAD_TRAP(0x2f) BAD_TRAP(0x30) BAD_TRAP(0x31)
@@ -273,7 +282,7 @@
 	TRAP_ENTRY_INTERRUPT(13) TRAP_ENTRY_INTERRUPT(14)
 	TRAP_ENTRY(0x1f, linux_trap_ipi15_sun4m)
 	TRAP_ENTRY(0x20, do_reg_access) BAD_TRAP(0x21) BAD_TRAP(0x22)
-	BAD_TRAP(0x23) TRAP_ENTRY(0x24, do_cp_disabled) TRAP_ENTRY(0x25, do_bad_flush)
+	BAD_TRAP(0x23) TRAP_ENTRY(0x24, do_cp_disabled) SKIP_TRAP(0x25, unimp_flush)
 	BAD_TRAP(0x26) BAD_TRAP(0x27) TRAP_ENTRY(0x28, do_cp_exception)
 	SRMMU_DFAULT TRAP_ENTRY(0x2a, do_hw_divzero) BAD_TRAP(0x2b) BAD_TRAP(0x2c)
 	BAD_TRAP(0x2d) BAD_TRAP(0x2e) BAD_TRAP(0x2f) BAD_TRAP(0x30) BAD_TRAP(0x31)
@@ -341,7 +350,7 @@
 	TRAP_ENTRY_INTERRUPT(13) TRAP_ENTRY_INTERRUPT(14)
 	TRAP_ENTRY(0x1f, linux_trap_ipi15_sun4m)
 	TRAP_ENTRY(0x20, do_reg_access) BAD_TRAP(0x21) BAD_TRAP(0x22)
-	BAD_TRAP(0x23) TRAP_ENTRY(0x24, do_cp_disabled) TRAP_ENTRY(0x25, do_bad_flush)
+	BAD_TRAP(0x23) TRAP_ENTRY(0x24, do_cp_disabled) SKIP_TRAP(0x25, unimp_flush)
 	BAD_TRAP(0x26) BAD_TRAP(0x27) TRAP_ENTRY(0x28, do_cp_exception)
 	SRMMU_DFAULT TRAP_ENTRY(0x2a, do_hw_divzero) BAD_TRAP(0x2b) BAD_TRAP(0x2c)
 	BAD_TRAP(0x2d) BAD_TRAP(0x2e) BAD_TRAP(0x2f) BAD_TRAP(0x30) BAD_TRAP(0x31)
@@ -394,28 +403,26 @@
 	BAD_TRAP(0xfc) BAD_TRAP(0xfd) BAD_TRAP(0xfe) BAD_TRAP(0xff)
 
 #endif
-	.skip 4096
+	.align 4096
 
 /* This was the only reasonable way I could think of to properly align
  * these page-table data structures.
  */
 	.globl C_LABEL(bootup_user_stack)
-	.globl C_LABEL(bootup_kernel_stack)
 	.globl C_LABEL(pg0), C_LABEL(pg1), C_LABEL(pg2), C_LABEL(pg3)
 	.globl C_LABEL(empty_bad_page)
 	.globl C_LABEL(empty_bad_page_table)
 	.globl C_LABEL(empty_zero_page)
 	.globl C_LABEL(swapper_pg_dir)
 C_LABEL(bootup_user_stack):		.skip 0x2000
-C_LABEL(bootup_kernel_stack):		.skip 0x2000
-C_LABEL(swapper_pg_dir):		.skip 0x1000
-C_LABEL(pg0):				.skip 0x1000
-C_LABEL(pg1):				.skip 0x1000
-C_LABEL(pg2):				.skip 0x1000
-C_LABEL(pg3):				.skip 0x1000
-C_LABEL(empty_bad_page):		.skip 0x1000
-C_LABEL(empty_bad_page_table):		.skip 0x1000
-C_LABEL(empty_zero_page):		.skip 0x1000
+C_LABEL(swapper_pg_dir):		.skip PAGE_SIZE
+C_LABEL(pg0):				.skip PAGE_SIZE
+C_LABEL(pg1):				.skip PAGE_SIZE
+C_LABEL(pg2):				.skip PAGE_SIZE
+C_LABEL(pg3):				.skip PAGE_SIZE
+C_LABEL(empty_bad_page):		.skip PAGE_SIZE
+C_LABEL(empty_bad_page_table):		.skip PAGE_SIZE
+C_LABEL(empty_zero_page):		.skip PAGE_SIZE
 
 	.global C_LABEL(root_flags)
 	.global C_LABEL(ram_flags)
@@ -778,18 +785,24 @@
  * your code. Sun probably still does that because they don't even
  * trust their own "OpenBoot" specifications.
  */
-
 		set	LOAD_ADDR, %g6
 		cmp	%o0, %g6		! an old sun4?
-		be	no_sun4_here
+		be	sun4_init
 		 nop
 
 found_version:
-
+#ifdef CONFIG_SUN4
+/* For people who try sun4 kernels, even if Configure.help advises them. */
+		ld	[%g7 + 0x68], %o1
+		set	sun4cdm_notsup, %o0
+		call	%o1
+		 nop
+		b	halt_me
+		 nop
+#endif
 /* Get the machine type via the mysterious romvec node operations. */
 
-		or	%g0, %g7, %l1
-		add	%l1, 0x1c, %l1		
+		add	%g7, 0x1c, %l1		
 		ld	[%l1], %l0
 		ld	[%l0], %l0
 		call 	%l0
@@ -825,10 +838,11 @@
 		set	C_LABEL(cputypval), %o2
 		ldub	[%o2 + 0x4], %l1
 
-		cmp	%l1, 'c'		! We already know we are not
-		be	1f			! on a plain sun4 because of
-		 				! the check for 0x4000 in %o0
-		 cmp	%l1, 'm'		! at start
+		cmp	%l1, ' '
+		be	1f
+		 cmp	%l1, 'c'
+		be	1f
+		 cmp	%l1, 'm'
 		be	1f
 		 cmp	%l1, 'd'
 		be	1f
@@ -853,6 +867,9 @@
 		b	sun4c_continue_boot
 		 nop
 
+/* CPUID in bootbus can be found at PA 0xff0140000 */
+#define SUN4D_BOOTBUS_CPUID     0xf0140000
+
 sun4d_init:
 	/* Need to patch call to handler_irq */
 	set	C_LABEL(patch_handler_irq), %g4
@@ -862,6 +879,21 @@
 	srl	%g5, 2, %g5
 	or	%g5, %g3, %g5
 	st	%g5, [%g4]
+
+#ifdef __SMP__
+	/* Get our CPU id out of bootbus */
+	set     SUN4D_BOOTBUS_CPUID, %g3
+	lduba   [%g3] ASI_M_CTL, %g3
+	and     %g3, 0xf8, %g3
+	srl     %g3, 3, %g4
+	sta     %g4, [%g0] ASI_M_VIKING_TMP1
+	sethi	%hi(boot_cpu_id), %g5
+	stb	%g4, [%g5 + %lo(boot_cpu_id)]
+	sll	%g4, 2, %g4
+	sethi	%hi(boot_cpu_id4), %g5
+	stb	%g4, [%g5 + %lo(boot_cpu_id4)]
+#endif
+
 	/* Fall through to sun4m_init */
 
 sun4m_init:
@@ -974,7 +1006,8 @@
 
 		/* I want a kernel stack NOW! */
 		set	C_LABEL(bootup_user_stack), %g1
-		add	%g1, (PAGE_SIZE - REGWIN_SZ), %sp
+		set	(0x2000 - REGWIN_SZ), %g2
+		add	%g1, %g2, %sp
 		mov	0, %fp			/* And for good luck */
 
 		/* Zero out our BSS section. */
@@ -988,10 +1021,16 @@
 		 add	%o0, 0x1, %o0
 
 		/* Initialize the umask value for init_task just in case.
-		 * But first make current_set[0] point to something useful.
+		 * But first make current_set[boot_cpu_id] point to something useful.
 		 */
 		set	C_LABEL(init_task_union), %g6
 		set	C_LABEL(current_set), %g2
+#ifdef __SMP__
+		sethi	%hi(C_LABEL(boot_cpu_id4)), %g3
+		ldub	[%g3 + %lo(C_LABEL(boot_cpu_id4))], %g3
+		st	%g6, [%g2]
+		add	%g2, %g3, %g2
+#endif
 		st	%g6, [%g2]
 
 		st	%g0, [%g6 + AOFF_task_tss + AOFF_thread_uwinmask]
@@ -1114,19 +1153,28 @@
 		call	halt_me
 		 nop
 
+sun4_init:
+#ifdef CONFIG_SUN4
 /* There, happy now Adrian? */
+		set	C_LABEL(cputypval), %o2		! Let everyone know we
+		set	' ', %o0			! are a "sun4 " architecture
+		stb	%o0, [%o2 + 0x4]		
 
-		/* XXX Fix this... XXX */
-no_sun4_here:
-		sethi	%hi(SUN4_PROM_VECTOR+SUN4_PRINTF), %o1
-		ld	[%o1 + %lo(SUN4_PROM_VECTOR+SUN4_PRINTF)], %o1
-		set	sun4_notsup, %o0
-		call	%o1
+		b got_prop 
 		 nop
-1:
-		ba	1b			! Cannot exit into KMON
+#else
+		sethi   %hi(SUN4_PROM_VECTOR+0x84), %o1
+		ld      [%o1 + %lo(SUN4_PROM_VECTOR+0x84)], %o1
+		set     sun4_notsup, %o0
+		call    %o1	/* printf */
+		 nop
+		sethi   %hi(SUN4_PROM_VECTOR+0xc4), %o1
+		ld      [%o1 + %lo(SUN4_PROM_VECTOR+0xc4)], %o1
+		call    %o1	/* exittomon */
 		 nop
-
+1:		ba      1b                      ! Cannot exit into KMON
+		 nop
+#endif
 no_sun4e_here:
 		ld	[%g7 + 0x68], %o1
 		set	sun4e_notsup, %o0
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/kernel/init_task.c linux/arch/sparc/kernel/init_task.c
--- v2.1.96/linux/arch/sparc/kernel/init_task.c	Tue Mar 17 22:18:14 1998
+++ linux/arch/sparc/kernel/init_task.c	Tue Apr 14 17:44:18 1998
@@ -11,9 +11,9 @@
 static struct signal_struct init_signals = INIT_SIGNALS;
 struct mm_struct init_mm = INIT_MM;
 
-/* .text section in head.S is aligned at 2 page boundry and this gets linked
+/* .text section in head.S is aligned at 8k boundry and this gets linked
  * right after that so that the init_task_union is aligned properly as well.
- * We really don't need this special alignment like the Intel does, but
- * I do it anyways for completeness.
+ * If this is not aligned on a 8k boundry, then you should change code
+ * in etrap.S which assumes it.
  */
 union task_union init_task_union __attribute__((__section__(".text"))) = { INIT_TASK };
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/kernel/irq.c linux/arch/sparc/kernel/irq.c
--- v2.1.96/linux/arch/sparc/kernel/irq.c	Thu Feb 12 20:56:04 1998
+++ linux/arch/sparc/kernel/irq.c	Tue Apr 14 17:44:19 1998
@@ -1,4 +1,4 @@
-/*  $Id: irq.c,v 1.77 1997/11/19 15:33:05 jj Exp $
+/*  $Id: irq.c,v 1.85 1998/03/09 14:03:40 jj Exp $
  *  arch/sparc/kernel/irq.c:  Interrupt request handling routines. On the
  *                            Sparc the IRQ's are basically 'cast in stone'
  *                            and you are supposed to probe the prom's device
@@ -67,22 +67,9 @@
     prom_halt();
 }
 
-void (*enable_irq)(unsigned int) = (void (*)(unsigned int)) irq_panic;
-void (*disable_irq)(unsigned int) = (void (*)(unsigned int)) irq_panic;
-void (*enable_pil_irq)(unsigned int) = (void (*)(unsigned int)) irq_panic;
-void (*disable_pil_irq)(unsigned int) = (void (*)(unsigned int)) irq_panic;
-void (*clear_clock_irq)(void) = irq_panic;
-void (*clear_profile_irq)(int) = (void (*)(int)) irq_panic;
-void (*load_profile_irq)(int, unsigned int) =  (void (*)(int, unsigned int)) irq_panic;
 void (*init_timers)(void (*)(int, void *,struct pt_regs *)) =
     (void (*)(void (*)(int, void *,struct pt_regs *))) irq_panic;
 
-#ifdef __SMP__
-void (*set_cpu_int)(int, int);
-void (*clear_cpu_int)(int, int);
-void (*set_irq_udt)(int);
-#endif
-
 /*
  * Dave Redman (djhr@tadpole.co.uk)
  *
@@ -109,6 +96,9 @@
 {
 	int i, len = 0;
 	struct irqaction * action;
+#ifdef __SMP__
+	int j;
+#endif
 
 	if (sparc_cpu_model == sun4d) {
 		extern int sun4d_get_irq_list(char *);
@@ -119,8 +109,15 @@
 	        action = *(i + irq_action);
 		if (!action) 
 		        continue;
-		len += sprintf(buf+len, "%2d: %8d %c %s",
-			i, kstat.interrupts[i],
+		len += sprintf(buf+len, "%3d: ", i);
+#ifndef __SMP__
+		len += sprintf(buf+len, "%10u ", kstat_irqs(i));
+#else
+		for (j = 0; j < smp_num_cpus; j++)
+			len += sprintf(buf+len, "%10u ",
+				kstat.irqs[cpu_logical_map(j)][i]);
+#endif
+		len += sprintf(buf+len, " %c %s",
 			(action->flags & SA_INTERRUPT) ? '+' : ' ',
 			action->name);
 		for (action=action->next; action; action = action->next) {
@@ -280,7 +277,7 @@
 			do {
 				STUCK;
 				barrier();
-			} while (*((unsigned char *)&global_irq_lock));
+			} while (*((volatile unsigned char *)&global_irq_lock));
 		} while (!spin_trylock(&global_irq_lock));
 	}
 	/*
@@ -352,7 +349,7 @@
 
 	hardirq_enter(cpu);
 	barrier();
-	while (*((unsigned char *)&global_irq_lock)) {
+	while (*((volatile unsigned char *)&global_irq_lock)) {
 		if ((unsigned char) cpu == global_irq_holder) {
 			struct pt_regs *regs = _opaque;
 			int sbh_cnt = atomic_read(&__sparc_bh_counter);
@@ -436,18 +433,20 @@
 	struct irqaction * action;
 	int cpu = smp_processor_id();
 #ifdef __SMP__
-	extern void smp_irq_rotate(int cpu);
+	extern void smp4m_irq_rotate(int cpu);
 #endif
-	
+
 	disable_pil_irq(irq);
+#if 0 /* FIXME: rotating IRQs halts the machine during SCSI probe. -ecd */
 #ifdef __SMP__
 	/* Only rotate on lower priority IRQ's (scsi, ethernet, etc.). */
 	if(irq < 10)
-		smp_irq_rotate(cpu);
+		smp4m_irq_rotate(cpu);
+#endif
 #endif
 	irq_enter(cpu, irq, regs);
 	action = *(irq + irq_action);
-	kstat.interrupts[irq]++;
+	kstat.irqs[cpu][irq]++;
 	do {
 		if (!action || !action->handler)
 			unexpected_irq(irq, 0, regs);
@@ -467,6 +466,7 @@
 
 	disable_pil_irq(irq);
 	irq_enter(cpu, irq, regs);
+	kstat.irqs[cpu][irq]++;
 	floppy_interrupt(irq, dev_id, regs);
 	irq_exit(cpu, irq);
 	enable_pil_irq(irq);
@@ -667,6 +667,7 @@
     
 	switch(sparc_cpu_model) {
 	case sun4c:
+	case sun4:
 		sun4c_init_IRQ();
 		break;
 
@@ -688,4 +689,5 @@
 		prom_printf("Cannot initialize IRQ's on this Sun machine...");
 		break;
 	}
+	btfixup();
 }
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/kernel/process.c linux/arch/sparc/kernel/process.c
--- v2.1.96/linux/arch/sparc/kernel/process.c	Mon Jan 12 15:15:43 1998
+++ linux/arch/sparc/kernel/process.c	Tue Apr 14 17:44:19 1998
@@ -1,4 +1,4 @@
-/*  $Id: process.c,v 1.102 1997/12/01 03:36:31 davem Exp $
+/*  $Id: process.c,v 1.110 1998/04/08 16:15:51 jj Exp $
  *  linux/arch/sparc/kernel/process.c
  *
  *  Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -40,6 +40,7 @@
 #include <asm/elf.h>
 
 extern void fpsave(unsigned long *, unsigned long *, void *, unsigned long *);
+extern void srmmu_check_pgt_cache(void);
 
 struct task_struct *current_set[NR_CPUS] = {&init_task, };
 
@@ -62,7 +63,7 @@
 	current->priority = -100;
 	current->counter = -100;
 	for (;;) {
-		if (sparc_cpu_model == sun4c) {
+		if (ARCH_SUN4C_SUN4) {
 			static int count = HZ;
 			static unsigned long last_jiffies = 0;
 			static unsigned long last_faults = 0;
@@ -91,7 +92,9 @@
 				}
 			}
 			restore_flags(flags);
-		}
+			check_pgt_cache();
+		} else
+			srmmu_check_pgt_cache();
 		schedule();
 	}
 	ret = 0;
@@ -109,6 +112,7 @@
 
 	current->priority = -100;
 	while(1) {
+		srmmu_check_pgt_cache();
 		/*
 		 * tq_scheduler currently assumes we're running in a process
 		 * context (ie that we hold the kernel lock..)
@@ -187,12 +191,12 @@
 
 void show_regwindow(struct reg_window *rw)
 {
-	printk("l0: %08lx l1: %08lx l2: %08lx l3: %08lx\n"
+	printk("l0: %08lx l1: %08lx l2: %08lx l3: %08lx "
 	       "l4: %08lx l5: %08lx l6: %08lx l7: %08lx\n",
 	       rw->locals[0], rw->locals[1], rw->locals[2], rw->locals[3],
 	       rw->locals[4], rw->locals[5], rw->locals[6], rw->locals[7]);
-	printk("i0: %08lx i1: %08lx i2: %08lx i3: %08lx\n"
-	       "i4: %08lx i5: %08lx i6: %08lx i7: %08lx\n",
+	printk("i0: %08lx i1: %08lx i2: %08lx i3: %08lx "
+	       "i4: %08lx i5: %08lx fp: %08lx i7: %08lx\n",
 	       rw->ins[0], rw->ins[1], rw->ins[2], rw->ins[3],
 	       rw->ins[4], rw->ins[5], rw->ins[6], rw->ins[7]);
 }
@@ -201,15 +205,13 @@
 static spinlock_t sparc_backtrace_lock = SPIN_LOCK_UNLOCKED;
 #endif
 
-void show_backtrace(void)
+void __show_backtrace(unsigned long fp)
 {
 	struct reg_window *rw;
 	unsigned long flags;
-	unsigned long fp;
 	int cpu = smp_processor_id();
 
 	spin_lock_irqsave(&sparc_backtrace_lock, flags);
-	__asm__ __volatile__("mov %%i6, %0" : "=r" (fp));
 	rw = (struct reg_window *) fp;
 	while(rw) {
 		printk("CPU[%d]: ARGS[%08lx,%08lx,%08lx,%08lx,%08lx,%08lx] "
@@ -223,6 +225,31 @@
 	spin_unlock_irqrestore(&sparc_backtrace_lock, flags);
 }
 
+void show_backtrace(void)
+{
+	unsigned long fp;
+
+	__asm__ __volatile__(
+		"save %%sp, -64, %%sp\n\t"
+		"save %%sp, -64, %%sp\n\t"
+		"save %%sp, -64, %%sp\n\t"
+		"save %%sp, -64, %%sp\n\t"
+		"save %%sp, -64, %%sp\n\t"
+		"save %%sp, -64, %%sp\n\t"
+		"save %%sp, -64, %%sp\n\t"
+		"save %%sp, -64, %%sp\n\t"
+		"restore\n\t"
+		"restore\n\t"
+		"restore\n\t"
+		"restore\n\t"
+		"restore\n\t"
+		"restore\n\t"
+		"restore\n\t"
+		"restore\n\t"
+		"mov %%i6, %0" : "=r" (fp));
+	__show_backtrace(fp);
+}
+
 #ifdef __SMP__
 void smp_show_backtrace_all_cpus(void)
 {
@@ -236,15 +263,15 @@
 	unsigned long *stk;
 	int i;
 
-	printk("l0: %08lx l1: %08lx l2: %08lx l3: %08lx\n"
+	printk("l0: %08lx l1: %08lx l2: %08lx l3: %08lx "
 	       "l4: %08lx l5: %08lx l6: %08lx l7: %08lx\n",
 	       sf->locals[0], sf->locals[1], sf->locals[2], sf->locals[3],
 	       sf->locals[4], sf->locals[5], sf->locals[6], sf->locals[7]);
-	printk("i0: %08lx i1: %08lx i2: %08lx i3: %08lx\n"
-	       "i4: %08lx i5: %08lx fp: %08lx ret_pc: %08lx\n",
+	printk("i0: %08lx i1: %08lx i2: %08lx i3: %08lx "
+	       "i4: %08lx i5: %08lx fp: %08lx i7: %08lx\n",
 	       sf->ins[0], sf->ins[1], sf->ins[2], sf->ins[3],
 	       sf->ins[4], sf->ins[5], (unsigned long)sf->fp, sf->callers_pc);
-	printk("sp: %08lx x0: %08lx x1: %08lx x2: %08lx\n"
+	printk("sp: %08lx x0: %08lx x1: %08lx x2: %08lx "
 	       "x3: %08lx x4: %08lx x5: %08lx xx: %08lx\n",
 	       (unsigned long)sf->structptr, sf->xargs[0], sf->xargs[1],
 	       sf->xargs[2], sf->xargs[3], sf->xargs[4], sf->xargs[5],
@@ -265,36 +292,32 @@
 #endif
         printk("PSR: %08lx PC: %08lx NPC: %08lx Y: %08lx\n", regs->psr,
 	       regs->pc, regs->npc, regs->y);
-	printk("g0: %08lx g1: %08lx g2: %08lx g3: %08lx\n",
+	printk("g0: %08lx g1: %08lx g2: %08lx g3: %08lx ",
 	       regs->u_regs[0], regs->u_regs[1], regs->u_regs[2],
 	       regs->u_regs[3]);
 	printk("g4: %08lx g5: %08lx g6: %08lx g7: %08lx\n",
 	       regs->u_regs[4], regs->u_regs[5], regs->u_regs[6],
 	       regs->u_regs[7]);
-	printk("o0: %08lx o1: %08lx o2: %08lx o3: %08lx\n",
+	printk("o0: %08lx o1: %08lx o2: %08lx o3: %08lx ",
 	       regs->u_regs[8], regs->u_regs[9], regs->u_regs[10],
 	       regs->u_regs[11]);
-	printk("o4: %08lx o5: %08lx sp: %08lx ret_pc: %08lx\n",
+	printk("o4: %08lx o5: %08lx sp: %08lx o7: %08lx\n",
 	       regs->u_regs[12], regs->u_regs[13], regs->u_regs[14],
 	       regs->u_regs[15]);
 	show_regwindow((struct reg_window *)regs->u_regs[14]);
 }
 
+#if NOTUSED
 void show_thread(struct thread_struct *tss)
 {
 	int i;
 
-	printk("uwinmask:          0x%08lx\n", tss->uwinmask);
-	printk("kregs:             0x%08lx\n", (unsigned long)tss->kregs);
+	printk("uwinmask:          0x%08lx  kregs:             0x%08lx\n", tss->uwinmask, (unsigned long)tss->kregs);
 	show_regs(tss->kregs);
-	printk("sig_address:       0x%08lx\n", tss->sig_address);
-	printk("sig_desc:          0x%08lx\n", tss->sig_desc);
-	printk("ksp:               0x%08lx\n", tss->ksp);
-	printk("kpc:               0x%08lx\n", tss->kpc);
-	printk("kpsr:              0x%08lx\n", tss->kpsr);
-	printk("kwim:              0x%08lx\n", tss->kwim);
-	printk("fork_kpsr:         0x%08lx\n", tss->fork_kpsr);
-	printk("fork_kwim:         0x%08lx\n", tss->fork_kwim);
+	printk("sig_address:       0x%08lx  sig_desc:          0x%08lx\n", tss->sig_address, tss->sig_desc);
+	printk("ksp:               0x%08lx  kpc:               0x%08lx\n", tss->ksp, tss->kpc);
+	printk("kpsr:              0x%08lx  kwim:              0x%08lx\n", tss->kpsr, tss->kwim);
+	printk("fork_kpsr:         0x%08lx  fork_kwim:         0x%08lx\n", tss->fork_kpsr, tss->fork_kwim);
 
 	for (i = 0; i < NSWINS; i++) {
 		if (!tss->rwbuf_stkptrs[i])
@@ -306,19 +329,19 @@
 	printk("w_saved:           0x%08lx\n", tss->w_saved);
 
 	/* XXX missing: float_regs */
-	printk("fsr:               0x%08lx\n", tss->fsr);
-	printk("fpqdepth:          0x%08lx\n", tss->fpqdepth);
+	printk("fsr:               0x%08lx  fpqdepth:          0x%08lx\n", tss->fsr, tss->fpqdepth);
 	/* XXX missing: fpqueue */
 
-	printk("sstk_info.stack:   0x%08lx\n",
-	        (unsigned long)tss->sstk_info.the_stack);
-	printk("sstk_info.status:  0x%08lx\n",
-	        (unsigned long)tss->sstk_info.cur_status);
-	printk("flags:             0x%08lx\n", tss->flags);
-	printk("current_ds:        0x%08x\n", tss->current_ds);
+	printk("sstk_info.stack:   0x%08lx  sstk_info.status:  0x%08lx\n", 
+								(unsigned long)tss->sstk_info.the_stack,
+							        (unsigned long)tss->sstk_info.cur_status);
+	printk("flags:             0x%08lx  current_ds:        0x%08lx\n", tss->flags, tss->current_ds.seg);
+	
+	show_regwindow((struct reg_window *)tss->ksp);
 
 	/* XXX missing: core_exec */
 }
+#endif
 
 /*
  * Free current thread data structures etc..
@@ -367,8 +390,11 @@
 	}
 
 	/* Now, this task is no longer a kernel thread. */
-	current->tss.flags &= ~SPARC_FLAG_KTHREAD;
 	current->tss.current_ds = USER_DS;
+	if (current->tss.flags & SPARC_FLAG_KTHREAD) {
+		current->tss.flags &= ~SPARC_FLAG_KTHREAD;
+		switch_to_context(current);
+	}
 }
 
 static __inline__ void copy_regs(struct pt_regs *dst, struct pt_regs *src)
@@ -475,7 +501,7 @@
 	}
 
 	/* Calculate offset to stack_frame & pt_regs */
-	stack_offset = ((PAGE_SIZE<<1) - TRACEREG_SZ);
+	stack_offset = TASK_UNION_SIZE - TRACEREG_SZ;
 
 	if(regs->psr & PSR_PS)
 		stack_offset -= REGWIN_SZ;
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/kernel/rtrap.S linux/arch/sparc/kernel/rtrap.S
--- v2.1.96/linux/arch/sparc/kernel/rtrap.S	Mon Jan 12 15:15:43 1998
+++ linux/arch/sparc/kernel/rtrap.S	Tue Apr 14 17:44:19 1998
@@ -1,4 +1,4 @@
-/* $Id: rtrap.S,v 1.49 1997/12/14 23:24:24 ecd Exp $
+/* $Id: rtrap.S,v 1.50 1998/02/05 14:18:43 jj Exp $
  * rtrap.S: Return from Sparc trap low-level code.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -296,9 +296,8 @@
 
 	.globl	C_LABEL(srmmu_rett_stackchk)
 C_LABEL(srmmu_rett_stackchk):
-	sethi	%hi(C_LABEL(page_offset)), %g1
 	bne	ret_trap_user_stack_is_bolixed
-	 ld	[%g1 + %lo(C_LABEL(page_offset))], %g1
+	 GET_PAGE_OFFSET(g1)
 	cmp	%g1, %fp
 	bleu	ret_trap_user_stack_is_bolixed
 	 mov	AC_M_SFSR, %g1
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/kernel/sclow.S linux/arch/sparc/kernel/sclow.S
--- v2.1.96/linux/arch/sparc/kernel/sclow.S	Mon Mar 17 14:54:21 1997
+++ linux/arch/sparc/kernel/sclow.S	Tue Apr 14 17:44:19 1998
@@ -11,6 +11,7 @@
 #include <asm/errno.h>
 #include <asm/winmacro.h>
 #include <asm/psr.h>
+#include <asm/page.h>
 
 #define CC_AND_RETT  \
 	set	PSR_C, %l4; \
@@ -94,7 +95,7 @@
 
 	.globl	LABEL(getpagesize)
 LABEL(getpagesize):
-	set	4096, %i0
+	set	PAGE_SIZE, %i0
 	CC_AND_RETT
 
 	/* XXX sys_nice() XXX */
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/kernel/setup.c linux/arch/sparc/kernel/setup.c
--- v2.1.96/linux/arch/sparc/kernel/setup.c	Mon Jan 12 15:15:43 1998
+++ linux/arch/sparc/kernel/setup.c	Tue Apr 14 17:44:19 1998
@@ -1,4 +1,4 @@
-/*  $Id: setup.c,v 1.87 1997/12/18 02:42:42 ecd Exp $
+/*  $Id: setup.c,v 1.93 1998/03/09 14:03:18 jj Exp $
  *  linux/arch/sparc/kernel/setup.c
  *
  *  Copyright (C) 1995  David S. Miller (davem@caip.rutgers.edu)
@@ -43,6 +43,7 @@
 #include <asm/spinlock.h>
 #include <asm/softirq.h>
 #include <asm/hardirq.h>
+#include <asm/machines.h>
 
 struct screen_info screen_info = {
 	0, 0,			/* orig-x, orig-y */
@@ -58,11 +59,6 @@
 
 unsigned int phys_bytes_of_ram, end_of_phys_memory;
 
-unsigned long bios32_init(unsigned long memory_start, unsigned long memory_end)
-{
-	return memory_start;
-}
-
 /* Typing sync at the prom prompt calls the function pointed to by
  * romvec->pv_synchook which I set to the following function.
  * This should sync all filesystems and return, for now it just
@@ -127,7 +123,7 @@
 extern char *console_fb_path;
 static int console_fb = 0;
 #endif
-static unsigned long memory_size = 0;
+static unsigned long memory_size __initdata = 0;
 
 void kernel_enter_debugger(void)
 {
@@ -260,7 +256,7 @@
 extern char cputypval;
 extern unsigned long start, end;
 extern void panic_setup(char *, int *);
-extern unsigned long srmmu_endmem_fixup(unsigned long);
+extern void srmmu_end_memory(unsigned long, unsigned long *);
 extern unsigned long sun_serial_setup(unsigned long);
 
 extern unsigned short root_flags;
@@ -311,6 +307,13 @@
 	if(!strcmp(&cputypval,"sun4d")) { sparc_cpu_model=sun4d; }
 	if(!strcmp(&cputypval,"sun4e")) { sparc_cpu_model=sun4e; }
 	if(!strcmp(&cputypval,"sun4u")) { sparc_cpu_model=sun4u; }
+
+#ifdef CONFIG_SUN4
+	if (sparc_cpu_model != sun4) {
+		prom_printf("This kernel is for Sun4 architecture only.\n");
+		prom_halt();
+	}
+#endif
 #if CONFIG_AP1000
 	sparc_cpu_model=ap1000;
 	strcpy(&cputypval, "ap+");
@@ -320,12 +323,10 @@
 	switch(sparc_cpu_model) {
 	case sun4:
 		printk("SUN4\n");
-		sun4c_probe_vac();
 		packed = 0;
 		break;
 	case sun4c:
 		printk("SUN4C\n");
-		sun4c_probe_vac();
 		packed = 0;
 		break;
 	case sun4m:
@@ -356,6 +357,8 @@
 	boot_flags_init(*cmdline_p);
 
 	idprom_init();
+	if (ARCH_SUN4C_SUN4)
+		sun4c_probe_vac();
 	load_mmu();
 	total = prom_probe_memory();
 	*memory_start_p = (((unsigned long) &end));
@@ -374,41 +377,37 @@
 				}
 			}
 		}
-	} else {
-		unsigned int sum = 0;
+		*memory_end_p = (end_of_phys_memory + KERNBASE);
+	} else
+		srmmu_end_memory(memory_size, memory_end_p);
 
-		for(i = 0; sp_banks[i].num_bytes != 0; i++) {
-			sum += sp_banks[i].num_bytes;
-			if (memory_size) {
-				if (sum > memory_size) {
-					sp_banks[i].num_bytes -=
-							(sum - memory_size);
-					sum = memory_size;
-					sp_banks[++i].base_addr = 0xdeadbeef;
-					sp_banks[i].num_bytes = 0;
-					break;
-				}
-			}
+	if (!root_flags)
+		root_mountflags &= ~MS_RDONLY;
+	ROOT_DEV = to_kdev_t(root_dev);
+#ifdef CONFIG_BLK_DEV_RAM
+	rd_image_start = ram_flags & RAMDISK_IMAGE_START_MASK;
+	rd_prompt = ((ram_flags & RAMDISK_PROMPT_FLAG) != 0);
+	rd_doload = ((ram_flags & RAMDISK_LOAD_FLAG) != 0);	
+#endif
+#ifdef CONFIG_BLK_DEV_INITRD
+	if (ramdisk_image) {
+		initrd_start = ramdisk_image;
+		if (initrd_start < KERNBASE) initrd_start += KERNBASE;
+		initrd_end = initrd_start + ramdisk_size;
+		if (initrd_end > *memory_end_p) {
+			printk(KERN_CRIT "initrd extends beyond end of memory "
+		                 	 "(0x%08lx > 0x%08lx)\ndisabling initrd\n",
+		       			 initrd_end,*memory_end_p);
+			initrd_start = 0;
+		}
+		if (initrd_start >= *memory_start_p && initrd_start < *memory_start_p + 2 * PAGE_SIZE) {
+			initrd_below_start_ok = 1;
+			*memory_start_p = PAGE_ALIGN (initrd_end);
 		}
-		end_of_phys_memory = sum;
 	}
-
+#endif	
 	prom_setsync(prom_sync_me);
 
-	*memory_end_p = (end_of_phys_memory + KERNBASE);
-	if((sparc_cpu_model == sun4c) ||
-	   (sparc_cpu_model == sun4))
-		goto not_relevant;
-	if(end_of_phys_memory >= 0x0d000000) {
-		*memory_end_p = 0xfd000000;
-	} else {
-		if((sparc_cpu_model == sun4m) ||
-		   (sparc_cpu_model == sun4d) ||
-		   (sparc_cpu_model == ap1000))
-			*memory_end_p = srmmu_endmem_fixup(*memory_end_p);
-	}
-not_relevant:
-		
 #ifdef CONFIG_SUN_SERIAL
 	*memory_start_p = sun_serial_setup(*memory_start_p); /* set this up ASAP */
 #endif
@@ -459,31 +458,6 @@
 		breakpoint();
 	}
 
-	if (!root_flags)
-		root_mountflags &= ~MS_RDONLY;
-	ROOT_DEV = to_kdev_t(root_dev);
-#ifdef CONFIG_BLK_DEV_RAM
-	rd_image_start = ram_flags & RAMDISK_IMAGE_START_MASK;
-	rd_prompt = ((ram_flags & RAMDISK_PROMPT_FLAG) != 0);
-	rd_doload = ((ram_flags & RAMDISK_LOAD_FLAG) != 0);	
-#endif
-#ifdef CONFIG_BLK_DEV_INITRD
-	if (ramdisk_image) {
-		initrd_start = ramdisk_image;
-		if (initrd_start < KERNBASE) initrd_start += KERNBASE;
-		initrd_end = initrd_start + ramdisk_size;
-		if (initrd_end > *memory_end_p) {
-			printk(KERN_CRIT "initrd extends beyond end of memory "
-		                 	 "(0x%08lx > 0x%08lx)\ndisabling initrd\n",
-		       			 initrd_end,*memory_end_p);
-			initrd_start = 0;
-		}
-		if (initrd_start >= *memory_start_p && initrd_start < *memory_start_p + 2 * PAGE_SIZE) {
-			initrd_below_start_ok = 1;
-			*memory_start_p = PAGE_ALIGN (initrd_end);
-		}
-	}
-#endif	
 
 	/* Due to stack alignment restrictions and assumptions... */
 	init_task.mm->mmap->vm_page_prot = PAGE_SHARED;
@@ -504,13 +478,12 @@
 extern char *sparc_cpu_type[];
 extern char *sparc_fpu_type[];
 
-extern char *smp_info(void);
-
 int get_cpuinfo(char *buffer)
 {
-	int cpuid=get_cpuid();
+	int cpuid=hard_smp_processor_id();
+	int len;
 
-	return sprintf(buffer, "cpu\t\t: %s\n"
+	len = sprintf(buffer, "cpu\t\t: %s\n"
             "fpu\t\t: %s\n"
             "promlib\t\t: Version %d Revision %d\n"
             "prom\t\t: %d.%d\n"
@@ -519,34 +492,23 @@
 	    "ncpus active\t: %d\n"
 #ifndef __SMP__
             "BogoMips\t: %lu.%02lu\n"
-#else
-	    "Cpu0Bogo\t: %lu.%02lu\n"
-	    "Cpu1Bogo\t: %lu.%02lu\n"
-	    "Cpu2Bogo\t: %lu.%02lu\n"
-	    "Cpu3Bogo\t: %lu.%02lu\n"
-#endif
-	    "%s"
-#ifdef __SMP__
-	    "%s"
 #endif
 	    ,
-            sparc_cpu_type[cpuid],
-            sparc_fpu_type[cpuid],
+	    sparc_cpu_type[cpuid] ? : "undetermined",
+	    sparc_fpu_type[cpuid] ? : "undetermined",
             romvec->pv_romvers, prom_rev, romvec->pv_printrev >> 16, (short)romvec->pv_printrev,
             &cputypval,
-	    linux_num_cpus, smp_num_cpus,
+	    linux_num_cpus, smp_num_cpus
 #ifndef __SMP__
-            loops_per_sec/500000, (loops_per_sec/5000) % 100,
-#else
-	    cpu_data[0].udelay_val/500000, (cpu_data[0].udelay_val/5000)%100,
-	    cpu_data[1].udelay_val/500000, (cpu_data[1].udelay_val/5000)%100,
-	    cpu_data[2].udelay_val/500000, (cpu_data[2].udelay_val/5000)%100,
-	    cpu_data[3].udelay_val/500000, (cpu_data[3].udelay_val/5000)%100,
+	    , loops_per_sec/500000, (loops_per_sec/5000) % 100
 #endif
-	    mmu_info()
+	    );
 #ifdef __SMP__
-	    , smp_info()
+	len += smp_bogo_info(buffer + len);
 #endif
-            );
-
+	len += mmu_info(buffer + len);
+#ifdef __SMP__
+	len += smp_info(buffer + len);
+#endif
+	return len;
 }
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/kernel/signal.c linux/arch/sparc/kernel/signal.c
--- v2.1.96/linux/arch/sparc/kernel/signal.c	Mon Jan 12 15:15:43 1998
+++ linux/arch/sparc/kernel/signal.c	Tue Apr 14 17:44:19 1998
@@ -1,4 +1,4 @@
-/*  $Id: signal.c,v 1.77 1997/12/22 03:06:32 ecd Exp $
+/*  $Id: signal.c,v 1.79 1998/04/04 07:11:41 davem Exp $
  *  linux/arch/sparc/kernel/signal.c
  *
  *  Copyright (C) 1991, 1992  Linus Torvalds
@@ -78,9 +78,20 @@
 	__siginfo_fpu_t		fpu_state;
 };
 
+struct rt_signal_frame {
+	struct sparc_stackf	ss;
+	siginfo_t		info;
+	struct pt_regs		regs;
+	sigset_t		mask;
+	__siginfo_fpu_t		*fpu_save;
+	unsigned int		insns [2];
+	__siginfo_fpu_t		fpu_state;
+};
+
 /* Align macros */
 #define SF_ALIGNEDSZ  (((sizeof(struct signal_sframe) + 7) & (~7)))
 #define NF_ALIGNEDSZ  (((sizeof(struct new_signal_frame) + 7) & (~7)))
+#define RT_ALIGNEDSZ  (((sizeof(struct rt_signal_frame) + 7) & (~7)))
 
 /*
  * atomically swap in the new signal mask, and wait for a signal.
@@ -318,7 +329,60 @@
 
 asmlinkage void do_rt_sigreturn(struct pt_regs *regs)
 {
-	printk("XXX: FIXME: write do_rt_sigreturn\n");
+	struct rt_signal_frame *sf;
+	unsigned int psr, pc, npc;
+	__siginfo_fpu_t *fpu_save;
+	sigset_t set;
+
+	synchronize_user_stack();
+	sf = (struct rt_signal_frame *) regs->u_regs[UREG_FP];
+	if(verify_area(VERIFY_READ, sf, sizeof(*sf)) ||
+	   (((unsigned long) sf) & 0x03))
+		goto segv;
+
+	get_user(pc, &sf->regs.pc);
+	__get_user(npc, &sf->regs.npc);
+	if((pc | npc) & 0x03)
+		goto segv;
+
+	regs->pc = pc;
+	regs->npc = npc;
+
+	__get_user(regs->y, &sf->regs.y);
+	__get_user(psr, &sf->regs.psr);
+
+	__get_user(regs->u_regs[UREG_G1], &sf->regs.u_regs[UREG_G1]);
+	__get_user(regs->u_regs[UREG_G2], &sf->regs.u_regs[UREG_G2]);
+	__get_user(regs->u_regs[UREG_G3], &sf->regs.u_regs[UREG_G3]);
+	__get_user(regs->u_regs[UREG_G4], &sf->regs.u_regs[UREG_G4]);
+	__get_user(regs->u_regs[UREG_G5], &sf->regs.u_regs[UREG_G5]);
+	__get_user(regs->u_regs[UREG_G6], &sf->regs.u_regs[UREG_G6]);
+	__get_user(regs->u_regs[UREG_G7], &sf->regs.u_regs[UREG_G7]);
+	__get_user(regs->u_regs[UREG_I0], &sf->regs.u_regs[UREG_I0]);
+	__get_user(regs->u_regs[UREG_I1], &sf->regs.u_regs[UREG_I1]);
+	__get_user(regs->u_regs[UREG_I2], &sf->regs.u_regs[UREG_I2]);
+	__get_user(regs->u_regs[UREG_I3], &sf->regs.u_regs[UREG_I3]);
+	__get_user(regs->u_regs[UREG_I4], &sf->regs.u_regs[UREG_I4]);
+	__get_user(regs->u_regs[UREG_I5], &sf->regs.u_regs[UREG_I5]);
+	__get_user(regs->u_regs[UREG_I6], &sf->regs.u_regs[UREG_I6]);
+	__get_user(regs->u_regs[UREG_I7], &sf->regs.u_regs[UREG_I7]);
+
+	regs->psr = (regs->psr & ~PSR_ICC) | (psr & PSR_ICC);
+
+	__get_user(fpu_save, &sf->fpu_save);
+	if(fpu_save)
+		restore_fpu_state(regs, &sf->fpu_state);
+	if(copy_from_user(&set, &sf->mask, sizeof(sigset_t)))
+		goto segv;
+	sigdelsetmask(&set, ~_BLOCKABLE);
+	spin_lock_irq(&current->sigmask_lock);
+	current->blocked = set;
+	recalc_sigpending(current);
+	spin_unlock_irq(&current->sigmask_lock);
+	return;
+segv:
+	lock_kernel();
+	do_exit(SIGSEGV);
 }
 
 /* Checks if the fp is valid */
@@ -514,7 +578,63 @@
 new_setup_rt_frame(struct k_sigaction *ka, struct pt_regs *regs,
 		   int signo, sigset_t *oldset, siginfo_t *info)
 {
-	printk("XXX: FIXME: new_setup_rt_frame unimplemented\n");
+	struct rt_signal_frame *sf;
+	int sigframe_size;
+	unsigned int psr;
+	int i;
+
+	synchronize_user_stack();
+	sigframe_size = RT_ALIGNEDSZ;
+	if(!current->used_math)
+		sigframe_size -= sizeof(__siginfo_fpu_t);
+	sf = (struct rt_signal_frame *)(regs->u_regs[UREG_FP] - sigframe_size);
+	if(invalid_frame_pointer(sf, sigframe_size))
+		goto sigill;
+	if(current->tss.w_saved != 0)
+		goto sigill;
+
+	put_user(regs->pc, &sf->regs.pc);
+	__put_user(regs->npc, &sf->regs.npc);
+	__put_user(regs->y, &sf->regs.y);
+	psr = regs->psr;
+	if(current->used_math)
+		psr |= PSR_EF;
+	__put_user(psr, &sf->regs.psr);
+	for(i = 0; i < 16; i++)
+		__put_user(regs->u_regs[i], &sf->regs.u_regs[i]);
+	if(psr & PSR_EF) {
+		save_fpu_state(regs, &sf->fpu_state);
+		__put_user(&sf->fpu_state, &sf->fpu_save);
+	} else {
+		__put_user(0, &sf->fpu_save);
+	}
+	__copy_to_user(&sf->mask, &oldset->sig[0], sizeof(sigset_t));
+	copy_to_user(sf, (char *) regs->u_regs [UREG_FP],
+		     sizeof (struct reg_window));	
+
+	regs->u_regs[UREG_FP] = (unsigned long) sf;
+	regs->u_regs[UREG_I0] = signo;
+	regs->u_regs[UREG_I1] = (unsigned long) &sf->info;
+
+	regs->pc = (unsigned long) ka->sa.sa_handler;
+	regs->npc = (regs->pc + 4);
+
+	if(ka->ka_restorer)
+		regs->u_regs[UREG_I7] = (unsigned long)ka->ka_restorer;
+	else {
+		regs->u_regs[UREG_I7] = (unsigned long)(&(sf->insns[0]) - 2);
+
+		__put_user(0x821020d8, &sf->insns[0]); /* mov __NR_sigreturn, %g1 */
+		__put_user(0x91d02010, &sf->insns[1]); /* t 0x10 */
+
+		/* Flush instruction space. */
+		flush_sig_insns(current->mm, (unsigned long) &(sf->insns[0]));
+	}
+	return;
+
+sigill:
+	lock_kernel();
+	do_exit(SIGILL);
 }
 
 /* Setup a Solaris stack frame */
@@ -783,6 +903,7 @@
 		spin_lock_irq(&current->sigmask_lock);
 		sigorsets(&current->blocked,&current->blocked,&ka->sa.sa_mask);
 		sigaddset(&current->blocked, signr);
+		recalc_sigpending(current);
 		spin_unlock_irq(&current->sigmask_lock);
 	}
 }
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/kernel/smp.c linux/arch/sparc/kernel/smp.c
--- v2.1.96/linux/arch/sparc/kernel/smp.c	Mon Feb 23 18:12:03 1998
+++ linux/arch/sparc/kernel/smp.c	Tue Apr 14 17:44:19 1998
@@ -1,6 +1,7 @@
 /* smp.c: Sparc SMP support.
  *
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
  */
 
 #include <linux/config.h> /* for CONFIG_PROFILE */
@@ -13,6 +14,7 @@
 #include <linux/smp_lock.h>
 #include <linux/interrupt.h>
 #include <linux/kernel_stat.h>
+#include <linux/init.h>
 
 #include <asm/ptrace.h>
 #include <asm/atomic.h>
@@ -34,34 +36,29 @@
 #define IRQ_STOP_CPU		14
 #define IRQ_CROSS_CALL		15
 
-extern ctxd_t *srmmu_ctx_table_phys;
-extern int linux_num_cpus;
-
-extern void calibrate_delay(void);
-
-/* XXX Let's get rid of this thing if we can... */
-extern struct task_struct *current_set[NR_CPUS];
-
 volatile int smp_processors_ready = 0;
-
 unsigned long cpu_present_map = 0;
 int smp_num_cpus = 1;
 int smp_threads_ready=0;
 unsigned char mid_xlate[NR_CPUS] = { 0, 0, 0, 0, };
-volatile unsigned long cpu_callin_map[NR_CPUS] = {0,};
+volatile unsigned long cpu_callin_map[NR_CPUS] __initdata = {0,};
+#ifdef NOTUSED
 volatile unsigned long smp_spinning[NR_CPUS] = { 0, };
+#endif
 unsigned long smp_proc_in_lock[NR_CPUS] = { 0, };
 struct cpuinfo_sparc cpu_data[NR_CPUS];
-static unsigned char boot_cpu_id = 0;
-static int smp_activated = 0;
+unsigned long cpu_offset[NR_CPUS];
+unsigned char boot_cpu_id = 0;
+unsigned char boot_cpu_id4 = 0; /* boot_cpu_id << 2 */
+int smp_activated = 0;
 volatile int cpu_number_map[NR_CPUS];
-volatile int cpu_logical_map[NR_CPUS];
+volatile int __cpu_logical_map[NR_CPUS];
 
 /* The only guaranteed locking primitive available on all Sparc
  * processors is 'ldstub [%reg + immediate], %dest_reg' which atomically
  * places the current byte at the effective address into dest_reg and
  * places 0xff there afterwards.  Pretty lame locking primitive
- * compared to the Alpha and the intel no?  Most Sparcs have 'swap'
+ * compared to the Alpha and the Intel no?  Most Sparcs have 'swap'
  * instruction which is much better...
  */
 struct klock_info klock_info = { KLOCK_CLEAR, 0 };
@@ -69,42 +66,11 @@
 volatile unsigned long ipi_count;
 
 volatile int smp_process_available=0;
-
-/*#define SMP_DEBUG*/
-
-#ifdef SMP_DEBUG
-#define SMP_PRINTK(x)	printk x
-#else
-#define SMP_PRINTK(x)
-#endif
-
 volatile int smp_commenced = 0;
 
-static char smp_buf[512];
-
 /* Not supported on Sparc yet. */
-void smp_setup(char *str, int *ints)
-{
-}
-
-char *smp_info(void)
+__initfunc(void smp_setup(char *str, int *ints))
 {
-	sprintf(smp_buf,
-"        CPU0\t\tCPU1\t\tCPU2\t\tCPU3\n"
-"State:  %s\t\t%s\t\t%s\t\t%s\n",
-(cpu_present_map & 1) ? ((klock_info.akp == 0) ? "akp" : "online") : "offline",
-(cpu_present_map & 2) ? ((klock_info.akp == 1) ? "akp" : "online") : "offline",
-(cpu_present_map & 4) ? ((klock_info.akp == 2) ? "akp" : "online") : "offline",
-(cpu_present_map & 8) ? ((klock_info.akp == 3) ? "akp" : "online") : "offline");
-	return smp_buf;
-}
-
-static inline unsigned long swap(volatile unsigned long *ptr, unsigned long val)
-{
-	__asm__ __volatile__("swap [%1], %0\n\t" :
-			     "=&r" (val), "=&r" (ptr) :
-			     "0" (val), "1" (ptr));
-	return val;
 }
 
 /*
@@ -112,12 +78,12 @@
  *	a given CPU
  */
 
-void smp_store_cpu_info(int id)
+__initfunc(void smp_store_cpu_info(int id))
 {
 	cpu_data[id].udelay_val = loops_per_sec; /* this is it on sparc. */
 }
 
-void smp_commence(void)
+__initfunc(void smp_commence(void))
 {
 	/*
 	 *	Lets the callin's below out of their loop.
@@ -129,65 +95,19 @@
 	local_flush_tlb_all();
 }
 
-static void smp_setup_percpu_timer(void);
-
-void smp_callin(void)
-{
-	int cpuid = hard_smp_processor_id();
-
-	local_flush_cache_all();
-	local_flush_tlb_all();
-	set_irq_udt(mid_xlate[boot_cpu_id]);
-
-	/* Get our local ticker going. */
-	smp_setup_percpu_timer();
-
-	calibrate_delay();
-	smp_store_cpu_info(cpuid);
-	local_flush_cache_all();
-	local_flush_tlb_all();
-
-	/* Allow master to continue. */
-	swap((unsigned long *)&cpu_callin_map[cpuid], 1);
-	local_flush_cache_all();
-	local_flush_tlb_all();
-
-	while(!task[cpuid] || current_set[cpuid] != task[cpuid])
-		barrier();
-
-	/* Fix idle thread fields. */
-	__asm__ __volatile__("ld [%0], %%g6\n\t"
-			     : : "r" (&current_set[cpuid])
-			     : "memory" /* paranoid */);
-	current->mm->mmap->vm_page_prot = PAGE_SHARED;
-	current->mm->mmap->vm_start = PAGE_OFFSET;
-	current->mm->mmap->vm_end = init_task.mm->mmap->vm_end;
-	
-	while(!smp_commenced)
-		barrier();
-
-	local_flush_cache_all();
-	local_flush_tlb_all();
-
-	__sti();
-}
-
-extern int cpu_idle(void *unused);
-extern void init_IRQ(void);
-
 /* Only broken Intel needs this, thus it should not even be referenced
  * globally...
  */
-void initialize_secondary(void)
+__initfunc(void initialize_secondary(void))
 {
 }
 
+extern int cpu_idle(void *unused);
+
 /* Activate a secondary processor. */
 int start_secondary(void *unused)
 {
-	trap_init();
-	init_IRQ();
-	smp_callin();
+	prom_printf("Start secondary called. Should not happen\n");
 	return cpu_idle(NULL);
 }
 
@@ -201,255 +121,25 @@
  *	Cycle through the processors asking the PROM to start each one.
  */
  
-extern struct prom_cpuinfo linux_cpus[NCPUS];
-static struct linux_prom_registers penguin_ctable;
-
-void smp_boot_cpus(void)
-{
-	int cpucount = 0;
-	int i = 0;
-	int first, prev;
-
-	printk("Entering SMP Mode...\n");
-
-	penguin_ctable.which_io = 0;
-	penguin_ctable.phys_addr = (unsigned int) srmmu_ctx_table_phys;
-	penguin_ctable.reg_size = 0;
-
-	__sti();
-	cpu_present_map = 0;
-	for(i=0; i < linux_num_cpus; i++)
-		cpu_present_map |= (1<<i);
-	for(i=0; i < NR_CPUS; i++)
-		cpu_number_map[i] = -1;
-	for(i=0; i < NR_CPUS; i++)
-		cpu_logical_map[i] = -1;
-	mid_xlate[boot_cpu_id] = (linux_cpus[boot_cpu_id].mid & ~8);
-	cpu_number_map[boot_cpu_id] = 0;
-	cpu_logical_map[0] = boot_cpu_id;
-	klock_info.akp = boot_cpu_id;
-	current->processor = boot_cpu_id;
-	smp_store_cpu_info(boot_cpu_id);
-	set_irq_udt(mid_xlate[boot_cpu_id]);
-	smp_setup_percpu_timer();
-	local_flush_cache_all();
-	if(linux_num_cpus == 1)
-		return;  /* Not an MP box. */
-	for(i = 0; i < NR_CPUS; i++) {
-		if(i == boot_cpu_id)
-			continue;
-
-		if(cpu_present_map & (1 << i)) {
-			extern unsigned long sparc_cpu_startup;
-			unsigned long *entry = &sparc_cpu_startup;
-			struct task_struct *p;
-			int timeout;
-
-			/* Cook up an idler for this guy. */
-			kernel_thread(start_secondary, NULL, CLONE_PID);
-
-			p = task[++cpucount];
-
-			p->processor = i;
-			current_set[i] = p;
-
-			/* See trampoline.S for details... */
-			entry += ((i-1) * 3);
-
-			/* whirrr, whirrr, whirrrrrrrrr... */
-			printk("Starting CPU %d at %p\n", i, entry);
-			mid_xlate[i] = (linux_cpus[i].mid & ~8);
-			local_flush_cache_all();
-			prom_startcpu(linux_cpus[i].prom_node,
-				      &penguin_ctable, 0, (char *)entry);
-
-			/* wheee... it's going... */
-			for(timeout = 0; timeout < 5000000; timeout++) {
-				if(cpu_callin_map[i])
-					break;
-				udelay(100);
-			}
-			if(cpu_callin_map[i]) {
-				/* Another "Red Snapper". */
-				cpu_number_map[i] = i;
-				cpu_logical_map[i] = i;
-			} else {
-				cpucount--;
-				printk("Processor %d is stuck.\n", i);
-			}
-		}
-		if(!(cpu_callin_map[i])) {
-			cpu_present_map &= ~(1 << i);
-			cpu_number_map[i] = -1;
-		}
-	}
-	local_flush_cache_all();
-	if(cpucount == 0) {
-		printk("Error: only one Processor found.\n");
-		cpu_present_map = (1 << smp_processor_id());
-	} else {
-		unsigned long bogosum = 0;
-		for(i = 0; i < NR_CPUS; i++) {
-			if(cpu_present_map & (1 << i))
-				bogosum += cpu_data[i].udelay_val;
-		}
-		printk("Total of %d Processors activated (%lu.%02lu BogoMIPS).\n",
-		       cpucount + 1,
-		       (bogosum + 2500)/500000,
-		       ((bogosum + 2500)/5000)%100);
-		smp_activated = 1;
-		smp_num_cpus = cpucount + 1;
-	}
-
-	/* Setup CPU list for IRQ distribution scheme. */
-	first = prev = -1;
-	for(i = 0; i < NR_CPUS; i++) {
-		if(cpu_present_map & (1 << i)) {
-			if(first == -1)
-				first = i;
-			if(prev != -1)
-				cpu_data[i].next = i;
-			cpu_data[i].mid = mid_xlate[i];
-			prev = i;
-		}
-	}
-	cpu_data[prev].next = first;
-
-	/* Ok, they are spinning and ready to go. */
-	smp_processors_ready = 1;
-}
+extern struct prom_cpuinfo linux_cpus[NR_CPUS];
+struct linux_prom_registers smp_penguin_ctable __initdata = { 0 };
 
-/* At each hardware IRQ, we get this called to forward IRQ reception
- * to the next processor.  The caller must disable the IRQ level being
- * serviced globally so that there are no double interrupts received.
- */
-void smp_irq_rotate(int cpu)
+__initfunc(void smp_boot_cpus(void))
 {
-	if(smp_processors_ready)
-		set_irq_udt(cpu_data[cpu_data[cpu].next].mid);
-}
-
-/* Cross calls, in order to work efficiently and atomically do all
- * the message passing work themselves, only stopcpu and reschedule
- * messages come through here.
- */
-void smp_message_pass(int target, int msg, unsigned long data, int wait)
-{
-	static unsigned long smp_cpu_in_msg[NR_CPUS];
-	unsigned long mask;
-	int me = smp_processor_id();
-	int irq, i;
-
-	if(msg == MSG_RESCHEDULE) {
-		irq = IRQ_RESCHEDULE;
-
-		if(smp_cpu_in_msg[me])
-			return;
-	} else if(msg == MSG_STOP_CPU) {
-		irq = IRQ_STOP_CPU;
-	} else {
-		goto barf;
-	}
-
-	smp_cpu_in_msg[me]++;
-	if(target == MSG_ALL_BUT_SELF || target == MSG_ALL) {
-		mask = cpu_present_map;
-		if(target == MSG_ALL_BUT_SELF)
-			mask &= ~(1 << me);
-		for(i = 0; i < 4; i++) {
-			if(mask & (1 << i))
-				set_cpu_int(mid_xlate[i], irq);
-		}
-	} else {
-		set_cpu_int(mid_xlate[target], irq);
-	}
-	smp_cpu_in_msg[me]--;
-
-	return;
-barf:
-	printk("Yeeee, trying to send SMP msg(%d) on cpu %d\n", msg, me);
-	panic("Bogon SMP message pass.");
-}
-
-struct smp_funcall {
-	smpfunc_t func;
-	unsigned long arg1;
-	unsigned long arg2;
-	unsigned long arg3;
-	unsigned long arg4;
-	unsigned long arg5;
-	unsigned long processors_in[NR_CPUS];  /* Set when ipi entered. */
-	unsigned long processors_out[NR_CPUS]; /* Set when ipi exited. */
-} ccall_info;
-
-static spinlock_t cross_call_lock = SPIN_LOCK_UNLOCKED;
-
-/* Cross calls must be serialized, at least currently. */
-void smp_cross_call(smpfunc_t func, unsigned long arg1, unsigned long arg2,
-		    unsigned long arg3, unsigned long arg4, unsigned long arg5)
-{
-	if(smp_processors_ready) {
-		register int ncpus = smp_num_cpus;
-		unsigned long flags;
-
-		spin_lock_irqsave(&cross_call_lock, flags);
-
-		/* Init function glue. */
-		ccall_info.func = func;
-		ccall_info.arg1 = arg1;
-		ccall_info.arg2 = arg2;
-		ccall_info.arg3 = arg3;
-		ccall_info.arg4 = arg4;
-		ccall_info.arg5 = arg5;
-
-		/* Init receive/complete mapping, plus fire the IPI's off. */
-		{
-			register void (*send_ipi)(int,int) = set_cpu_int;
-			register unsigned long mask;
-			register int i;
-
-			mask = (cpu_present_map & ~(1 << smp_processor_id()));
-			for(i = 0; i < ncpus; i++) {
-				if(mask & (1 << i)) {
-					ccall_info.processors_in[i] = 0;
-					ccall_info.processors_out[i] = 0;
-					send_ipi(mid_xlate[i], IRQ_CROSS_CALL);
-				} else {
-					ccall_info.processors_in[i] = 1;
-					ccall_info.processors_out[i] = 1;
-				}
-			}
-		}
-
-		/* First, run local copy. */
-		func(arg1, arg2, arg3, arg4, arg5);
-
-		{
-			register int i;
-
-			i = 0;
-			do {
-				while(!ccall_info.processors_in[i])
-					barrier();
-			} while(++i < ncpus);
-
-			i = 0;
-			do {
-				while(!ccall_info.processors_out[i])
-					barrier();
-			} while(++i < ncpus);
-		}
-
-		spin_unlock_irqrestore(&cross_call_lock, flags);
-	} else
-		func(arg1, arg2, arg3, arg4, arg5); /* Just need to run local copy. */
+	extern void smp4m_boot_cpus(void);
+	extern void smp4d_boot_cpus(void);
+	
+	if (sparc_cpu_model == sun4m)
+		smp4m_boot_cpus();
+	else
+		smp4d_boot_cpus();
 }
 
 void smp_flush_cache_all(void)
-{ xc0((smpfunc_t) local_flush_cache_all); }
+{ xc0((smpfunc_t) BTFIXUP_CALL(local_flush_cache_all)); }
 
 void smp_flush_tlb_all(void)
-{ xc0((smpfunc_t) local_flush_tlb_all); }
+{ xc0((smpfunc_t) BTFIXUP_CALL(local_flush_tlb_all)); }
 
 void smp_flush_cache_mm(struct mm_struct *mm)
 { 
@@ -457,7 +147,7 @@
 		if(mm->cpu_vm_mask == (1 << smp_processor_id()))
 			local_flush_cache_mm(mm);
 		else
-			xc1((smpfunc_t) local_flush_cache_mm, (unsigned long) mm);
+			xc1((smpfunc_t) BTFIXUP_CALL(local_flush_cache_mm), (unsigned long) mm);
 	}
 }
 
@@ -467,7 +157,7 @@
 		if(mm->cpu_vm_mask == (1 << smp_processor_id())) {
 			local_flush_tlb_mm(mm);
 		} else {
-			xc1((smpfunc_t) local_flush_tlb_mm, (unsigned long) mm);
+			xc1((smpfunc_t) BTFIXUP_CALL(local_flush_tlb_mm), (unsigned long) mm);
 			if(mm->count == 1 && current->mm == mm)
 				mm->cpu_vm_mask = (1 << smp_processor_id());
 		}
@@ -481,7 +171,7 @@
 		if(mm->cpu_vm_mask == (1 << smp_processor_id()))
 			local_flush_cache_range(mm, start, end);
 		else
-			xc3((smpfunc_t) local_flush_cache_range, (unsigned long) mm,
+			xc3((smpfunc_t) BTFIXUP_CALL(local_flush_cache_range), (unsigned long) mm,
 			    start, end);
 	}
 }
@@ -493,7 +183,7 @@
 		if(mm->cpu_vm_mask == (1 << smp_processor_id()))
 			local_flush_tlb_range(mm, start, end);
 		else
-			xc3((smpfunc_t) local_flush_tlb_range, (unsigned long) mm,
+			xc3((smpfunc_t) BTFIXUP_CALL(local_flush_tlb_range), (unsigned long) mm,
 			    start, end);
 	}
 }
@@ -506,7 +196,7 @@
 		if(mm->cpu_vm_mask == (1 << smp_processor_id()))
 			local_flush_cache_page(vma, page);
 		else
-			xc2((smpfunc_t) local_flush_cache_page,
+			xc2((smpfunc_t) BTFIXUP_CALL(local_flush_cache_page),
 			    (unsigned long) vma, page);
 	}
 }
@@ -519,7 +209,7 @@
 		if(mm->cpu_vm_mask == (1 << smp_processor_id()))
 			local_flush_tlb_page(vma, page);
 		else
-			xc2((smpfunc_t) local_flush_tlb_page, (unsigned long) vma, page);
+			xc2((smpfunc_t) BTFIXUP_CALL(local_flush_tlb_page), (unsigned long) vma, page);
 	}
 }
 
@@ -532,7 +222,7 @@
 	 * XXX This experiment failed, research further... -DaveM
 	 */
 #if 1
-	xc1((smpfunc_t) local_flush_page_to_ram, page);
+	xc1((smpfunc_t) BTFIXUP_CALL(local_flush_page_to_ram), page);
 #else
 	local_flush_page_to_ram(page);
 #endif
@@ -543,7 +233,7 @@
 	if(mm->cpu_vm_mask == (1 << smp_processor_id()))
 		local_flush_sig_insns(mm, insn_addr);
 	else
-		xc2((smpfunc_t) local_flush_sig_insns, (unsigned long) mm, insn_addr);
+		xc2((smpfunc_t) BTFIXUP_CALL(local_flush_sig_insns), (unsigned long) mm, insn_addr);
 }
 
 /* Reschedule call back. */
@@ -552,17 +242,6 @@
 	need_resched = 1;
 }
 
-/* Running cross calls. */
-void smp_cross_call_irq(void)
-{
-	int i = smp_processor_id();
-
-	ccall_info.processors_in[i] = 1;
-	ccall_info.func(ccall_info.arg1, ccall_info.arg2, ccall_info.arg3,
-			ccall_info.arg4, ccall_info.arg5);
-	ccall_info.processors_out[i] = 1;
-}
-
 /* Stopping processors. */
 void smp_stop_cpu_irq(void)
 {
@@ -571,83 +250,9 @@
 		barrier();
 }
 
-/* Protects counters touched during level14 ticker */
-spinlock_t ticker_lock = SPIN_LOCK_UNLOCKED;
-
-#ifdef CONFIG_PROFILE
-
-/* 32-bit Sparc specific profiling function. */
-static inline void sparc_do_profile(unsigned long pc)
-{
-	if(prof_buffer && current->pid) {
-		extern int _stext;
-
-		pc -= (unsigned long) &_stext;
-		pc >>= prof_shift;
-
-		spin_lock(&ticker_lock);
-		if(pc < prof_len)
-			prof_buffer[pc]++;
-		else
-			prof_buffer[prof_len - 1]++;
-		spin_unlock(&ticker_lock);
-	}
-}
-
-#endif
-
 unsigned int prof_multiplier[NR_CPUS];
 unsigned int prof_counter[NR_CPUS];
-
-extern void update_one_process(struct task_struct *p, unsigned long ticks,
-			       unsigned long user, unsigned long system);
-
-void smp_percpu_timer_interrupt(struct pt_regs *regs)
-{
-	int cpu = smp_processor_id();
-
-	clear_profile_irq(mid_xlate[cpu]);
-#ifdef CONFIG_PROFILE
-	if(!user_mode(regs))
-		sparc_do_profile(regs->pc);
-#endif
-	if(!--prof_counter[cpu]) {
-		int user = user_mode(regs);
-		if(current->pid) {
-			update_one_process(current, 1, user, !user);
-
-			if(--current->counter < 0) {
-				current->counter = 0;
-				need_resched = 1;
-			}
-
-			spin_lock(&ticker_lock);
-			if(user) {
-				if(current->priority < DEF_PRIORITY)
-					kstat.cpu_nice++;
-				else
-					kstat.cpu_user++;
-			} else {
-				kstat.cpu_system++;
-			}
-			spin_unlock(&ticker_lock);
-		}
-		prof_counter[cpu] = prof_multiplier[cpu];
-	}
-}
-
 extern unsigned int lvl14_resolution;
-
-static void smp_setup_percpu_timer(void)
-{
-	int cpu = smp_processor_id();
-
-	prof_counter[cpu] = prof_multiplier[cpu] = 1;
-	load_profile_irq(mid_xlate[cpu], lvl14_resolution);
-
-	if(cpu == boot_cpu_id)
-		enable_pil_irq(14);
-}
 
 int setup_profiling_timer(unsigned int multiplier)
 {
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/kernel/sparc-stub.c linux/arch/sparc/kernel/sparc-stub.c
--- v2.1.96/linux/arch/sparc/kernel/sparc-stub.c	Mon Jan 12 15:15:43 1998
+++ linux/arch/sparc/kernel/sparc-stub.c	Tue Apr 14 17:44:19 1998
@@ -1,4 +1,4 @@
-/* $Id: sparc-stub.c,v 1.22 1998/01/07 06:33:48 baccala Exp $
+/* $Id: sparc-stub.c,v 1.24 1998/02/08 07:58:44 ecd Exp $
  * sparc-stub.c:  KGDB support for the Linux kernel.
  *
  * Modifications to run under Linux
@@ -165,9 +165,10 @@
 	return entry;
 }
 
-static void flush_cache_all_nop(void)
-{
-}
+#if 0
+/* Have to sort this out. This cannot be done after initialization. */
+static void flush_cache_all_nop(void) {}
+#endif
 
 /* Place where we save old trap entries for restoration */
 struct tt_entry kgdb_savettable[256];
@@ -398,10 +399,12 @@
 {
 	struct hard_trap_info *ht;
 	unsigned long flags;
-	unsigned char c;
 
 	save_and_cli(flags);
-	flush_cache_all = flush_cache_all_nop;
+#if 0	
+/* Have to sort this out. This cannot be done after initialization. */
+	BTFIXUPSET_CALL(flush_cache_all, flush_cache_all_nop, BTFIXUPCALL_NOP);
+#endif
 
 	/* Initialize our copy of the Linux Sparc trap table */
 	eh_init();
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/kernel/sparc_ksyms.c linux/arch/sparc/kernel/sparc_ksyms.c
--- v2.1.96/linux/arch/sparc/kernel/sparc_ksyms.c	Tue Mar 17 22:18:14 1998
+++ linux/arch/sparc/kernel/sparc_ksyms.c	Tue Apr 14 17:44:19 1998
@@ -1,4 +1,4 @@
-/* $Id: sparc_ksyms.c,v 1.61 1997/11/19 07:57:44 jj Exp $
+/* $Id: sparc_ksyms.c,v 1.64 1998/03/19 15:36:43 jj Exp $
  * arch/sparc/kernel/ksyms.c: Sparc specific ksyms support.
  *
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
@@ -15,7 +15,6 @@
 #include <linux/string.h>
 #include <linux/interrupt.h>
 #include <linux/in6.h>
-#include <linux/pci.h>
 
 #include <asm/oplib.h>
 #include <asm/delay.h>
@@ -138,7 +137,6 @@
 EXPORT_SYMBOL(stack_top);
 
 /* Atomic operations. */
-EXPORT_SYMBOL_PRIVATE(_xchg32);
 EXPORT_SYMBOL_PRIVATE(_atomic_add);
 EXPORT_SYMBOL_PRIVATE(_atomic_sub);
 
@@ -168,13 +166,23 @@
 EXPORT_SYMBOL(sparc_alloc_io);
 EXPORT_SYMBOL(sparc_free_io);
 EXPORT_SYMBOL(io_remap_page_range);
-EXPORT_SYMBOL(mmu_v2p);
-EXPORT_SYMBOL(mmu_unlockarea);
-EXPORT_SYMBOL(mmu_lockarea);
-EXPORT_SYMBOL(mmu_get_scsi_sgl);
-EXPORT_SYMBOL(mmu_get_scsi_one);
-EXPORT_SYMBOL(mmu_release_scsi_sgl);
-EXPORT_SYMBOL(mmu_release_scsi_one);
+
+/* Btfixup stuff cannot have versions, it would be complicated too much */
+#ifndef __SMP__
+EXPORT_SYMBOL_NOVERS(BTFIXUP_CALL(___xchg32));
+#else
+EXPORT_SYMBOL_NOVERS(BTFIXUP_CALL(__smp_processor_id));
+#endif
+EXPORT_SYMBOL_NOVERS(BTFIXUP_CALL(enable_irq));
+EXPORT_SYMBOL_NOVERS(BTFIXUP_CALL(disable_irq));
+EXPORT_SYMBOL_NOVERS(BTFIXUP_CALL(mmu_v2p));
+EXPORT_SYMBOL_NOVERS(BTFIXUP_CALL(mmu_unlockarea));
+EXPORT_SYMBOL_NOVERS(BTFIXUP_CALL(mmu_lockarea));
+EXPORT_SYMBOL_NOVERS(BTFIXUP_CALL(mmu_get_scsi_sgl));
+EXPORT_SYMBOL_NOVERS(BTFIXUP_CALL(mmu_get_scsi_one));
+EXPORT_SYMBOL_NOVERS(BTFIXUP_CALL(mmu_release_scsi_sgl));
+EXPORT_SYMBOL_NOVERS(BTFIXUP_CALL(mmu_release_scsi_one));
+
 EXPORT_SYMBOL(_sparc_dvma_malloc);
 EXPORT_SYMBOL(sun4c_unmapioaddr);
 EXPORT_SYMBOL(srmmu_unmapioaddr);
@@ -272,7 +280,3 @@
 EXPORT_SYMBOL_DOT(umul);
 EXPORT_SYMBOL_DOT(div);
 EXPORT_SYMBOL_DOT(udiv);
-
-#if CONFIG_PCI
-EXPORT_SYMBOL(pci_devices);
-#endif
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/kernel/sun4c_irq.c linux/arch/sparc/kernel/sun4c_irq.c
--- v2.1.96/linux/arch/sparc/kernel/sun4c_irq.c	Wed Apr 23 19:01:16 1997
+++ linux/arch/sparc/kernel/sun4c_irq.c	Tue Apr 14 17:44:19 1998
@@ -9,6 +9,7 @@
  *  Copyright (C) 1996 Dave Redman (djhr@tadpole.co.uk)
  */
 
+#include <linux/config.h>
 #include <linux/ptrace.h>
 #include <linux/errno.h>
 #include <linux/linkage.h>
@@ -30,6 +31,9 @@
 #include <asm/traps.h>
 #include <asm/irq.h>
 #include <asm/io.h>
+#include <asm/sun4paddr.h>
+#include <asm/idprom.h>
+#include <asm/machines.h>
 
 /* Pointer to the interrupt enable byte
  *
@@ -128,7 +132,7 @@
 	/* Map the Timer chip, this is implemented in hardware inside
 	 * the cache chip on the sun4c.
 	 */
-	sun4c_timers = sparc_alloc_io (SUN4C_TIMER_PHYSADDR, 0,
+	sun4c_timers = sparc_alloc_io (SUN_TIMER_PHYSADDR, 0,
 				       sizeof(struct sun4c_timer_info),
 				       "timer", 0x0, 0x0);
     
@@ -160,30 +164,41 @@
 {
 	struct linux_prom_registers int_regs[2];
 	int ie_node;
+
+	if (ARCH_SUN4) {
+		interrupt_enable =
+			(char *) sparc_alloc_io(SUN4_IE_PHYSADDR, 0,
+					   	PAGE_SIZE,
+					   	"sun4c_interrupts",
+					   	0x0, 0x0);
+	} else {
     
-	ie_node = prom_searchsiblings (prom_getchild(prom_root_node),
-				       "interrupt-enable");
-	if(ie_node == 0)
-		panic("Cannot find /interrupt-enable node");
-
-	/* Depending on the "address" property is bad news... */
-	prom_getproperty(ie_node, "reg", (char *) int_regs, sizeof(int_regs));
-	interrupt_enable = (char *) sparc_alloc_io(int_regs[0].phys_addr, 0,
-						   int_regs[0].reg_size,
-						   "sun4c_interrupts",
-						   int_regs[0].which_io, 0x0);
-	enable_irq = sun4c_enable_irq;
-	disable_irq = sun4c_disable_irq;
-	enable_pil_irq = sun4c_enable_irq;
-	disable_pil_irq = sun4c_disable_irq;
-	clear_clock_irq = sun4c_clear_clock_irq;
-	clear_profile_irq = sun4c_clear_profile_irq;
-	load_profile_irq = sun4c_load_profile_irq;
+		ie_node = prom_searchsiblings (prom_getchild(prom_root_node),
+				       	"interrupt-enable");
+		if(ie_node == 0)
+			panic("Cannot find /interrupt-enable node");
+
+		/* Depending on the "address" property is bad news... */
+		prom_getproperty(ie_node, "reg", (char *) int_regs, sizeof(int_regs));
+		interrupt_enable =
+			(char *) sparc_alloc_io(int_regs[0].phys_addr, 0,
+					   	int_regs[0].reg_size,
+					   	"sun4c_interrupts",
+					   	int_regs[0].which_io, 0x0);
+	}
+
+	BTFIXUPSET_CALL(enable_irq, sun4c_enable_irq, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(disable_irq, sun4c_disable_irq, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(enable_pil_irq, sun4c_enable_irq, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(disable_pil_irq, sun4c_disable_irq, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(clear_clock_irq, sun4c_clear_clock_irq, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(clear_profile_irq, sun4c_clear_profile_irq, BTFIXUPCALL_NOP);
+	BTFIXUPSET_CALL(load_profile_irq, sun4c_load_profile_irq, BTFIXUPCALL_NOP);
 	init_timers = sun4c_init_timers;
 #ifdef __SMP__
-	set_cpu_int = (void (*) (int, int))sun4c_nop;
-	clear_cpu_int = (void (*) (int, int))sun4c_nop;
-	set_irq_udt = (void (*) (int))sun4c_nop;
+	BTFIXUPSET_CALL(set_cpu_int, sun4c_nop, BTFIXUPCALL_NOP);
+	BTFIXUPSET_CALL(clear_cpu_int, sun4c_nop, BTFIXUPCALL_NOP);
+	BTFIXUPSET_CALL(set_irq_udt, sun4c_nop, BTFIXUPCALL_NOP);
 #endif
 	*interrupt_enable = (SUN4C_INT_ENABLE);
 	/* Cannot enable interrupts until OBP ticker is disabled. */
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/kernel/sun4d_irq.c linux/arch/sparc/kernel/sun4d_irq.c
--- v2.1.96/linux/arch/sparc/kernel/sun4d_irq.c	Mon Jan 12 15:15:43 1998
+++ linux/arch/sparc/kernel/sun4d_irq.c	Tue Apr 14 17:44:19 1998
@@ -1,8 +1,8 @@
-/*  $Id: sun4d_irq.c,v 1.3 1997/12/22 16:09:15 jj Exp $
- *  arch/sparc/kernel/sun4d_irq.c: 
+/*  $Id: sun4d_irq.c,v 1.12 1998/03/19 15:36:36 jj Exp $
+ *  arch/sparc/kernel/sun4d_irq.c:
  *			SS1000/SC2000 interrupt handling.
  *
- *  Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ *  Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
  *  Heavily based on arch/sparc/kernel/irq.c.
  */
 
@@ -36,32 +36,47 @@
 #include <asm/sbus.h>
 #include <asm/sbi.h>
 
+/* If you trust current SCSI layer to handle different SCSI IRQs, enable this. I don't trust it... -jj */
+/* #define DISTRIBUTE_IRQS */
+
 struct sun4d_timer_regs *sun4d_timers;
 #define TIMER_IRQ	10
 
 #define MAX_STATIC_ALLOC	4
 extern struct irqaction static_irqaction[MAX_STATIC_ALLOC];
 extern int static_irq_count;
+unsigned char cpu_leds[32];
+#ifdef __SMP__
+unsigned char sbus_tid[32];
+#endif
 
 extern struct irqaction *irq_action[];
 
 struct sbus_action {
 	struct irqaction *action;
-	unsigned char	lock;
-	unsigned char	active;
-	unsigned char	disabled;
+	/* For SMP this needs to be extended */
 } *sbus_actions;
 
 static int pil_to_sbus[] = {
 	0, 0, 1, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 0,
 };
 
+static int sbus_to_pil[] = {
+	0, 2, 3, 5, 7, 9, 11, 13,
+};
+
 static int nsbi;
+#ifdef __SMP__
+spinlock_t sun4d_imsk_lock = SPIN_LOCK_UNLOCKED;
+#endif
 
 int sun4d_get_irq_list(char *buf)
 {
 	int i, j = 0, k = 0, len = 0, sbusl;
 	struct irqaction * action;
+#ifdef __SMP__
+	int x;
+#endif
 
 	for (i = 0 ; i < NR_IRQS ; i++) {
 		sbusl = pil_to_sbus[i];
@@ -77,8 +92,15 @@
 			}
 			continue;
 		}
-found_it:	len += sprintf(buf+len, "%2d: %8d %c %s",
-			i, kstat.interrupts[i],
+found_it:	len += sprintf(buf+len, "%3d: ", i);
+#ifndef __SMP__
+		len += sprintf(buf+len, "%10u ", kstat_irqs(i));
+#else
+		for (x = 0; x < smp_num_cpus; x++)
+			len += sprintf(buf+len, "%10u ",
+				       kstat.irqs[cpu_logical_map(x)][i]);
+#endif
+		len += sprintf(buf+len, "%c %s",
 			(action->flags & SA_INTERRUPT) ? '+' : ' ',
 			action->name);
 		action = action->next;
@@ -172,7 +194,7 @@
 	cc_set_iclr(1 << irq);
 	
 	irq_enter(cpu, irq, regs);
-	kstat.interrupts[irq]++;
+	kstat.irqs[cpu][irq]++;
 	if (!sbusl) {
 		action = *(irq + irq_action);
 		if (!action)
@@ -183,7 +205,6 @@
 		} while (action);
 	} else {
 		int bus_mask = bw_get_intr_mask(sbusl) & 0x3ffff;
-		int lock;
 		int sbino;
 		struct sbus_action *actionp;
 		unsigned mask, slot;
@@ -202,19 +223,13 @@
 					if (mask & slot) {
 						mask &= ~slot;
 						action = actionp->action;
-						__asm__ __volatile__ ("ldstub	[%1 + 4], %0"
-						: "=r" (lock) : "r" (actionp));
 						
-						if (!lock) {
-							if (!action)
-								unexpected_irq(irq, 0, regs);
-							do {
-								action->handler(irq, action->dev_id, regs);
-								action = action->next;
-							} while (action);
-							actionp->lock = 0;
-						} else
-							actionp->active = 1;
+						if (!action)
+							unexpected_irq(irq, 0, regs);
+						do {
+							action->handler(irq, action->dev_id, regs);
+							action = action->next;
+						} while (action);
 						release_sbi(SBI2DEVID(sbino), slot);
 					}
 			}
@@ -305,79 +320,49 @@
 	else
 		*actionp = action;
 		
-	if (ret) irq = *ret;
-
-	if (irq > NR_IRQS) {
-		struct sbus_action *s = sbus_actions + irq - (1 << 5);
-	
-		if (s->disabled) {
-			s->disabled = 0;
-			s->active = 0;
-			s->lock = 0;
-		}
-	}
-	
+	enable_irq(irq);
 	restore_flags(flags);
 	return 0;
 }
 
 static void sun4d_disable_irq(unsigned int irq)
 {
-	struct sbus_action *s;
-	
-	if (irq < NR_IRQS) {
-		/* FIXME */
-		printk ("Unable to disable IRQ %d\n", irq);
-		return;
-	}
-	s = sbus_actions + irq - (1 << 5);
+#ifdef __SMP__
+	int tid = sbus_tid[(irq >> 5) - 1];
+	unsigned long flags;
+#endif	
 	
-	if (s->disabled) return;
-	s->disabled = 1;
-	__asm__ __volatile__ ("
-1:		ldstub	[%0 + 4], %%g1
-		orcc	%%g1, 0, %%g0
-		bne	1b"
-		: : "r" (s) : "g1", "cc");
+	if (irq < NR_IRQS) return;
+#ifdef __SMP__
+	spin_lock_irqsave(&sun4d_imsk_lock, flags);
+	cc_set_imsk_other(tid, cc_get_imsk_other(tid) | (1 << sbus_to_pil[(irq >> 2) & 7]));
+	spin_unlock_irqrestore(&sun4d_imsk_lock, flags);
+#else		
+	cc_set_imsk(cc_get_imsk() | (1 << sbus_to_pil[(irq >> 2) & 7]));
+#endif
 }
 
 static void sun4d_enable_irq(unsigned int irq)
 {
-	struct sbus_action *s;
-	struct irqaction *action;
-	
-	if (irq < NR_IRQS)
-		/* FIXME */
-		return;
-	s = sbus_actions + irq - (1 << 5);
+#ifdef __SMP__
+	int tid = sbus_tid[(irq >> 5) - 1];
+	unsigned long flags;
+#endif	
 	
-	if (!s->disabled) return;
-	action = s->action;
-	s->disabled = 0;
-	while (s->active) {
-		s->active = 0;
-		while (action) {
-			/* FIXME: Hope no sbus intr handler uses regs */
-			action->handler(irq, action->dev_id, NULL);
-			action = action->next;
-		}
-	}
-	s->lock = 0;
+	if (irq < NR_IRQS) return;
+#ifdef __SMP__
+	spin_lock_irqsave(&sun4d_imsk_lock, flags);
+	cc_set_imsk_other(tid, cc_get_imsk_other(tid) & ~(1 << sbus_to_pil[(irq >> 2) & 7]));
+	spin_unlock_irqrestore(&sun4d_imsk_lock, flags);
+#else		
+	cc_set_imsk(cc_get_imsk() & ~(1 << sbus_to_pil[(irq >> 2) & 7]));
+#endif
 }
 
 #ifdef __SMP__
-
-/* +-------+-------------+-----------+------------------------------------+
- * | bcast |  devid      |   sid     |              levels mask           |
- * +-------+-------------+-----------+------------------------------------+
- *  31      30         23 22       15 14                                 0
- */
-#define IGEN_MESSAGE(bcast, devid, sid, levels) \
-	(((bcast) << 31) | ((devid) << 23) | ((sid) << 15) | (levels))
-
-static void sun4d_send_ipi(int cpu, int level)
+static void sun4d_set_cpu_int(int cpu, int level)
 {
-	cc_set_igen(IGEN_MESSAGE(0, cpu << 3, 6 + ((level >> 1) & 7), 1 << (level - 1)));
+	sun4d_send_ipi(cpu, level);
 }
 
 static void sun4d_clear_ipi(int cpu, int level)
@@ -387,6 +372,55 @@
 static void sun4d_set_udt(int cpu)
 {
 }
+
+/* Setup IRQ distribution scheme. */
+__initfunc(void sun4d_distribute_irqs(void))
+{
+#ifdef DISTRIBUTE_IRQS
+	struct linux_sbus *sbus;
+	unsigned long sbus_serving_map;
+
+	sbus_serving_map = cpu_present_map;
+	for_each_sbus(sbus) {
+		if ((sbus->board * 2) == boot_cpu_id && (cpu_present_map & (1 << (sbus->board * 2 + 1))))
+			sbus_tid[sbus->board] = (sbus->board * 2 + 1);
+		else if (cpu_present_map & (1 << (sbus->board * 2)))
+			sbus_tid[sbus->board] = (sbus->board * 2);
+		else if (cpu_present_map & (1 << (sbus->board * 2 + 1)))
+			sbus_tid[sbus->board] = (sbus->board * 2 + 1);
+		else
+			sbus_tid[sbus->board] = 0xff;
+		if (sbus_tid[sbus->board] != 0xff)
+			sbus_serving_map &= ~(1 << sbus_tid[sbus->board]);
+	}
+	for_each_sbus(sbus)
+		if (sbus_tid[sbus->board] == 0xff) {
+			int i = 31;
+				
+			if (!sbus_serving_map)
+				sbus_serving_map = cpu_present_map;
+			while (!(sbus_serving_map & (1 << i)))
+				i--;
+			sbus_tid[sbus->board] = i;
+			sbus_serving_map &= ~(1 << i);
+		}
+	for_each_sbus(sbus) {
+		printk("sbus%d IRQs directed to CPU%d\n", sbus->board, sbus_tid[sbus->board]);
+		set_sbi_tid(sbus->devid, sbus_tid[sbus->board] << 3);
+	}
+#else
+	struct linux_sbus *sbus;
+	int cpuid = cpu_logical_map(1);
+
+	if (cpuid == -1)
+		cpuid = cpu_logical_map(0);
+	for_each_sbus(sbus) {
+		sbus_tid[sbus->board] = cpuid;
+		set_sbi_tid(sbus->devid, cpuid << 3);
+	}
+	printk("All sbus IRQs directed to CPU%d\n", cpuid);
+#endif
+}
 #endif
  
 static void sun4d_clear_clock_irq(void)
@@ -408,7 +442,7 @@
 __initfunc(static void sun4d_init_timers(void (*counter_fn)(int, void *, struct pt_regs *)))
 {
 	int irq;
-	extern struct prom_cpuinfo linux_cpus[NCPUS];
+	extern struct prom_cpuinfo linux_cpus[NR_CPUS];
 	int cpu;
 
 	/* Map the User Timer registers. */
@@ -431,15 +465,39 @@
 	/* Enable user timer free run for CPU 0 in BW */
 	/* bw_set_ctrl(0, bw_get_ctrl(0) | BW_CTRL_USER_TIMER); */
     
-	for(cpu = 0; cpu < NCPUS; cpu++)
-		sun4d_load_profile_irq(linux_cpus[cpu].mid, 0);
+	for(cpu = 0; cpu < linux_num_cpus; cpu++)
+		sun4d_load_profile_irq((linux_cpus[cpu].mid >> 3), 0);
+		
+#ifdef __SMP__
+	{
+		unsigned long flags;
+		extern unsigned long lvl14_save[4];
+		struct tt_entry *trap_table = &sparc_ttable[SP_TRAP_IRQ1 + (14 - 1)];
+		extern unsigned int real_irq_entry[], smp4d_ticker[];
+		extern unsigned int patchme_maybe_smp_msg[];
+
+		/* Adjust so that we jump directly to smp4d_ticker */
+		lvl14_save[2] += smp4d_ticker - real_irq_entry;
+
+		/* For SMP we use the level 14 ticker, however the bootup code
+		 * has copied the firmwares level 14 vector into boot cpu's
+		 * trap table, we must fix this now or we get squashed.
+		 */
+		__save_and_cli(flags);
+		patchme_maybe_smp_msg[0] = 0x01000000; /* NOP out the branch */
+		trap_table->inst_one = lvl14_save[0];
+		trap_table->inst_two = lvl14_save[1];
+		trap_table->inst_three = lvl14_save[2];
+		trap_table->inst_four = lvl14_save[3];
+		local_flush_cache_all();
+		__restore_flags(flags);
+	}
+#endif
 }
 
 __initfunc(unsigned long sun4d_init_sbi_irq(unsigned long memory_start))
 {
 	struct linux_sbus *sbus;
-	struct sbus_action *s;
-	int i;
 	unsigned mask;
 
 	nsbi = 0;
@@ -449,11 +507,13 @@
 	sbus_actions = (struct sbus_action *)memory_start;
 	memory_start += (nsbi * 8 * 4 * sizeof(struct sbus_action));
 	memset (sbus_actions, 0, (nsbi * 8 * 4 * sizeof(struct sbus_action)));
-	for (i = 0, s = sbus_actions; i < nsbi * 8 * 4; i++, s++) {
-		s->lock = 0xff;
-		s->disabled = 1;
-	}
 	for_each_sbus(sbus) {
+#ifdef __SMP__	
+		extern unsigned char boot_cpu_id;
+		
+		set_sbi_tid(sbus->devid, boot_cpu_id << 3);
+		sbus_tid[sbus->board] = boot_cpu_id;
+#endif
 		/* Get rid of pending irqs from PROM */
 		mask = acquire_sbi(sbus->devid, 0xffffffff);
 		if (mask) {
@@ -468,16 +528,16 @@
 {
 	__cli();
 
-	enable_irq = sun4d_enable_irq;
-	disable_irq = sun4d_disable_irq;
-	clear_clock_irq = sun4d_clear_clock_irq;
-	clear_profile_irq = sun4d_clear_profile_irq;
-	load_profile_irq = sun4d_load_profile_irq;
+	BTFIXUPSET_CALL(enable_irq, sun4d_enable_irq, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(disable_irq, sun4d_disable_irq, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(clear_clock_irq, sun4d_clear_clock_irq, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(clear_profile_irq, sun4d_clear_profile_irq, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(load_profile_irq, sun4d_load_profile_irq, BTFIXUPCALL_NORM);
 	init_timers = sun4d_init_timers;
 #ifdef __SMP__
-	set_cpu_int = (void (*) (int, int))sun4d_send_ipi;
-	clear_cpu_int = (void (*) (int, int))sun4d_clear_ipi;
-	set_irq_udt = (void (*) (int))sun4d_set_udt;
+	BTFIXUPSET_CALL(set_cpu_int, sun4d_set_cpu_int, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(clear_cpu_int, sun4d_clear_ipi, BTFIXUPCALL_NOP);
+	BTFIXUPSET_CALL(set_irq_udt, sun4d_set_udt, BTFIXUPCALL_NOP);
 #endif
 	/* Cannot enable interrupts until OBP ticker is disabled. */
 }
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/kernel/sun4d_smp.c linux/arch/sparc/kernel/sun4d_smp.c
--- v2.1.96/linux/arch/sparc/kernel/sun4d_smp.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc/kernel/sun4d_smp.c	Tue Apr 14 17:44:19 1998
@@ -0,0 +1,576 @@
+/* sun4d_smp.c: Sparc SS1000/SC2000 SMP support.
+ *
+ * Copyright (C) 1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ *
+ * Based on sun4m's smp.c, which is:
+ * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <linux/config.h> /* for CONFIG_PROFILE */
+#include <asm/head.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/tasks.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/interrupt.h>
+#include <linux/kernel_stat.h>
+#include <linux/init.h>
+
+#include <asm/ptrace.h>
+#include <asm/atomic.h>
+
+#include <asm/delay.h>
+#include <asm/irq.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/oplib.h>
+#include <asm/atops.h>
+#include <asm/spinlock.h>
+#include <asm/hardirq.h>
+#include <asm/softirq.h>
+#include <asm/sbus.h>
+#include <asm/sbi.h>
+
+#define __KERNEL_SYSCALLS__
+#include <linux/unistd.h>
+
+#define IRQ_CROSS_CALL		15
+
+extern ctxd_t *srmmu_ctx_table_phys;
+extern int linux_num_cpus;
+
+extern void calibrate_delay(void);
+
+extern struct task_struct *current_set[NR_CPUS];
+extern volatile int smp_processors_ready;
+extern unsigned long cpu_present_map;
+extern int smp_num_cpus;
+static int smp_highest_cpu = 0;
+extern int smp_threads_ready;
+extern unsigned char mid_xlate[NR_CPUS];
+extern volatile unsigned long cpu_callin_map[NR_CPUS];
+extern unsigned long smp_proc_in_lock[NR_CPUS];
+extern struct cpuinfo_sparc cpu_data[NR_CPUS];
+extern unsigned long cpu_offset[NR_CPUS];
+extern unsigned char boot_cpu_id;
+extern int smp_activated;
+extern volatile int cpu_number_map[NR_CPUS];
+extern volatile int __cpu_logical_map[NR_CPUS];
+extern struct klock_info klock_info;
+extern volatile unsigned long ipi_count;
+extern volatile int smp_process_available;
+extern volatile int smp_commenced;
+extern int __smp4d_processor_id(void);
+
+/* #define SMP_DEBUG */
+
+#ifdef SMP_DEBUG
+#define SMP_PRINTK(x)	printk x
+#else
+#define SMP_PRINTK(x)
+#endif
+
+int smp4d_bogo_info(char *buf)
+{
+	int len = 0, i;
+	
+	for (i = 0; i < NR_CPUS; i++)
+		if (cpu_present_map & (1 << i))
+			len += sprintf(buf + len, "Cpu%dBogo\t: %lu.%02lu\n", 
+					i,
+					cpu_data[i].udelay_val/500000,
+					(cpu_data[i].udelay_val/5000)%100);
+	return len;
+}
+
+int smp4d_info(char *buf)
+{
+	int len = 0, i;
+	
+	for (i = 0; i < NR_CPUS; i++)
+		if (cpu_present_map & (1 << i))
+			len += sprintf(buf + len, "CPU%d\t\t: %s\n", 
+					i,
+					(klock_info.akp == i) ? "akp" : "online");
+	return len;
+}
+
+static inline unsigned long swap(volatile unsigned long *ptr, unsigned long val)
+{
+	__asm__ __volatile__("swap [%1], %0\n\t" :
+			     "=&r" (val), "=&r" (ptr) :
+			     "0" (val), "1" (ptr));
+	return val;
+}
+
+static void smp_setup_percpu_timer(void);
+extern void cpu_probe(void);
+extern void sun4d_distribute_irqs(void);
+
+__initfunc(void smp4d_callin(void))
+{
+	int cpuid = hard_smp4d_processor_id();
+	extern spinlock_t sun4d_imsk_lock;
+	unsigned long flags;
+	
+	/* Show we are alive */
+	cpu_leds[cpuid] = 0x6;
+	show_leds(cpuid);
+
+	/* Enable level15 interrupt, disable level14 interrupt for now */
+	cc_set_imsk((cc_get_imsk() & ~0x8000) | 0x4000);
+
+	local_flush_cache_all();
+	local_flush_tlb_all();
+
+	/* Get our local ticker going. */
+	smp_setup_percpu_timer();
+
+	calibrate_delay();
+	smp_store_cpu_info(cpuid);
+	local_flush_cache_all();
+	local_flush_tlb_all();
+
+	/* Allow master to continue. */
+	swap((unsigned long *)&cpu_callin_map[cpuid], 1);
+	local_flush_cache_all();
+	local_flush_tlb_all();
+	
+	cpu_probe();
+
+	while((unsigned long)current_set[cpuid] < PAGE_OFFSET)
+		barrier();
+		
+	while(current_set[cpuid]->processor != cpuid)
+		barrier();
+		
+	/* Fix idle thread fields. */
+	__asm__ __volatile__("ld [%0], %%g6\n\t"
+			     "sta %%g6, [%%g0] %1\n\t"
+			     : : "r" (&current_set[cpuid]), "i" (ASI_M_VIKING_TMP2)
+			     : "memory" /* paranoid */);
+
+	cpu_leds[cpuid] = 0x9;
+	show_leds(cpuid);
+	
+	current->mm->mmap->vm_page_prot = PAGE_SHARED;
+	current->mm->mmap->vm_start = PAGE_OFFSET;
+	current->mm->mmap->vm_end = init_task.mm->mmap->vm_end;
+	
+	local_flush_cache_all();
+	local_flush_tlb_all();
+	
+	__sti();	/* We don't allow PIL 14 yet */
+	
+	while(!smp_commenced)
+		barrier();
+
+	spin_lock_irqsave(&sun4d_imsk_lock, flags);
+	cc_set_imsk(cc_get_imsk() & ~0x4000); /* Allow PIL 14 as well */
+	spin_unlock_irqrestore(&sun4d_imsk_lock, flags);
+}
+
+extern int cpu_idle(void *unused);
+extern void init_IRQ(void);
+extern void cpu_panic(void);
+extern int start_secondary(void *unused);
+
+/*
+ *	Cycle through the processors asking the PROM to start each one.
+ */
+ 
+extern struct prom_cpuinfo linux_cpus[NR_CPUS];
+extern struct linux_prom_registers smp_penguin_ctable;
+extern unsigned long trapbase_cpu1[];
+extern unsigned long trapbase_cpu2[];
+extern unsigned long trapbase_cpu3[];
+
+__initfunc(void smp4d_boot_cpus(void))
+{
+	int cpucount = 0;
+	int i = 0;
+
+	printk("Entering SMP Mode...\n");
+	
+	smp_penguin_ctable.which_io = 0;
+	smp_penguin_ctable.phys_addr = (unsigned int) srmmu_ctx_table_phys;
+	smp_penguin_ctable.reg_size = 0;
+
+	for (i = 0; i < NR_CPUS; i++)
+		cpu_offset[i] = (char *)&cpu_data[i] - (char *)&cpu_data;
+		
+	if (boot_cpu_id)
+		current_set[0] = NULL;
+
+	__sti();
+	cpu_present_map = 0;
+	for(i=0; i < linux_num_cpus; i++)
+		cpu_present_map |= (1<<linux_cpus[i].mid);
+	SMP_PRINTK(("cpu_present_map %08lx\n", cpu_present_map));
+	for(i=0; i < NR_CPUS; i++)
+		cpu_number_map[i] = -1;
+	for(i=0; i < NR_CPUS; i++)
+		__cpu_logical_map[i] = -1;
+	for(i=0; i < NR_CPUS; i++)
+		mid_xlate[i] = i;
+	cpu_number_map[boot_cpu_id] = 0;
+	__cpu_logical_map[0] = boot_cpu_id;
+	klock_info.akp = boot_cpu_id;
+	current->processor = boot_cpu_id;
+	smp_store_cpu_info(boot_cpu_id);
+	smp_setup_percpu_timer();
+	local_flush_cache_all();
+	if(linux_num_cpus == 1)
+		return;  /* Not an MP box. */
+	SMP_PRINTK(("Iterating over CPUs\n"));
+	for(i = 0; i < NR_CPUS; i++) {
+		if(i == boot_cpu_id)
+			continue;
+
+		if(cpu_present_map & (1 << i)) {
+			extern unsigned long sun4d_cpu_startup;
+			unsigned long *entry = &sun4d_cpu_startup;
+			struct task_struct *p;
+			int timeout;
+			int no;
+
+			/* Cook up an idler for this guy. */
+			kernel_thread(start_secondary, NULL, CLONE_PID);
+
+			p = task[++cpucount];
+
+			p->processor = i;
+			current_set[i] = p;
+			
+			for (no = 0; no < linux_num_cpus; no++)
+				if (linux_cpus[no].mid == i)
+					break;
+			
+			/* whirrr, whirrr, whirrrrrrrrr... */
+			SMP_PRINTK(("Starting CPU %d at %p task %d node %08x\n", i, entry, cpucount, linux_cpus[no].prom_node));
+			local_flush_cache_all();
+			prom_startcpu(linux_cpus[no].prom_node,
+				      &smp_penguin_ctable, 0, (char *)entry);
+				      
+			SMP_PRINTK(("prom_startcpu returned :)\n"));
+
+			/* wheee... it's going... */
+			for(timeout = 0; timeout < 5000000; timeout++) {
+				if(cpu_callin_map[i])
+					break;
+				udelay(100);
+			}
+			
+			if(cpu_callin_map[i]) {
+				/* Another "Red Snapper". */
+				cpu_number_map[i] = cpucount;
+				__cpu_logical_map[cpucount] = i;
+			} else {
+				cpucount--;
+				printk("Processor %d is stuck.\n", i);
+			}
+		}
+		if(!(cpu_callin_map[i])) {
+			cpu_present_map &= ~(1 << i);
+			cpu_number_map[i] = -1;
+		}
+	}
+	local_flush_cache_all();
+	if(cpucount == 0) {
+		printk("Error: only one Processor found.\n");
+		cpu_present_map = (1 << hard_smp4d_processor_id());
+	} else {
+		unsigned long bogosum = 0;
+		
+		for(i = 0; i < NR_CPUS; i++) {
+			if(cpu_present_map & (1 << i)) {
+				bogosum += cpu_data[i].udelay_val;
+				smp_highest_cpu = i;
+			}
+		}
+		SMP_PRINTK(("Total of %d Processors activated (%lu.%02lu BogoMIPS).\n", cpucount + 1, (bogosum + 2500)/500000, ((bogosum + 2500)/5000)%100));
+		printk("Total of %d Processors activated (%lu.%02lu BogoMIPS).\n",
+		       cpucount + 1,
+		       (bogosum + 2500)/500000,
+		       ((bogosum + 2500)/5000)%100);
+		smp_activated = 1;
+		smp_num_cpus = cpucount + 1;
+	}
+
+	/* Free unneeded trap tables */
+	
+	mem_map[MAP_NR((unsigned long)trapbase_cpu1)].flags &= ~(1 << PG_reserved);
+	free_page((unsigned long)trapbase_cpu1);
+	mem_map[MAP_NR((unsigned long)trapbase_cpu2)].flags &= ~(1 << PG_reserved);
+	free_page((unsigned long)trapbase_cpu2);
+	mem_map[MAP_NR((unsigned long)trapbase_cpu3)].flags &= ~(1 << PG_reserved);
+	free_page((unsigned long)trapbase_cpu3);
+
+	/* Ok, they are spinning and ready to go. */
+	smp_processors_ready = 1;
+	sun4d_distribute_irqs();
+}
+
+static struct smp_funcall {
+	smpfunc_t func;
+	unsigned long arg1;
+	unsigned long arg2;
+	unsigned long arg3;
+	unsigned long arg4;
+	unsigned long arg5;
+	unsigned char processors_in[NR_CPUS];  /* Set when ipi entered. */
+	unsigned char processors_out[NR_CPUS]; /* Set when ipi exited. */
+} ccall_info __attribute__((aligned(8)));
+
+static spinlock_t cross_call_lock = SPIN_LOCK_UNLOCKED;
+
+/* Cross calls must be serialized, at least currently. */
+void smp4d_cross_call(smpfunc_t func, unsigned long arg1, unsigned long arg2,
+		    unsigned long arg3, unsigned long arg4, unsigned long arg5)
+{
+	if(smp_processors_ready) {
+		register int high = smp_highest_cpu;
+		unsigned long flags;
+
+		spin_lock_irqsave(&cross_call_lock, flags);
+
+		{
+			/* If you make changes here, make sure gcc generates proper code... */
+			smpfunc_t f asm("i0") = func;
+			unsigned long a1 asm("i1") = arg1;
+			unsigned long a2 asm("i2") = arg2;
+			unsigned long a3 asm("i3") = arg3;
+			unsigned long a4 asm("i4") = arg4;
+			unsigned long a5 asm("i5") = arg5;
+					
+			__asm__ __volatile__("
+				std %0, [%6]
+				std %2, [%6 + 8]
+				std %4, [%6 + 16]" : : 
+				"r"(f), "r"(a1), "r"(a2), "r"(a3), "r"(a4), "r"(a5),
+				"r" (&ccall_info.func));
+		}
+
+		/* Init receive/complete mapping, plus fire the IPI's off. */
+		{
+			register unsigned long mask;
+			register int i;
+
+			mask = (cpu_present_map & ~(1 << hard_smp4d_processor_id()));
+			for(i = 0; i <= high; i++) {
+				if(mask & (1 << i)) {
+					ccall_info.processors_in[i] = 0;
+					ccall_info.processors_out[i] = 0;
+					sun4d_send_ipi(i, IRQ_CROSS_CALL);
+				}
+			}
+		}
+
+		/* First, run local copy. */
+		func(arg1, arg2, arg3, arg4, arg5);
+
+		{
+			register int i;
+
+			i = 0;
+			do {
+				while(!ccall_info.processors_in[i])
+					barrier();
+			} while(++i <= high);
+
+			i = 0;
+			do {
+				while(!ccall_info.processors_out[i])
+					barrier();
+			} while(++i <= high);
+		}
+
+		spin_unlock_irqrestore(&cross_call_lock, flags);
+	} else
+		func(arg1, arg2, arg3, arg4, arg5); /* Just need to run local copy. */
+}
+
+/* Running cross calls. */
+void smp4d_cross_call_irq(void)
+{
+	int i = hard_smp4d_processor_id();
+
+	ccall_info.processors_in[i] = 1;
+	ccall_info.func(ccall_info.arg1, ccall_info.arg2, ccall_info.arg3,
+			ccall_info.arg4, ccall_info.arg5);
+	ccall_info.processors_out[i] = 1;
+}
+
+static int smp4d_stop_cpu_sender;
+
+static void smp4d_stop_cpu(void)
+{
+	int me = hard_smp4d_processor_id();
+	
+	if (me != smp4d_stop_cpu_sender)
+		while(1) barrier();
+}
+
+/* Cross calls, in order to work efficiently and atomically do all
+ * the message passing work themselves, only stopcpu and reschedule
+ * messages come through here.
+ */
+void smp4d_message_pass(int target, int msg, unsigned long data, int wait)
+{
+	int me = hard_smp4d_processor_id();
+
+	SMP_PRINTK(("smp4d_message_pass %d %d %08lx %d\n", target, msg, data, wait));
+	if (msg == MSG_STOP_CPU && target == MSG_ALL_BUT_SELF) {
+		unsigned long flags;
+		static spinlock_t stop_cpu_lock = SPIN_LOCK_UNLOCKED;
+		spin_lock_irqsave(&stop_cpu_lock, flags);
+		smp4d_stop_cpu_sender = me;
+		smp4d_cross_call((smpfunc_t)smp4d_stop_cpu, 0, 0, 0, 0, 0);
+		spin_unlock_irqrestore(&stop_cpu_lock, flags);
+	}
+	printk("Yeeee, trying to send SMP msg(%d) to %d on cpu %d\n", msg, target, me);
+	panic("Bogon SMP message pass.");
+}
+
+/* Protects counters touched during level14 ticker */
+static spinlock_t ticker_lock = SPIN_LOCK_UNLOCKED;
+
+#ifdef CONFIG_PROFILE
+
+/* 32-bit Sparc specific profiling function. */
+static inline void sparc_do_profile(unsigned long pc)
+{
+	if(prof_buffer && current->pid) {
+		extern int _stext;
+
+		pc -= (unsigned long) &_stext;
+		pc >>= prof_shift;
+
+		spin_lock(&ticker_lock);
+		if(pc < prof_len)
+			prof_buffer[pc]++;
+		else
+			prof_buffer[prof_len - 1]++;
+		spin_unlock(&ticker_lock);
+	}
+}
+
+#endif
+
+extern unsigned int prof_multiplier[NR_CPUS];
+extern unsigned int prof_counter[NR_CPUS];
+
+extern void update_one_process(struct task_struct *p, unsigned long ticks,
+			       unsigned long user, unsigned long system,
+			       int cpu);
+			       
+
+void smp4d_percpu_timer_interrupt(struct pt_regs *regs)
+{
+	int cpu = hard_smp4d_processor_id();
+	static int cpu_tick[NR_CPUS];
+	static char led_mask[] = { 0xe, 0xd, 0xb, 0x7, 0xb, 0xd };
+
+	bw_get_prof_limit(cpu);	
+	bw_clear_intr_mask(0, 1);	/* INTR_TABLE[0] & 1 is Profile IRQ */
+
+	cpu_tick[cpu]++;
+	if (!(cpu_tick[cpu] & 15)) {
+		if (cpu_tick[cpu] == 0x60)
+			cpu_tick[cpu] = 0;
+		cpu_leds[cpu] = led_mask[cpu_tick[cpu] >> 4];
+		show_leds(cpu);
+	}
+
+#ifdef CONFIG_PROFILE
+	if(!user_mode(regs))
+		sparc_do_profile(regs->pc);
+#endif
+	if(!--prof_counter[cpu]) {
+		int user = user_mode(regs);
+		if(current->pid) {
+			update_one_process(current, 1, user, !user, cpu);
+
+			if(--current->counter < 0) {
+				current->counter = 0;
+				need_resched = 1;
+			}
+
+			spin_lock(&ticker_lock);
+			if(user) {
+				if(current->priority < DEF_PRIORITY) {
+					kstat.cpu_nice++;
+					kstat.per_cpu_nice[cpu]++;
+				} else {
+					kstat.cpu_user++;
+					kstat.per_cpu_user[cpu]++;
+				}
+			} else {
+				kstat.cpu_system++;
+				kstat.per_cpu_system[cpu]++;
+			}
+			spin_unlock(&ticker_lock);
+		}
+		prof_counter[cpu] = prof_multiplier[cpu];
+	}
+}
+
+extern unsigned int lvl14_resolution;
+
+__initfunc(static void smp_setup_percpu_timer(void))
+{
+	int cpu = hard_smp4d_processor_id();
+
+	prof_counter[cpu] = prof_multiplier[cpu] = 1;
+	load_profile_irq(cpu, lvl14_resolution);
+}
+
+__initfunc(void smp4d_blackbox_id(unsigned *addr))
+{
+	int rd = *addr & 0x3e000000;
+	
+	addr[0] = 0xc0800800 | rd;		/* lda [%g0] ASI_M_VIKING_TMP1, reg */
+	addr[1] = 0x01000000;    		/* nop */
+	addr[2] = 0x01000000;    		/* nop */
+}
+
+__initfunc(void smp4d_blackbox_current(unsigned *addr))
+{
+	/* We have a nice Linux current register :) */
+	int rd = addr[1] & 0x3e000000;
+	
+	addr[0] = 0x10800006;			/* b .+24 */
+	addr[1] = 0xc0800820 | rd;		/* lda [%g0] ASI_M_VIKING_TMP2, reg */
+}
+
+__initfunc(void sun4d_init_smp(void))
+{
+	int i;
+	extern unsigned int patchme_store_new_current[];
+	extern unsigned int t_nmi[], linux_trap_ipi15_sun4d[], linux_trap_ipi15_sun4m[];
+
+	/* Store current into Linux current register :) */
+	__asm__ __volatile__("sta %%g6, [%%g0] %0" : : "i"(ASI_M_VIKING_TMP2));
+	
+	/* Patch switch_to */
+	patchme_store_new_current[0] = (patchme_store_new_current[0] & 0x3e000000) | 0xc0a00820;
+	
+	/* Patch ipi15 trap table */
+	t_nmi[1] = t_nmi[1] + (linux_trap_ipi15_sun4d - linux_trap_ipi15_sun4m);
+	
+	/* And set btfixup... */
+	BTFIXUPSET_BLACKBOX(smp_processor_id, smp4d_blackbox_id);
+	BTFIXUPSET_BLACKBOX(load_current, smp4d_blackbox_current);
+	BTFIXUPSET_CALL(smp_cross_call, smp4d_cross_call, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(smp_message_pass, smp4d_message_pass, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(smp_bogo_info, smp4d_bogo_info, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(smp_info, smp4d_info, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(__smp_processor_id, __smp4d_processor_id, BTFIXUPCALL_NORM);
+	
+	for (i = 0; i < NR_CPUS; i++) {
+		ccall_info.processors_in[i] = 1;
+		ccall_info.processors_out[i] = 1;
+	}
+}
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/kernel/sun4m_irq.c linux/arch/sparc/kernel/sun4m_irq.c
--- v2.1.96/linux/arch/sparc/kernel/sun4m_irq.c	Mon Jan 12 15:15:43 1998
+++ linux/arch/sparc/kernel/sun4m_irq.c	Tue Apr 14 17:44:19 1998
@@ -83,9 +83,9 @@
 		if (!mask)
 			printk("sun4m_get_irqmask: IRQ%d has no valid mask!\n",irq);
 	} else {
-		/* Soft Interrupts will come here
-		 * Currently there is no way to trigger them but I'm sure something
-		 * could be cooked up.
+		/* Soft Interrupts will come here.
+		 * Currently there is no way to trigger them but I'm sure
+		 * something could be cooked up.
 		 */
 		irq &= 0xf;
 		mask = SUN4M_SOFT_INT(irq);
@@ -349,18 +349,18 @@
 				&sun4m_interrupts->undirected_target;
 		sun4m_interrupts->undirected_target = 0;
 	}
-	enable_irq = sun4m_enable_irq;
-	disable_irq = sun4m_disable_irq;
-	enable_pil_irq = sun4m_enable_pil_irq;
-	disable_pil_irq = sun4m_disable_pil_irq;
-	clear_clock_irq = sun4m_clear_clock_irq;
-	clear_profile_irq = sun4m_clear_profile_irq;
-	load_profile_irq = sun4m_load_profile_irq;
+	BTFIXUPSET_CALL(enable_irq, sun4m_enable_irq, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(disable_irq, sun4m_disable_irq, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(enable_pil_irq, sun4m_enable_pil_irq, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(disable_pil_irq, sun4m_disable_pil_irq, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(clear_clock_irq, sun4m_clear_clock_irq, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(clear_profile_irq, sun4m_clear_profile_irq, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(load_profile_irq, sun4m_load_profile_irq, BTFIXUPCALL_NORM);
 	init_timers = sun4m_init_timers;
 #ifdef __SMP__
-	set_cpu_int = (void (*) (int, int))sun4m_send_ipi;
-	clear_cpu_int = (void (*) (int, int))sun4m_clear_ipi;
-	set_irq_udt = (void (*) (int))sun4m_set_udt;
+	BTFIXUPSET_CALL(set_cpu_int, sun4m_send_ipi, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(clear_cpu_int, sun4m_clear_ipi, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(set_irq_udt, sun4m_set_udt, BTFIXUPCALL_NORM);
 #endif
 	/* Cannot enable interrupts until OBP ticker is disabled. */
 }
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/kernel/sun4m_smp.c linux/arch/sparc/kernel/sun4m_smp.c
--- v2.1.96/linux/arch/sparc/kernel/sun4m_smp.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc/kernel/sun4m_smp.c	Tue Apr 14 17:44:19 1998
@@ -0,0 +1,545 @@
+/* sun4m_smp.c: Sparc SUN4M SMP support.
+ *
+ * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <linux/config.h> /* for CONFIG_PROFILE */
+#include <asm/head.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/tasks.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/interrupt.h>
+#include <linux/kernel_stat.h>
+#include <linux/init.h>
+
+#include <asm/ptrace.h>
+#include <asm/atomic.h>
+
+#include <asm/delay.h>
+#include <asm/irq.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/oplib.h>
+#include <asm/atops.h>
+#include <asm/spinlock.h>
+#include <asm/hardirq.h>
+#include <asm/softirq.h>
+
+#define __KERNEL_SYSCALLS__
+#include <linux/unistd.h>
+
+#define IRQ_RESCHEDULE		13
+#define IRQ_STOP_CPU		14
+#define IRQ_CROSS_CALL		15
+
+extern ctxd_t *srmmu_ctx_table_phys;
+extern int linux_num_cpus;
+
+extern void calibrate_delay(void);
+
+extern struct task_struct *current_set[NR_CPUS];
+extern volatile int smp_processors_ready;
+extern unsigned long cpu_present_map;
+extern int smp_num_cpus;
+extern int smp_threads_ready;
+extern unsigned char mid_xlate[NR_CPUS];
+extern volatile unsigned long cpu_callin_map[NR_CPUS];
+extern unsigned long smp_proc_in_lock[NR_CPUS];
+extern struct cpuinfo_sparc cpu_data[NR_CPUS];
+extern unsigned long cpu_offset[NR_CPUS];
+extern unsigned char boot_cpu_id;
+extern int smp_activated;
+extern volatile int cpu_number_map[NR_CPUS];
+extern volatile int __cpu_logical_map[NR_CPUS];
+extern struct klock_info klock_info;
+extern volatile unsigned long ipi_count;
+extern volatile int smp_process_available;
+extern volatile int smp_commenced;
+extern int __smp4m_processor_id(void);
+
+/*#define SMP_DEBUG*/
+
+#ifdef SMP_DEBUG
+#define SMP_PRINTK(x)	printk x
+#else
+#define SMP_PRINTK(x)
+#endif
+
+int smp4m_bogo_info(char *buf)
+{
+	return sprintf(buf,
+            "Cpu0Bogo\t: %lu.%02lu\n"
+	    "Cpu1Bogo\t: %lu.%02lu\n"
+	    "Cpu2Bogo\t: %lu.%02lu\n"
+	    "Cpu3Bogo\t: %lu.%02lu\n",
+	    cpu_data[0].udelay_val/500000, (cpu_data[0].udelay_val/5000)%100,
+	    cpu_data[1].udelay_val/500000, (cpu_data[1].udelay_val/5000)%100,
+	    cpu_data[2].udelay_val/500000, (cpu_data[2].udelay_val/5000)%100,
+	    cpu_data[3].udelay_val/500000, (cpu_data[3].udelay_val/5000)%100);
+}
+
+int smp4m_info(char *buf)
+{
+	return sprintf(buf,
+"        CPU0\t\tCPU1\t\tCPU2\t\tCPU3\n"
+"State:  %s\t\t%s\t\t%s\t\t%s\n",
+(cpu_present_map & 1) ? ((klock_info.akp == 0) ? "akp" : "online") : "offline",
+(cpu_present_map & 2) ? ((klock_info.akp == 1) ? "akp" : "online") : "offline",
+(cpu_present_map & 4) ? ((klock_info.akp == 2) ? "akp" : "online") : "offline",
+(cpu_present_map & 8) ? ((klock_info.akp == 3) ? "akp" : "online") : "offline");
+}
+
+static inline unsigned long swap(volatile unsigned long *ptr, unsigned long val)
+{
+	__asm__ __volatile__("swap [%1], %0\n\t" :
+			     "=&r" (val), "=&r" (ptr) :
+			     "0" (val), "1" (ptr));
+	return val;
+}
+
+static void smp_setup_percpu_timer(void);
+extern void cpu_probe(void);
+
+__initfunc(void smp4m_callin(void))
+{
+	int cpuid = hard_smp_processor_id();
+
+	local_flush_cache_all();
+	local_flush_tlb_all();
+	set_irq_udt(mid_xlate[boot_cpu_id]);
+
+	/* Get our local ticker going. */
+	smp_setup_percpu_timer();
+
+	calibrate_delay();
+	smp_store_cpu_info(cpuid);
+	local_flush_cache_all();
+	local_flush_tlb_all();
+
+	/* Allow master to continue. */
+	swap((unsigned long *)&cpu_callin_map[cpuid], 1);
+	local_flush_cache_all();
+	local_flush_tlb_all();
+	
+	cpu_probe();
+
+	while(!task[cpuid] || current_set[cpuid] != task[cpuid])
+		barrier();
+
+	/* Fix idle thread fields. */
+	__asm__ __volatile__("ld [%0], %%g6\n\t"
+			     : : "r" (&current_set[cpuid])
+			     : "memory" /* paranoid */);
+	current->mm->mmap->vm_page_prot = PAGE_SHARED;
+	current->mm->mmap->vm_start = PAGE_OFFSET;
+	current->mm->mmap->vm_end = init_task.mm->mmap->vm_end;
+	
+	while(!smp_commenced)
+		barrier();
+
+	local_flush_cache_all();
+	local_flush_tlb_all();
+
+	__sti();
+}
+
+extern int cpu_idle(void *unused);
+extern void init_IRQ(void);
+extern void cpu_panic(void);
+extern int start_secondary(void *unused);
+
+/*
+ *	Cycle through the processors asking the PROM to start each one.
+ */
+ 
+extern struct prom_cpuinfo linux_cpus[NR_CPUS];
+extern struct linux_prom_registers smp_penguin_ctable;
+extern unsigned long trapbase_cpu1[];
+extern unsigned long trapbase_cpu2[];
+extern unsigned long trapbase_cpu3[];
+
+__initfunc(void smp4m_boot_cpus(void))
+{
+	int cpucount = 0;
+	int i = 0;
+	int first, prev;
+
+	printk("Entering SMP Mode...\n");
+
+	smp_penguin_ctable.which_io = 0;
+	smp_penguin_ctable.phys_addr = (unsigned int) srmmu_ctx_table_phys;
+	smp_penguin_ctable.reg_size = 0;
+
+	for (i = 0; i < NR_CPUS; i++)
+		cpu_offset[i] = (char *)&cpu_data[i] - (char *)&cpu_data;
+
+	__sti();
+	cpu_present_map = 0;
+	for(i=0; i < linux_num_cpus; i++)
+		cpu_present_map |= (1<<i);
+	for(i=0; i < NR_CPUS; i++)
+		cpu_number_map[i] = -1;
+	for(i=0; i < NR_CPUS; i++)
+		__cpu_logical_map[i] = -1;
+	mid_xlate[boot_cpu_id] = (linux_cpus[boot_cpu_id].mid & ~8);
+	cpu_number_map[boot_cpu_id] = 0;
+	__cpu_logical_map[0] = boot_cpu_id;
+	klock_info.akp = boot_cpu_id;
+	current->processor = boot_cpu_id;
+	smp_store_cpu_info(boot_cpu_id);
+	set_irq_udt(mid_xlate[boot_cpu_id]);
+	smp_setup_percpu_timer();
+	local_flush_cache_all();
+	if(linux_num_cpus == 1)
+		return;  /* Not an MP box. */
+	for(i = 0; i < NR_CPUS; i++) {
+		if(i == boot_cpu_id)
+			continue;
+
+		if(cpu_present_map & (1 << i)) {
+			extern unsigned long sun4m_cpu_startup;
+			unsigned long *entry = &sun4m_cpu_startup;
+			struct task_struct *p;
+			int timeout;
+
+			/* Cook up an idler for this guy. */
+			kernel_thread(start_secondary, NULL, CLONE_PID);
+
+			p = task[++cpucount];
+
+			p->processor = i;
+			current_set[i] = p;
+
+			/* See trampoline.S for details... */
+			entry += ((i-1) * 3);
+
+			/* whirrr, whirrr, whirrrrrrrrr... */
+			printk("Starting CPU %d at %p\n", i, entry);
+			mid_xlate[i] = (linux_cpus[i].mid & ~8);
+			local_flush_cache_all();
+			prom_startcpu(linux_cpus[i].prom_node,
+				      &smp_penguin_ctable, 0, (char *)entry);
+
+			/* wheee... it's going... */
+			for(timeout = 0; timeout < 5000000; timeout++) {
+				if(cpu_callin_map[i])
+					break;
+				udelay(100);
+			}
+			if(cpu_callin_map[i]) {
+				/* Another "Red Snapper". */
+				cpu_number_map[i] = i;
+				__cpu_logical_map[i] = i;
+			} else {
+				cpucount--;
+				printk("Processor %d is stuck.\n", i);
+			}
+		}
+		if(!(cpu_callin_map[i])) {
+			cpu_present_map &= ~(1 << i);
+			cpu_number_map[i] = -1;
+		}
+	}
+	local_flush_cache_all();
+	if(cpucount == 0) {
+		printk("Error: only one Processor found.\n");
+		cpu_present_map = (1 << smp_processor_id());
+	} else {
+		unsigned long bogosum = 0;
+		for(i = 0; i < NR_CPUS; i++) {
+			if(cpu_present_map & (1 << i))
+				bogosum += cpu_data[i].udelay_val;
+		}
+		printk("Total of %d Processors activated (%lu.%02lu BogoMIPS).\n",
+		       cpucount + 1,
+		       (bogosum + 2500)/500000,
+		       ((bogosum + 2500)/5000)%100);
+		smp_activated = 1;
+		smp_num_cpus = cpucount + 1;
+	}
+
+	/* Setup CPU list for IRQ distribution scheme. */
+	first = prev = -1;
+	for(i = 0; i < NR_CPUS; i++) {
+		if(cpu_present_map & (1 << i)) {
+			if(first == -1)
+				first = i;
+			if(prev != -1)
+				cpu_data[prev].next = i;
+			cpu_data[i].mid = mid_xlate[i];
+			prev = i;
+		}
+	}
+	cpu_data[prev].next = first;
+	
+	/* Free unneeded trap tables */
+	
+	if (!(cpu_present_map & (1 << 1))) {
+		mem_map[MAP_NR((unsigned long)trapbase_cpu1)].flags &= ~(1 << PG_reserved);
+		free_page((unsigned long)trapbase_cpu1);
+	}
+	if (!(cpu_present_map & (1 << 2))) {
+		mem_map[MAP_NR((unsigned long)trapbase_cpu2)].flags &= ~(1 << PG_reserved);
+		free_page((unsigned long)trapbase_cpu2);
+	}
+	if (!(cpu_present_map & (1 << 3))) {
+		mem_map[MAP_NR((unsigned long)trapbase_cpu3)].flags &= ~(1 << PG_reserved);
+		free_page((unsigned long)trapbase_cpu3);
+	}
+
+	/* Ok, they are spinning and ready to go. */
+	smp_processors_ready = 1;
+}
+
+/* At each hardware IRQ, we get this called to forward IRQ reception
+ * to the next processor.  The caller must disable the IRQ level being
+ * serviced globally so that there are no double interrupts received.
+ */
+void smp4m_irq_rotate(int cpu)
+{
+	if(smp_processors_ready)
+		set_irq_udt(cpu_data[cpu_data[cpu].next].mid);
+}
+
+/* Cross calls, in order to work efficiently and atomically do all
+ * the message passing work themselves, only stopcpu and reschedule
+ * messages come through here.
+ */
+void smp4m_message_pass(int target, int msg, unsigned long data, int wait)
+{
+	static unsigned long smp_cpu_in_msg[NR_CPUS];
+	unsigned long mask;
+	int me = smp_processor_id();
+	int irq, i;
+
+	if(msg == MSG_RESCHEDULE) {
+		irq = IRQ_RESCHEDULE;
+
+		if(smp_cpu_in_msg[me])
+			return;
+	} else if(msg == MSG_STOP_CPU) {
+		irq = IRQ_STOP_CPU;
+	} else {
+		goto barf;
+	}
+
+	smp_cpu_in_msg[me]++;
+	if(target == MSG_ALL_BUT_SELF || target == MSG_ALL) {
+		mask = cpu_present_map;
+		if(target == MSG_ALL_BUT_SELF)
+			mask &= ~(1 << me);
+		for(i = 0; i < 4; i++) {
+			if(mask & (1 << i))
+				set_cpu_int(mid_xlate[i], irq);
+		}
+	} else {
+		set_cpu_int(mid_xlate[target], irq);
+	}
+	smp_cpu_in_msg[me]--;
+
+	return;
+barf:
+	printk("Yeeee, trying to send SMP msg(%d) on cpu %d\n", msg, me);
+	panic("Bogon SMP message pass.");
+}
+
+static struct smp_funcall {
+	smpfunc_t func;
+	unsigned long arg1;
+	unsigned long arg2;
+	unsigned long arg3;
+	unsigned long arg4;
+	unsigned long arg5;
+	unsigned long processors_in[NR_CPUS];  /* Set when ipi entered. */
+	unsigned long processors_out[NR_CPUS]; /* Set when ipi exited. */
+} ccall_info;
+
+static spinlock_t cross_call_lock = SPIN_LOCK_UNLOCKED;
+
+/* Cross calls must be serialized, at least currently. */
+void smp4m_cross_call(smpfunc_t func, unsigned long arg1, unsigned long arg2,
+		    unsigned long arg3, unsigned long arg4, unsigned long arg5)
+{
+	if(smp_processors_ready) {
+		register int ncpus = smp_num_cpus;
+		unsigned long flags;
+
+		spin_lock_irqsave(&cross_call_lock, flags);
+
+		/* Init function glue. */
+		ccall_info.func = func;
+		ccall_info.arg1 = arg1;
+		ccall_info.arg2 = arg2;
+		ccall_info.arg3 = arg3;
+		ccall_info.arg4 = arg4;
+		ccall_info.arg5 = arg5;
+
+		/* Init receive/complete mapping, plus fire the IPI's off. */
+		{
+			register unsigned long mask;
+			register int i;
+
+			mask = (cpu_present_map & ~(1 << smp_processor_id()));
+			for(i = 0; i < ncpus; i++) {
+				if(mask & (1 << i)) {
+					ccall_info.processors_in[i] = 0;
+					ccall_info.processors_out[i] = 0;
+					set_cpu_int(mid_xlate[i], IRQ_CROSS_CALL);
+				} else {
+					ccall_info.processors_in[i] = 1;
+					ccall_info.processors_out[i] = 1;
+				}
+			}
+		}
+
+		/* First, run local copy. */
+		func(arg1, arg2, arg3, arg4, arg5);
+
+		{
+			register int i;
+
+			i = 0;
+			do {
+				while(!ccall_info.processors_in[i])
+					barrier();
+			} while(++i < ncpus);
+
+			i = 0;
+			do {
+				while(!ccall_info.processors_out[i])
+					barrier();
+			} while(++i < ncpus);
+		}
+
+		spin_unlock_irqrestore(&cross_call_lock, flags);
+	} else
+		func(arg1, arg2, arg3, arg4, arg5); /* Just need to run local copy. */
+}
+
+/* Running cross calls. */
+void smp4m_cross_call_irq(void)
+{
+	int i = smp_processor_id();
+
+	ccall_info.processors_in[i] = 1;
+	ccall_info.func(ccall_info.arg1, ccall_info.arg2, ccall_info.arg3,
+			ccall_info.arg4, ccall_info.arg5);
+	ccall_info.processors_out[i] = 1;
+}
+
+/* Protects counters touched during level14 ticker */
+static spinlock_t ticker_lock = SPIN_LOCK_UNLOCKED;
+
+#ifdef CONFIG_PROFILE
+
+/* 32-bit Sparc specific profiling function. */
+static inline void sparc_do_profile(unsigned long pc)
+{
+	if(prof_buffer && current->pid) {
+		extern int _stext;
+
+		pc -= (unsigned long) &_stext;
+		pc >>= prof_shift;
+
+		spin_lock(&ticker_lock);
+		if(pc < prof_len)
+			prof_buffer[pc]++;
+		else
+			prof_buffer[prof_len - 1]++;
+		spin_unlock(&ticker_lock);
+	}
+}
+
+#endif
+
+extern unsigned int prof_multiplier[NR_CPUS];
+extern unsigned int prof_counter[NR_CPUS];
+
+extern void update_one_process(struct task_struct *p, unsigned long ticks,
+			       unsigned long user, unsigned long system,
+			       int cpu);
+
+void smp4m_percpu_timer_interrupt(struct pt_regs *regs)
+{
+	int cpu = smp_processor_id();
+
+	clear_profile_irq(mid_xlate[cpu]);
+#ifdef CONFIG_PROFILE
+	if(!user_mode(regs))
+		sparc_do_profile(regs->pc);
+#endif
+	if(!--prof_counter[cpu]) {
+		int user = user_mode(regs);
+		if(current->pid) {
+			update_one_process(current, 1, user, !user, cpu);
+
+			if(--current->counter < 0) {
+				current->counter = 0;
+				need_resched = 1;
+			}
+
+			spin_lock(&ticker_lock);
+			if(user) {
+				if(current->priority < DEF_PRIORITY) {
+					kstat.cpu_nice++;
+					kstat.per_cpu_nice[cpu]++;
+				} else {
+					kstat.cpu_user++;
+					kstat.per_cpu_user[cpu]++;
+				}
+			} else {
+				kstat.cpu_system++;
+				kstat.per_cpu_system[cpu]++;
+			}
+			spin_unlock(&ticker_lock);
+		}
+		prof_counter[cpu] = prof_multiplier[cpu];
+	}
+}
+
+extern unsigned int lvl14_resolution;
+
+__initfunc(static void smp_setup_percpu_timer(void))
+{
+	int cpu = smp_processor_id();
+
+	prof_counter[cpu] = prof_multiplier[cpu] = 1;
+	load_profile_irq(mid_xlate[cpu], lvl14_resolution);
+
+	if(cpu == boot_cpu_id)
+		enable_pil_irq(14);
+}
+
+__initfunc(void smp4m_blackbox_id(unsigned *addr))
+{
+	int rd = *addr & 0x3e000000;
+	int rs1 = rd >> 11;
+	
+	addr[0] = 0x81580000 | rd;		/* rd %tbr, reg */
+	addr[1] = 0x8130200c | rd | rs1;    	/* srl reg, 0xc, reg */
+	addr[2] = 0x80082003 | rd | rs1;	/* and reg, 3, reg */
+}
+
+__initfunc(void smp4m_blackbox_current(unsigned *addr))
+{
+	int rd = *addr & 0x3e000000;
+	int rs1 = rd >> 11;
+	
+	addr[0] = 0x81580000 | rd;		/* rd %tbr, reg */
+	addr[2] = 0x8130200a | rd | rs1;    	/* srl reg, 0xa, reg */
+	addr[4] = 0x8008200c | rd | rs1;	/* and reg, 3, reg */
+}
+
+__initfunc(void sun4m_init_smp(void))
+{
+	BTFIXUPSET_BLACKBOX(smp_processor_id, smp4m_blackbox_id);
+	BTFIXUPSET_BLACKBOX(load_current, smp4m_blackbox_current);
+	BTFIXUPSET_CALL(smp_cross_call, smp4m_cross_call, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(smp_message_pass, smp4m_message_pass, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(smp_bogo_info, smp4m_bogo_info, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(smp_info, smp4m_info, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(__smp_processor_id, __smp4m_processor_id, BTFIXUPCALL_NORM);
+}
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/kernel/sunos_ioctl.c linux/arch/sparc/kernel/sunos_ioctl.c
--- v2.1.96/linux/arch/sparc/kernel/sunos_ioctl.c	Mon Jan 12 15:15:43 1998
+++ linux/arch/sparc/kernel/sunos_ioctl.c	Tue Apr 14 17:44:19 1998
@@ -1,4 +1,4 @@
-/* $Id: sunos_ioctl.c,v 1.29 1997/09/18 10:37:31 rth Exp $
+/* $Id: sunos_ioctl.c,v 1.30 1998/01/21 06:17:32 ecd Exp $
  * sunos_ioctl.c: The Linux Operating system: SunOS ioctl compatibility.
  * 
  * Copyright (C) 1995 Miguel de Icaza (miguel@nuclecu.unam.mx)
@@ -114,7 +114,7 @@
 		ret = sys_ioctl(fd, SIOCGIFBRDADDR, arg);
 		goto out;
 	case _IOW('i', 24, struct ifreq):
-		ret = sys_ioctl(fd, SIOCGIFBRDADDR, arg);
+		ret = sys_ioctl(fd, SIOCSIFBRDADDR, arg);
 		goto out;
 	case _IOWR('i', 25, struct ifreq):
 		ret = sys_ioctl(fd, SIOCGIFNETMASK, arg);
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/kernel/sys_sparc.c linux/arch/sparc/kernel/sys_sparc.c
--- v2.1.96/linux/arch/sparc/kernel/sys_sparc.c	Mon Jan 12 15:15:43 1998
+++ linux/arch/sparc/kernel/sys_sparc.c	Tue Apr 14 17:44:19 1998
@@ -1,4 +1,4 @@
-/* $Id: sys_sparc.c,v 1.38 1998/01/09 16:42:48 jj Exp $
+/* $Id: sys_sparc.c,v 1.40 1998/03/28 08:29:26 davem Exp $
  * linux/arch/sparc/kernel/sys_sparc.c
  *
  * This file contains various random system calls that
@@ -10,8 +10,9 @@
 #include <linux/types.h>
 #include <linux/sched.h>
 #include <linux/config.h>
-#include <linux/fs.h>
 #include <linux/mm.h>
+#include <linux/fs.h>
+#include <linux/file.h>
 #include <linux/sem.h>
 #include <linux/msg.h>
 #include <linux/shm.h>
@@ -39,7 +40,7 @@
 	unsigned long ret;
 
 	lock_kernel();
-	if(sparc_cpu_model == sun4c) {
+	if(ARCH_SUN4C_SUN4) {
 		if(brk >= 0x20000000 && brk < 0xe0000000) {
 			ret = current->mm->brk;
 			goto out;
@@ -192,31 +193,34 @@
 
 	lock_kernel();
 	if (!(flags & MAP_ANONYMOUS)) {
-		if (fd >= NR_OPEN || !(file = current->files->fd[fd])){
+		file = fget(fd);
+		if (!file)
 			goto out;
-	    }
 	}
 	retval = -ENOMEM;
 	if(!(flags & MAP_FIXED) && !addr) {
 		addr = get_unmapped_area(addr, len);
-		if(!addr){
-			goto out;
-		}
+		if(!addr)
+			goto out_putf;
 	}
 
 	/* See asm-sparc/uaccess.h */
 	retval = -EINVAL;
 	if((len > (TASK_SIZE - PAGE_SIZE)) || (addr > (TASK_SIZE-len-PAGE_SIZE)))
-		goto out;
+		goto out_putf;
 
-	if(sparc_cpu_model == sun4c) {
+	if(ARCH_SUN4C_SUN4) {
 		if(((addr >= 0x20000000) && (addr < 0xe0000000))) {
 			retval = current->mm->brk;
-			goto out;
+			goto out_putf;
 		}
 	}
 
 	retval = do_mmap(file, addr, len, prot, flags, off);
+
+out_putf:
+	if (file)
+		fput(file);
 out:
 	unlock_kernel();
 	return retval;
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/kernel/sys_sunos.c linux/arch/sparc/kernel/sys_sunos.c
--- v2.1.96/linux/arch/sparc/kernel/sys_sunos.c	Mon Jan 12 15:15:43 1998
+++ linux/arch/sparc/kernel/sys_sunos.c	Tue Apr 14 17:44:19 1998
@@ -1,4 +1,4 @@
-/* $Id: sys_sunos.c,v 1.83 1997/12/14 23:24:28 ecd Exp $
+/* $Id: sys_sunos.c,v 1.87 1998/03/29 03:48:16 shadow Exp $
  * sys_sunos.c: SunOS specific syscall compatibility support.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -17,6 +17,7 @@
 #include <linux/mm.h>
 #include <linux/swap.h>
 #include <linux/fs.h>
+#include <linux/file.h>
 #include <linux/resource.h>
 #include <linux/ipc.h>
 #include <linux/shm.h>
@@ -77,14 +78,19 @@
 		flags &= ~MAP_NORESERVE;
 	}
 	retval = -EBADF;
-	if(!(flags & MAP_ANONYMOUS))
-		if (fd >= SUNOS_NR_OPEN || !(file = current->files->fd[fd]))
+	if(!(flags & MAP_ANONYMOUS)) {
+		if (fd >= SUNOS_NR_OPEN)
 			goto out;
+		file = fget(fd);
+		if (!file)
+			goto out;
+	}
+
 	retval = -ENOMEM;
 	if(!(flags & MAP_FIXED) && !addr) {
 		addr = get_unmapped_area(addr, len);
 		if(!addr)
-			goto out;
+			goto out_putf;
 	}
 	/* If this is ld.so or a shared library doing an mmap
 	 * of /dev/zero, transform it into an anonymous mapping.
@@ -105,18 +111,22 @@
 	/* See asm-sparc/uaccess.h */
 	retval = -EINVAL;
 	if((len > (TASK_SIZE - PAGE_SIZE)) || (addr > (TASK_SIZE-len-PAGE_SIZE)))
-		goto out;
+		goto out_putf;
 
-	if(sparc_cpu_model == sun4c) {
+	if(ARCH_SUN4C_SUN4) {
 		if(((addr >= 0x20000000) && (addr < 0xe0000000))) {
 			retval = current->mm->brk;
-			goto out;
+			goto out_putf;
 		}
 	}
 
 	retval = do_mmap(file, addr, len, prot, flags, off);
 	if(!ret_type)
 		retval = ((retval < PAGE_OFFSET) ? 0 : retval);
+
+out_putf:
+	if (file)
+		fput(file);
 out:
 	unlock_kernel();
 	return retval;
@@ -139,7 +149,7 @@
 	unsigned long newbrk, oldbrk;
 
 	lock_kernel();
-	if(sparc_cpu_model == sun4c) {
+	if(ARCH_SUN4C_SUN4) {
 		if(brk >= 0x20000000 && brk < 0xe0000000) {
 			goto out;
 		}
@@ -423,39 +433,48 @@
 asmlinkage int sunos_getdents(unsigned int fd, void * dirent, int cnt)
 {
 	struct file * file;
+	struct inode * inode;
 	struct sunos_dirent * lastdirent;
 	struct sunos_dirent_callback buf;
 	int error = -EBADF;
 
 	lock_kernel();
-	if(fd >= SUNOS_NR_OPEN)
+	if (fd >= SUNOS_NR_OPEN)
 		goto out;
 
-	file = current->files->fd[fd];
-	if(!file)
+	file = fget(fd);
+	if (!file)
 		goto out;
 
 	error = -ENOTDIR;
 	if (!file->f_op || !file->f_op->readdir)
-		goto out;
+		goto out_putf;
 
 	error = -EINVAL;
-	if(cnt < (sizeof(struct sunos_dirent) + 255))
-		goto out;
+	if (cnt < (sizeof(struct sunos_dirent) + 255))
+		goto out_putf;
 
 	buf.curr = (struct sunos_dirent *) dirent;
 	buf.previous = NULL;
 	buf.count = cnt;
 	buf.error = 0;
+
+	inode = file->f_dentry->d_inode;
+	down(&inode->i_sem);
 	error = file->f_op->readdir(file, &buf, sunos_filldir);
+	up(&inode->i_sem);
 	if (error < 0)
-		goto out;
+		goto out_putf;
+
 	lastdirent = buf.previous;
 	error = buf.error;
 	if (lastdirent) {
 		put_user(file->f_pos, &lastdirent->d_off);
 		error = cnt - buf.count;
 	}
+
+out_putf:
+	fput(file);
 out:
 	unlock_kernel();
 	return error;
@@ -503,39 +522,48 @@
 asmlinkage int sunos_getdirentries(unsigned int fd, void * dirent, int cnt, unsigned int *basep)
 {
 	struct file * file;
+	struct inode * inode;
 	struct sunos_direntry * lastdirent;
 	struct sunos_direntry_callback buf;
 	int error = -EBADF;
 
 	lock_kernel();
-	if(fd >= SUNOS_NR_OPEN)
+	if (fd >= SUNOS_NR_OPEN)
 		goto out;
 
-	file = current->files->fd[fd];
-	if(!file)
+	file = fget(fd);
+	if (!file)
 		goto out;
 
 	error = -ENOTDIR;
 	if (!file->f_op || !file->f_op->readdir)
-		goto out;
+		goto out_putf;
 
 	error = -EINVAL;
 	if(cnt < (sizeof(struct sunos_direntry) + 255))
-		goto out;
+		goto out_putf;
 
 	buf.curr = (struct sunos_direntry *) dirent;
 	buf.previous = NULL;
 	buf.count = cnt;
 	buf.error = 0;
+
+	inode = file->f_dentry->d_inode;
+	down(&inode->i_sem);
 	error = file->f_op->readdir(file, &buf, sunos_filldirentry);
+	up(&inode->i_sem);
 	if (error < 0)
-		goto out;
+		goto out_putf;
+
 	lastdirent = buf.previous;
 	error = buf.error;
 	if (lastdirent) {
 		put_user(file->f_pos, basep);
 		error = cnt - buf.count;
 	}
+
+out_putf:
+	fput(file);
 out:
 	unlock_kernel();
 	return error;
@@ -669,6 +697,15 @@
 	lock_kernel();
 	current->personality |= STICKY_TIMEOUTS;
 	ret = sys_select (width, inp, outp, exp, tvp);
+	if (ret == -EINTR && tvp) {
+		time_t sec, usec;
+
+		__get_user(sec, &tvp->tv_sec);
+		__get_user(usec, &tvp->tv_usec);
+
+		if (sec == 0 && usec == 0)
+			ret = 0;
+	}
 	unlock_kernel();
 	return ret;
 }
@@ -720,7 +757,7 @@
 
 /* Bind the socket on a local reserved port and connect it to the
  * remote server.  This on Linux/i386 is done by the mount program,
- * not by the kernel. 
+ * not by the kernel.
  */
 static int
 sunos_nfs_get_server_fd (int fd, struct sockaddr_in *addr)
@@ -728,16 +765,16 @@
 	struct sockaddr_in local;
 	struct sockaddr_in server;
 	int    try_port;
-	int    ret;
 	struct socket *socket;
-	struct dentry *dentry;
 	struct inode  *inode;
 	struct file   *file;
+	int    ret, result = 0;
 
-	file = current->files->fd [fd];
-	dentry = file->f_dentry;
-	if(!dentry || !(inode = dentry->d_inode))
-		return 0;
+	file = fget(fd);
+	if (!file)
+		goto out;
+	if (!file->f_dentry || !(inode = file->f_dentry->d_inode))
+		goto out_putf;
 
 	socket = &inode->u.socket_i;
 	local.sin_family = AF_INET;
@@ -752,7 +789,7 @@
 	} while (ret && try_port > (1024 / 2));
 
 	if (ret)
-		return 0;
+		goto out_putf;
 
 	server.sin_family = AF_INET;
 	server.sin_addr = addr->sin_addr;
@@ -761,9 +798,13 @@
 	/* Call sys_connect */
 	ret = socket->ops->connect (socket, (struct sockaddr *) &server,
 				    sizeof (server), file->f_flags);
-	if (ret < 0)
-		return 0;
-	return 1;
+	if (ret >= 0)
+		result = 1;
+
+out_putf:
+	fput(file);
+out:
+	return result;
 }
 
 static int get_default (int value, int def_value)
@@ -1139,10 +1180,13 @@
    file descriptors that have been set non-blocking 
    using 4.2BSD style calls. (tridge) */
 
-static inline int check_nonblock(int ret,int fd)
+static inline int check_nonblock(int ret, int fd)
 {
-	if (ret == -EAGAIN && (current->files->fd[fd]->f_flags & O_NDELAY))
-		return -SUNOS_EWOULDBLOCK;
+	if (ret == -EAGAIN) {
+		struct file * file = fcheck(fd);
+		if (file && (file->f_flags & O_NDELAY))
+			ret = -SUNOS_EWOULDBLOCK;
+	}
 	return ret;
 }
 
@@ -1215,12 +1259,41 @@
 	return ret;
 }
 
+extern asmlinkage int sys_setsockopt(int fd, int level, int optname,
+				     char *optval, int optlen);
+
+asmlinkage int sunos_socket(int family, int type, int protocol)
+{
+	int ret, one = 1;
+
+	lock_kernel();
+	ret = sys_socket(family, type, protocol);
+	if (ret < 0)
+		goto out;
+
+	sys_setsockopt(ret, SOL_SOCKET, SO_BSDCOMPAT,
+		       (char *)&one, sizeof(one));
+out:
+	unlock_kernel();
+	return ret;
+}
+
 asmlinkage int sunos_accept(int fd, struct sockaddr *sa, int *addrlen)
 {
-	int ret;
+	int ret, one = 1;
 
 	lock_kernel();
-	ret = check_nonblock(sys_accept(fd,sa,addrlen),fd);	
+	while (1) {
+		ret = check_nonblock(sys_accept(fd,sa,addrlen),fd);	
+		if (ret != -ENETUNREACH && ret != -EHOSTUNREACH)
+			break;
+	}
+	if (ret < 0)
+		goto out;
+
+	sys_setsockopt(ret, SOL_SOCKET, SO_BSDCOMPAT,
+		       (char *)&one, sizeof(one));
+out:
 	unlock_kernel();
 	return ret;
 }
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/kernel/systbls.S linux/arch/sparc/kernel/systbls.S
--- v2.1.96/linux/arch/sparc/kernel/systbls.S	Mon Jan 12 15:15:43 1998
+++ linux/arch/sparc/kernel/systbls.S	Tue Apr 14 17:44:19 1998
@@ -1,4 +1,4 @@
-/* $Id: systbls.S,v 1.68 1997/12/24 17:26:38 ecd Exp $
+/* $Id: systbls.S,v 1.71 1998/03/24 06:25:06 ecd Exp $
  * systbls.S: System call entry point tables for OS compatibility.
  *            The native Linux system call table lives here also.
  *
@@ -23,9 +23,9 @@
 /*5*/	.long C_LABEL(sys_open), C_LABEL(sys_close), C_LABEL(sys_wait4)
 	.long C_LABEL(sys_creat), C_LABEL(sys_link)
 /*10*/  .long C_LABEL(sys_unlink), C_LABEL(sunos_execv), C_LABEL(sys_chdir)
-	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_mknod)
-/*15*/	.long C_LABEL(sys_chmod), C_LABEL(sys_chown), C_LABEL(sparc_brk)
-	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_lseek)
+	.long C_LABEL(sys_xstat), C_LABEL(sys_mknod)
+/*15*/	.long C_LABEL(sys_chmod), C_LABEL(sys_lchown), C_LABEL(sparc_brk)
+	.long C_LABEL(sys_xmknod), C_LABEL(sys_lseek)
 /*20*/	.long C_LABEL(sys_getpid), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
 	.long C_LABEL(sys_setuid), C_LABEL(sys_getuid)
 /*25*/	.long C_LABEL(sys_time), C_LABEL(sys_ptrace), C_LABEL(sys_alarm)
@@ -137,7 +137,7 @@
 	.long C_LABEL(sys_close), C_LABEL(sunos_wait4), C_LABEL(sys_creat)
 	.long C_LABEL(sys_link), C_LABEL(sys_unlink), C_LABEL(sunos_execv)
 	.long C_LABEL(sys_chdir), C_LABEL(sunos_nosys), C_LABEL(sys_mknod)
-	.long C_LABEL(sys_chmod), C_LABEL(sys_chown), C_LABEL(sunos_brk)
+	.long C_LABEL(sys_chmod), C_LABEL(sys_lchown), C_LABEL(sunos_brk)
 	.long C_LABEL(sunos_nosys), C_LABEL(sys_lseek), C_LABEL(sunos_getpid)
 	.long C_LABEL(sunos_nosys), C_LABEL(sunos_nosys), C_LABEL(sunos_nosys)
 	.long C_LABEL(sunos_getuid), C_LABEL(sunos_nosys), C_LABEL(sys_ptrace)
@@ -164,7 +164,7 @@
 	.long C_LABEL(sys_getitimer), C_LABEL(sys_gethostname), C_LABEL(sys_sethostname)
 	.long C_LABEL(sunos_getdtablesize), C_LABEL(sys_dup2), C_LABEL(sunos_nop)
 	.long C_LABEL(sys_fcntl), C_LABEL(sunos_select), C_LABEL(sunos_nop)
-	.long C_LABEL(sys_fsync), C_LABEL(sys_setpriority), C_LABEL(sys_socket)
+	.long C_LABEL(sys_fsync), C_LABEL(sys_setpriority), C_LABEL(sunos_socket)
 	.long C_LABEL(sys_connect), C_LABEL(sunos_accept)
 /*100*/	.long C_LABEL(sys_getpriority), C_LABEL(sunos_send), C_LABEL(sunos_recv)
 	.long C_LABEL(sunos_nosys), C_LABEL(sys_bind), C_LABEL(sunos_setsockopt)
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/kernel/tadpole.c linux/arch/sparc/kernel/tadpole.c
--- v2.1.96/linux/arch/sparc/kernel/tadpole.c	Tue May 13 22:41:03 1997
+++ linux/arch/sparc/kernel/tadpole.c	Tue Apr 14 17:44:19 1998
@@ -6,6 +6,7 @@
 #include <linux/string.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
+#include <linux/init.h>
 
 #include <asm/asi.h>
 #include <asm/oplib.h>
@@ -94,7 +95,7 @@
 	clk_ctrl[0] = 0;
 }
 
-void clock_stop_probe(void)
+__initfunc(void clock_stop_probe(void))
 {
 	unsigned int node, clk_nd;
 	char name[20];
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/kernel/time.c linux/arch/sparc/kernel/time.c
--- v2.1.96/linux/arch/sparc/kernel/time.c	Wed Apr 23 19:01:16 1997
+++ linux/arch/sparc/kernel/time.c	Tue Apr 14 17:44:19 1998
@@ -1,4 +1,4 @@
-/* $Id: time.c,v 1.29 1997/04/18 09:48:44 davem Exp $
+/* $Id: time.c,v 1.32 1998/03/23 08:41:13 jj Exp $
  * linux/arch/sparc/kernel/time.c
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -24,6 +24,10 @@
 #include <asm/system.h>
 #include <asm/irq.h>
 #include <asm/io.h>
+#include <asm/idprom.h>
+#include <asm/machines.h>
+#include <asm/sun4paddr.h>
+#include <asm/page.h>
 
 enum sparc_clock_type sp_clock_typ;
 struct mostek48t02 *mstk48t02_regs = 0;
@@ -88,7 +92,7 @@
 }
 
 /* Kick start a stopped clock (procedure from the Sun NVRAM/hostid FAQ). */
-static void kick_start_clock(void)
+__initfunc(static void kick_start_clock(void))
 {
 	register struct mostek48t02 *regs = mstk48t02_regs;
 	unsigned char sec;
@@ -137,7 +141,7 @@
 }
 
 /* Return nonzero if the clock chip battery is low. */
-static int has_low_battery(void)
+static __inline__ int has_low_battery(void)
 {
 	register struct mostek48t02 *regs = mstk48t02_regs;
 	unsigned char data1, data2;
@@ -150,8 +154,24 @@
 	return (data1 == data2);	/* Was the write blocked? */
 }
 
-/* Probe for the real time clock chip. */
-__initfunc(static void clock_probe(void))
+/* Probe for the real time clock chip on Sun4/300. */
+static __inline__ void sun4_clock_probe(void)
+{
+	sp_clock_typ = MSTK48T02;
+	mstk48t02_regs = (struct mostek48t02 *) 
+		sparc_alloc_io(SUN4_300_MOSTEK_PHYSADDR, 0,
+			       sizeof(*mstk48t02_regs),
+			       "clock", 0x0, 0x0);
+	mstk48t08_regs = 0;  /* To catch weirdness */
+	/* Kick start the clock if it is completely stopped. */
+	if (mstk48t02_regs->sec & MSTK_STOP) {
+		kick_start_clock();
+	}
+
+}
+
+/* Probe for the mostek real time clock chip. */
+static __inline__ void clock_probe(void)
 {
 	struct linux_prom_registers clk_reg[2];
 	char model[128];
@@ -247,7 +267,11 @@
         return;
 #endif
 
-	clock_probe();
+	if (ARCH_SUN4)
+		sun4_clock_probe();
+	else
+		clock_probe();
+
 	init_timers(timer_interrupt);
 
 	mregs = mstk48t02_regs;
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/kernel/trampoline.S linux/arch/sparc/kernel/trampoline.S
--- v2.1.96/linux/arch/sparc/kernel/trampoline.S	Tue May 13 22:41:03 1997
+++ linux/arch/sparc/kernel/trampoline.S	Tue Apr 14 17:44:19 1998
@@ -1,7 +1,8 @@
-/* $Id: trampoline.S,v 1.9 1997/05/01 08:53:34 davem Exp $
+/* $Id: trampoline.S,v 1.12 1998/03/19 15:36:38 jj Exp $
  * trampoline.S: SMP cpu boot-up trampoline code.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
  */
 
 #include <asm/cprefix.h>
@@ -12,9 +13,12 @@
 #include <asm/ptrace.h>
 #include <asm/vaddrs.h>
 #include <asm/contregs.h>
+#include <asm/init.h>
 
+	.globl C_LABEL(sun4m_cpu_startup), C_LABEL(__smp4m_processor_id)
+	.globl C_LABEL(sun4d_cpu_startup), C_LABEL(__smp4d_processor_id)
 
-	.text
+	__INIT
 	.align 4
 
 /* When we start up a cpu for the first time it enters this routine.
@@ -22,8 +26,7 @@
  * in and sets PIL in %psr to 15, no irqs.
  */
 
-	.globl C_LABEL(sparc_cpu_startup)
-C_LABEL(sparc_cpu_startup):
+C_LABEL(sun4m_cpu_startup):
 cpu1_startup:
 	sethi	%hi(C_LABEL(trapbase_cpu1)), %g3
 	b	1f
@@ -60,9 +63,8 @@
 	and	%g4, 0xc, %g4
 	ld	[%g5 + %g4], %g6
 
-	mov	1, %sp
-	sll	%sp, (PAGE_SHIFT + 1), %sp
-	sub	%sp, REGWIN_SZ, %sp
+	sethi	%hi(TASK_UNION_SIZE - REGWIN_SZ), %sp
+	or	%sp, %lo(TASK_UNION_SIZE - REGWIN_SZ), %sp
 	add	%g6, %sp, %sp
 
 	/* Turn on traps (PSR_ET). */
@@ -77,11 +79,84 @@
 	 nop
 
 	/* Start this processor. */
-	call	C_LABEL(smp_callin)
+	call	C_LABEL(smp4m_callin)
 	 nop
 
+	b,a	smp_do_cpu_idle
+
+	.text
+	.align	4
+
+smp_do_cpu_idle:
 	call	C_LABEL(cpu_idle)
 	 mov	0, %o0
 
 	call	C_LABEL(cpu_panic)
 	 nop
+
+C_LABEL(__smp4m_processor_id):
+	rd	%tbr, %g2
+	srl	%g2, 12, %g2
+	and	%g2, 3, %g2
+	retl
+	 mov	%g1, %o7
+
+C_LABEL(__smp4d_processor_id):
+	lda	[%g0] ASI_M_VIKING_TMP1, %g2
+	retl
+	 mov	%g1, %o7
+
+/* CPUID in bootbus can be found at PA 0xff0140000 */
+#define SUN4D_BOOTBUS_CPUID	0xf0140000
+
+	__INIT
+	.align	4
+
+C_LABEL(sun4d_cpu_startup):
+	/* Set up a sane %psr -- PIL<0xf> S<0x1> PS<0x1> CWP<0x0> */
+	set	(PSR_PIL | PSR_S | PSR_PS), %g1
+	wr	%g1, 0x0, %psr		! traps off though
+	WRITE_PAUSE
+
+	/* Our %wim is one behind CWP */
+	mov	2, %g1
+	wr	%g1, 0x0, %wim
+	WRITE_PAUSE
+
+	/* Set tbr - we use just one trap table. */
+	set	C_LABEL(trapbase), %g1
+	wr	%g1, 0x0, %tbr
+	WRITE_PAUSE
+
+	/* Get our CPU id out of bootbus */
+	set	SUN4D_BOOTBUS_CPUID, %g3
+	lduba	[%g3] ASI_M_CTL, %g3
+	and	%g3, 0xf8, %g3
+	srl	%g3, 3, %g1
+	sta	%g1, [%g0] ASI_M_VIKING_TMP1
+
+	/* Give ourselves a stack and curptr. */
+	set	C_LABEL(current_set), %g5
+	srl	%g3, 1, %g4
+	ld	[%g5 + %g4], %g6
+
+	sethi	%hi(TASK_UNION_SIZE - REGWIN_SZ), %sp
+	or	%sp, %lo(TASK_UNION_SIZE - REGWIN_SZ), %sp
+	add	%g6, %sp, %sp
+
+	/* Turn on traps (PSR_ET). */
+	rd	%psr, %g1
+	wr	%g1, PSR_ET, %psr	! traps on
+	WRITE_PAUSE
+
+	/* Init our caches, etc. */
+	set	C_LABEL(poke_srmmu), %g5
+	ld	[%g5], %g5
+	call	%g5
+	 nop
+
+	/* Start this processor. */
+	call	C_LABEL(smp4d_callin)
+	 nop
+
+	b,a	smp_do_cpu_idle
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/kernel/traps.c linux/arch/sparc/kernel/traps.c
--- v2.1.96/linux/arch/sparc/kernel/traps.c	Sun Jan 26 02:07:08 1997
+++ linux/arch/sparc/kernel/traps.c	Tue Apr 14 17:44:19 1998
@@ -1,4 +1,4 @@
-/* $Id: traps.c,v 1.53 1997/01/25 02:43:05 miguel Exp $
+/* $Id: traps.c,v 1.56 1998/04/06 16:08:32 jj Exp $
  * arch/sparc/kernel/traps.c
  *
  * Copyright 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -62,6 +62,14 @@
 	prom_halt();
 }
 
+void sun4d_nmi(struct pt_regs *regs)
+{
+	printk("Aieee: sun4d NMI received!\n");
+	printk("you lose buddy boy...\n");
+	show_regs(regs);
+	prom_halt();
+}
+
 void instruction_dump (unsigned long *pc)
 {
 	int i;
@@ -229,10 +237,13 @@
 static unsigned long fake_queue[32] __attribute__ ((aligned (8)));
 static unsigned long fake_depth;
 
+extern int do_mathemu(struct pt_regs *, struct task_struct *);
+
 void do_fpe_trap(struct pt_regs *regs, unsigned long pc, unsigned long npc,
 		 unsigned long psr)
 {
 	static calls = 0;
+	int ret;
 #ifndef __SMP__
 	struct task_struct *fpt = last_task_used_math;
 #else
@@ -255,6 +266,40 @@
 	}
 	fpsave(&fpt->tss.float_regs[0], &fpt->tss.fsr,
 	       &fpt->tss.fpqueue[0], &fpt->tss.fpqdepth);
+#ifdef DEBUG_FPU
+	printk("Hmm, FP exception, fsr was %016lx\n", fpt->tss.fsr);
+#endif
+
+	switch ((fpt->tss.fsr & 0x1c000)) {
+	/* switch on the contents of the ftt [floating point trap type] field */
+#ifdef DEBUG_FPU
+	case (1 << 14):
+		printk("IEEE_754_exception\n");
+		break;
+#endif
+	case (2 << 14):  /* unfinished_FPop (underflow & co) */
+	case (3 << 14):  /* unimplemented_FPop (quad stuff, maybe sqrt) */
+		ret = do_mathemu(regs, fpt);
+		break;
+#ifdef DEBUG_FPU
+	case (4 << 14):
+		printk("sequence_error (OS bug...)\n");
+		break;
+	case (5 << 14):
+		printk("hardware_error (uhoh!)\n");
+		break;
+	case (6 << 14):
+		printk("invalid_fp_register (user error)\n");
+		break;
+#endif /* DEBUG_FPU */
+	}
+	/* If we successfully emulated the FPop, we pretend the trap never happened :-> */
+	if (ret) {
+		fpload(&current->tss.float_regs[0], &current->tss.fsr);
+		return;
+	}
+	/* nope, better SIGFPE the offending process... */
+	       
 	fpt->tss.sig_address = pc;
 	fpt->tss.sig_desc = SUBSIG_FPERROR; /* as good as any */
 #ifdef __SMP__
@@ -327,19 +372,6 @@
 			unsigned long psr)
 {
 	lock_kernel();
-	send_sig(SIGILL, current, 1);
-	unlock_kernel();
-}
-
-void handle_bad_flush(struct pt_regs *regs, unsigned long pc, unsigned long npc,
-		      unsigned long psr)
-{
-	lock_kernel();
-#ifdef TRAP_DEBUG
-	printk("Unimplemented FLUSH Exception at PC %08lx NPC %08lx PSR %08lx\n",
-	       pc, npc, psr);
-#endif
-	printk("INSTRUCTION=%08lx\n", *((unsigned long *) regs->pc));
 	send_sig(SIGILL, current, 1);
 	unlock_kernel();
 }
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/kernel/wof.S linux/arch/sparc/kernel/wof.S
--- v2.1.96/linux/arch/sparc/kernel/wof.S	Tue May 13 22:41:03 1997
+++ linux/arch/sparc/kernel/wof.S	Tue Apr 14 17:44:19 1998
@@ -1,4 +1,4 @@
-/* $Id: wof.S,v 1.36 1997/05/01 08:53:35 davem Exp $
+/* $Id: wof.S,v 1.38 1998/02/06 14:14:22 jj Exp $
  * wof.S: Sparc window overflow handler.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -234,9 +234,10 @@
 spnwin_patch3:	and	%twin_tmp, 0xff, %twin_tmp	! patched on 7win Sparcs
 		st	%twin_tmp, [%curptr + AOFF_task_tss + AOFF_thread_uwinmask]
 
-	mov	1, %sp
-	sll	%sp, (PAGE_SHIFT + 1), %sp
-	sub	%sp, (TRACEREG_SZ + REGWIN_SZ), %sp
+#define STACK_OFFSET	(TASK_UNION_SIZE - TRACEREG_SZ - REGWIN_SZ)
+
+	sethi	%hi(STACK_OFFSET), %sp
+	or	%sp, %lo(STACK_OFFSET), %sp
 	add	%curptr, %sp, %sp
 
 	/* Restore the saved globals and build a pt_regs frame. */
@@ -244,9 +245,8 @@
 	mov	%saved_g6, %g6
 	STORE_PT_ALL(sp, t_psr, t_pc, t_npc, g1)
 
-	mov	1, %g6
-	sll	%g6, (PAGE_SHIFT + 1), %g6
-	sub	%g6, (TRACEREG_SZ + REGWIN_SZ), %g6
+	sethi	%hi(STACK_OFFSET), %g6
+	or	%g6, %lo(STACK_OFFSET), %g6
 	sub	%sp, %g6, %g6
 
 	/* Turn on traps and call c-code to deal with it. */
@@ -394,9 +394,8 @@
 	 * kernel is page aligned, which should always be the case.
 	 */
 	/* Check results of callers andcc %sp, 0x7, %g0 */
-	sethi	%hi(C_LABEL(page_offset)), %glob_tmp
 	bne	spwin_user_stack_is_bolixed
-	 ld	[%glob_tmp + %lo(C_LABEL(page_offset))], %glob_tmp
+	 GET_PAGE_OFFSET(glob_tmp)
 	cmp	%glob_tmp, %sp
 	bleu	spwin_user_stack_is_bolixed
 	 mov	AC_M_SFSR, %glob_tmp
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/kernel/wuf.S linux/arch/sparc/kernel/wuf.S
--- v2.1.96/linux/arch/sparc/kernel/wuf.S	Tue May 13 22:41:03 1997
+++ linux/arch/sparc/kernel/wuf.S	Tue Apr 14 17:44:19 1998
@@ -1,4 +1,4 @@
-/* $Id: wuf.S,v 1.34 1997/05/01 08:53:36 davem Exp $
+/* $Id: wuf.S,v 1.37 1998/02/19 21:25:50 ecd Exp $
  * wuf.S: Window underflow trap handler for the Sparc.
  *
  * Copyright (C) 1995 David S. Miller
@@ -138,6 +138,8 @@
 C_LABEL(fwin_mmu_patchme):	b	C_LABEL(sun4c_fwin_stackchk)
 				 andcc	%sp, 0x7, %g0
 
+#define STACK_OFFSET (TASK_UNION_SIZE - TRACEREG_SZ - REGWIN_SZ)
+
 fwin_user_stack_is_bolixed:
 	/* LOCATION: Window 'W' */
 
@@ -146,9 +148,8 @@
 	 */
 	LOAD_CURRENT(l4, l5)
 
-	mov	1, %l5
-	sll	%l5, (PAGE_SHIFT + 1), %l5
-	sub	%l5, (TRACEREG_SZ + REGWIN_SZ), %l5
+	sethi	%hi(STACK_OFFSET), %l5
+	or	%l5, %lo(STACK_OFFSET), %l5
 	add	%l4, %l5, %l5
 
 	/* Store globals into pt_regs frame. */
@@ -169,10 +170,9 @@
 
 	/* LOCATION: Window 'T' */
 
-	mov	1, %sp
-	sll	%sp, (PAGE_SHIFT + 1), %sp
-	sub	%sp, (TRACEREG_SZ + REGWIN_SZ), %sp
-	add	%curptr, %sp, %sp
+	sethi	%hi(STACK_OFFSET), %l5
+	or	%l5, %lo(STACK_OFFSET), %l5
+	add	%curptr, %l5, %sp
 
 	/* Build rest of pt_regs. */
 	STORE_PT_INS(sp)
@@ -299,9 +299,8 @@
 	/* LOCATION: Window 'W' */
 
 	/* Caller did 'andcc %sp, 0x7, %g0' */
-	sethi	%hi(C_LABEL(page_offset)), %l5
 	bne	fwin_user_stack_is_bolixed
-	 ld	[%l5 + %lo(C_LABEL(page_offset))], %l5
+	 GET_PAGE_OFFSET(l5)
 
 	/* Check if the users stack is in kernel vma, then our
 	 * trial and error technique below would succeed for
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/lib/Makefile linux/arch/sparc/lib/Makefile
--- v2.1.96/linux/arch/sparc/lib/Makefile	Thu May 15 16:48:01 1997
+++ linux/arch/sparc/lib/Makefile	Tue Apr 14 17:44:19 1998
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.24 1997/05/08 17:45:26 davem Exp $
+# $Id: Makefile,v 1.25 1998/01/30 10:58:43 jj Exp $
 # Makefile for Sparc library files..
 #
 
@@ -16,19 +16,19 @@
 	sync
 
 checksum.o: checksum.S
-	$(CC) -ansi -c -o checksum.o checksum.S
+	$(CC) -D__ASSEMBLY__ -ansi -c -o checksum.o checksum.S
 
 memcpy.o: memcpy.S
 	$(CC) -D__ASSEMBLY__ -ansi -c -o memcpy.o memcpy.S
 
 memcmp.o: memcmp.S
-	$(CC) -ansi -c -o memcmp.o memcmp.S
+	$(CC) -D__ASSEMBLY__ -ansi -c -o memcmp.o memcmp.S
 
 memscan.o: memscan.S
-	$(CC) -ansi -c -o memscan.o memscan.S
+	$(CC) -D__ASSEMBLY__ -ansi -c -o memscan.o memscan.S
 
 strncmp.o: strncmp.S
-	$(CC) -ansi -c -o strncmp.o strncmp.S
+	$(CC) -D__ASSEMBLY__ -ansi -c -o strncmp.o strncmp.S
 
 strncpy_from_user.o: strncpy_from_user.S
 	$(CC) -D__ASSEMBLY__ -ansi -c -o strncpy_from_user.o strncpy_from_user.S
@@ -40,7 +40,7 @@
 	$(CC) -D__ASSEMBLY__ -ansi -c -o copy_user.o copy_user.S
 
 blockops.o: blockops.S
-	$(CC) -ansi -c -o blockops.o blockops.S
+	$(CC) -D__ASSEMBLY__ -ansi -c -o blockops.o blockops.S
 
 memset.o: memset.S
 	$(CC) -D__ASSEMBLY__ -ansi -c -o memset.o memset.S
@@ -73,34 +73,34 @@
 endif
 
 strlen.o: strlen.S
-	$(CC) -ansi -c -o strlen.o strlen.S
+	$(CC) -D__ASSEMBLY__ -ansi -c -o strlen.o strlen.S
 
 divdi3.o: divdi3.S
-	$(CC) -ansi -c -o divdi3.o divdi3.S
+	$(CC) -D__ASSEMBLY__ -ansi -c -o divdi3.o divdi3.S
 
 udivdi3.o: udivdi3.S
-	$(CC) -ansi -c -o udivdi3.o udivdi3.S
+	$(CC) -D__ASSEMBLY__ -ansi -c -o udivdi3.o udivdi3.S
 
 mul.o: mul.S
-	$(CC) -c -o mul.o mul.S
+	$(CC) -D__ASSEMBLY__ -c -o mul.o mul.S
 
 rem.o: rem.S
-	$(CC) -DST_DIV0=0x2 -c -o rem.o rem.S
+	$(CC) -D__ASSEMBLY__ -DST_DIV0=0x2 -c -o rem.o rem.S
 
 sdiv.o: sdiv.S
-	$(CC) -DST_DIV0=0x2 -c -o sdiv.o sdiv.S
+	$(CC) -D__ASSEMBLY__ -DST_DIV0=0x2 -c -o sdiv.o sdiv.S
 
 udiv.o: udiv.S
-	$(CC) -DST_DIV0=0x2 -c -o udiv.o udiv.S
+	$(CC) -D__ASSEMBLY__ -DST_DIV0=0x2 -c -o udiv.o udiv.S
 
 umul.o: umul.S
-	$(CC) -c -o umul.o umul.S
+	$(CC) -D__ASSEMBLY__ -c -o umul.o umul.S
 
 urem.o: urem.S
-	$(CC) -DST_DIV0=0x2 -c -o urem.o urem.S
+	$(CC) -D__ASSEMBLY__ -DST_DIV0=0x2 -c -o urem.o urem.S
 
 ashrdi3.o: ashrdi3.S
-	$(CC) -c -o ashrdi3.o ashrdi3.S
+	$(CC) -D__ASSEMBLY__ -c -o ashrdi3.o ashrdi3.S
 
 dep:
 
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/lib/atomic.S linux/arch/sparc/lib/atomic.S
--- v2.1.96/linux/arch/sparc/lib/atomic.S	Mon Apr 14 16:28:08 1997
+++ linux/arch/sparc/lib/atomic.S	Tue Apr 14 17:44:19 1998
@@ -10,8 +10,9 @@
 	.text
 	.align	4
 
-	.globl	___xchg32
-___xchg32:
+#ifndef __SMP__
+	.globl	___xchg32_sun4c
+___xchg32_sun4c:
 	rd	%psr, %g3
 	andcc	%g3, PSR_PIL, %g0
 	bne	1f
@@ -27,8 +28,15 @@
 	nop; nop; nop
 1:
 	mov	%g7, %g2
-	jmpl	%o7, %g0		/* Note, not + 0x8, see call in system.h */
+	jmpl	%o7 + 8, %g0
 	 mov	%g4, %o7
+
+	.globl	___xchg32_sun4md
+___xchg32_sun4md:
+	swap	[%g1], %g2
+	jmpl	%o7 + 8, %g0
+	 mov	%g4, %o7
+#endif
 
 	/* Read asm-sparc/atomic.h carefully to understand how this works for SMP.
 	 * Really, some things here for SMP are overly clever, go read the header.
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/lib/blockops.S linux/arch/sparc/lib/blockops.S
--- v2.1.96/linux/arch/sparc/lib/blockops.S	Thu May 29 21:53:04 1997
+++ linux/arch/sparc/lib/blockops.S	Tue Apr 14 17:44:19 1998
@@ -1,10 +1,11 @@
-/* $Id: blockops.S,v 1.7 1997/05/20 07:58:28 jj Exp $
+/* $Id: blockops.S,v 1.8 1998/01/30 10:58:44 jj Exp $
  * blockops.S: Common block zero optimized routines.
  *
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
  */
 
 #include <asm/cprefix.h>
+#include <asm/page.h>
 
 	/* Zero out 64 bytes of memory at (buf + offset).
 	 * Assumes %g1 contains zero.
@@ -53,7 +54,7 @@
 	/* %o0 = buf */
 	or	%g0, %g0, %g1
 	or	%o0, %g0, %o1
-	or	%g0, 0x10, %g2
+	or	%g0, (PAGE_SIZE >> 8), %g2
 1:
 	BLAST_BLOCK(%o0, 0x00)
 	BLAST_BLOCK(%o0, 0x40)
@@ -70,7 +71,7 @@
 /* NOTE: If you change the number of insns of this routine, please check
  * arch/sparc/mm/hypersparc.S */
 	/* %o0 = dst, %o1 = src */
-	or	%g0, 0x10, %g1
+	or	%g0, (PAGE_SIZE >> 8), %g1
 1:
 	MIRROR_BLOCK(%o0, %o1, 0x00, %o2, %o3, %o4, %o5, %g2, %g3, %g4, %g5)
 	MIRROR_BLOCK(%o0, %o1, 0x20, %o2, %o3, %o4, %o5, %g2, %g3, %g4, %g5)
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/math-emu/Makefile linux/arch/sparc/math-emu/Makefile
--- v2.1.96/linux/arch/sparc/math-emu/Makefile	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc/math-emu/Makefile	Tue Apr 14 17:44:19 1998
@@ -0,0 +1,37 @@
+#
+# Makefile for the FPU instruction emulation.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definition is now in the main makefile...
+
+O_TARGET := math-emu.o
+O_OBJS   := math.o ashldi3.o fabss.o faddd.o faddq.o fadds.o 		\
+		fcmpd.o fcmped.o fcmpeq.o fcmpes.o fcmpq.o fcmps.o	\
+		fdivd.o fdivq.o fdivs.o fdmulq.o fdtoi.o fdtoq.o	\
+		fdtos.o fitoq.o fmovs.o fmuld.o fmulq.o fmuls.o		\
+		fnegs.o fqtod.o fqtoi.o fqtos.o fsmuld.o fsqrtd.o	\
+		fsqrtq.o fsqrts.o fstod.o fstoi.o fstoq.o fsubd.o 	\
+		fsubq.o	fsubs.o udivmodti4.o
+
+LINKS	 := double.h faddd.c faddq.c fadds.c fdivd.c fdivq.c fdivs.c	\
+		fdtoi.c fitoq.c fmuld.c fmulq.c fmuls.c fqtoi.c		\
+		fsqrtd.c fsqrtq.c fsqrts.c fstoi.c fsubd.c 		\
+		fsubq.c	fsubs.c op-1.h op-2.h op-4.h op-common.h quad.h	\
+		single.h soft-fp.h udivmodti4.c
+
+.S.s:
+	$(CPP) -D__ASSEMBLY__ -ansi $< -o $*.s
+
+.S.o:
+	$(CC) -D__ASSEMBLY__ -ansi -c $< -o $*.o
+
+include $(TOPDIR)/Rules.make
+
+symlinks:
+	ln -sf $(patsubst %,../../sparc64/math-emu/%,$(LINKS)) .
+
+cleansymlinks:
+	rm -f $(LINKS)
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/math-emu/ashldi3.S linux/arch/sparc/math-emu/ashldi3.S
--- v2.1.96/linux/arch/sparc/math-emu/ashldi3.S	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc/math-emu/ashldi3.S	Tue Apr 14 17:44:19 1998
@@ -0,0 +1,36 @@
+/* $Id: ashldi3.S,v 1.1 1998/04/06 16:09:28 jj Exp $
+ * ashldi3.S:	Math-emu code creates all kinds of references to
+ *              this little routine on the sparc with gcc.
+ *
+ * Copyright (C) 1998 Jakub Jelinek(jj@ultra.linux.cz)
+ */
+
+#include <asm/cprefix.h>
+
+	.globl C_LABEL(__ashldi3)
+C_LABEL(__ashldi3):
+	tst	%o2
+	be	3f
+	 mov	32, %g2
+
+	sub	%g2, %o2, %g2
+
+	tst	%g2
+	bg	1f
+	 srl	%o1, %g2, %g3
+
+	clr	%o5
+	neg	%g2
+	ba	2f
+	 sll	%o1, %g2, %o4
+
+1:
+	sll	%o1, %o2, %o5
+	srl	%o0, %o2, %g2
+	or	%g2, %g3, %o4
+2:
+	mov	%o4, %o0
+	mov	%o5, %o1
+3:
+	jmpl	%o7 + 8, %g0
+	 nop
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/math-emu/fabss.c linux/arch/sparc/math-emu/fabss.c
--- v2.1.96/linux/arch/sparc/math-emu/fabss.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc/math-emu/fabss.c	Tue Apr 14 17:44:19 1998
@@ -0,0 +1,6 @@
+int FABSS(unsigned long *rd, unsigned long *rs2)
+{
+	/* Clear the sign bit (high bit of word 0) */
+	rd[0] = rs2[0] & 0x7fffffffUL;
+	return 1;
+}
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/math-emu/fcmpd.c linux/arch/sparc/math-emu/fcmpd.c
--- v2.1.96/linux/arch/sparc/math-emu/fcmpd.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc/math-emu/fcmpd.c	Tue Apr 14 17:44:19 1998
@@ -0,0 +1,18 @@
+#include "soft-fp.h"
+#include "double.h"
+
+int FCMPD(void *rd, void *rs2, void *rs1)
+{
+	FP_DECL_D(A); FP_DECL_D(B);
+	long ret;
+	unsigned long *fsr = rd;
+	
+	__FP_UNPACK_D(A, rs1);
+	__FP_UNPACK_D(B, rs2);
+	FP_CMP_D(ret, B, A, 2);
+	if (ret == -1)
+		ret = 2;
+
+	*fsr = (*fsr & ~0xc00) | (ret << 10); 
+	return 1;
+}
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/math-emu/fcmped.c linux/arch/sparc/math-emu/fcmped.c
--- v2.1.96/linux/arch/sparc/math-emu/fcmped.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc/math-emu/fcmped.c	Tue Apr 14 17:44:19 1998
@@ -0,0 +1,18 @@
+#include "soft-fp.h"
+#include "double.h"
+
+int FCMPED(void *rd, void *rs2, void *rs1)
+{
+	FP_DECL_D(A); FP_DECL_D(B);
+	long ret;
+	unsigned long *fsr = rd;
+	
+	__FP_UNPACK_D(A, rs1);
+	__FP_UNPACK_D(B, rs2);
+	FP_CMP_D(ret, B, A, 2);
+	if (ret == -1)
+		ret = 2;
+
+	*fsr = (*fsr & ~0xc00) | (ret << 10); 
+	return 1;
+}
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/math-emu/fcmpeq.c linux/arch/sparc/math-emu/fcmpeq.c
--- v2.1.96/linux/arch/sparc/math-emu/fcmpeq.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc/math-emu/fcmpeq.c	Tue Apr 14 17:44:19 1998
@@ -0,0 +1,18 @@
+#include "soft-fp.h"
+#include "quad.h"
+
+int FCMPEQ(void *rd, void *rs2, void *rs1)
+{
+	FP_DECL_Q(A); FP_DECL_Q(B);
+	long ret;
+	unsigned long fsr;
+	
+	__FP_UNPACK_Q(A, rs1);
+	__FP_UNPACK_Q(B, rs2);
+	FP_CMP_Q(ret, B, A, 3);
+	if (ret == -1) ret = 2;
+	fsr = *(unsigned long *)rd;
+	fsr &= ~0xc00; fsr |= (ret << 10);
+	*(unsigned long *)rd = fsr;
+	return 1;
+}
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/math-emu/fcmpes.c linux/arch/sparc/math-emu/fcmpes.c
--- v2.1.96/linux/arch/sparc/math-emu/fcmpes.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc/math-emu/fcmpes.c	Tue Apr 14 17:44:19 1998
@@ -0,0 +1,18 @@
+#include "soft-fp.h"
+#include "single.h"
+
+int FCMPES(void *rd, void *rs2, void *rs1)
+{
+	FP_DECL_S(A); FP_DECL_S(B);
+	long ret;
+	unsigned long *fsr = rd;
+	
+	__FP_UNPACK_S(A, rs1);
+	__FP_UNPACK_S(B, rs2);
+	FP_CMP_S(ret, B, A, 1);
+	if (ret == -1)
+		ret = 2;
+
+	*fsr = (*fsr & ~0xc00) | (ret << 10); 
+	return 1;
+}
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/math-emu/fcmpq.c linux/arch/sparc/math-emu/fcmpq.c
--- v2.1.96/linux/arch/sparc/math-emu/fcmpq.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc/math-emu/fcmpq.c	Tue Apr 14 17:44:19 1998
@@ -0,0 +1,18 @@
+#include "soft-fp.h"
+#include "quad.h"
+
+int FCMPQ(void *rd, void *rs2, void *rs1)
+{
+	FP_DECL_Q(A); FP_DECL_Q(B);
+	long ret;
+	unsigned long fsr;
+	
+	__FP_UNPACK_Q(A, rs1);
+	__FP_UNPACK_Q(B, rs2);
+	FP_CMP_Q(ret, B, A, 3);
+	if (ret == -1) ret = 2;
+	fsr = *(unsigned long *)rd;
+	fsr &= ~0xc00; fsr |= (ret << 10);
+	*(unsigned long *)rd = fsr;
+	return 1;
+}
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/math-emu/fcmps.c linux/arch/sparc/math-emu/fcmps.c
--- v2.1.96/linux/arch/sparc/math-emu/fcmps.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc/math-emu/fcmps.c	Tue Apr 14 17:44:19 1998
@@ -0,0 +1,18 @@
+#include "soft-fp.h"
+#include "single.h"
+
+int FCMPS(void *rd, void *rs2, void *rs1)
+{
+	FP_DECL_S(A); FP_DECL_S(B);
+	long ret;
+	unsigned long *fsr = rd;
+	
+	__FP_UNPACK_S(A, rs1);
+	__FP_UNPACK_S(B, rs2);
+	FP_CMP_S(ret, B, A, 1);
+	if (ret == -1)
+		ret = 2;
+
+	*fsr = (*fsr & ~0xc00) | (ret << 10); 
+	return 1;
+}
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/math-emu/fdmulq.c linux/arch/sparc/math-emu/fdmulq.c
--- v2.1.96/linux/arch/sparc/math-emu/fdmulq.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc/math-emu/fdmulq.c	Tue Apr 14 17:44:19 1998
@@ -0,0 +1,16 @@
+#include "soft-fp.h"
+#include "quad.h"
+#include "double.h"
+
+int FDMULQ(void *rd, void *rs2, void *rs1)
+{
+	FP_DECL_D(IN); FP_DECL_Q(A); FP_DECL_Q(B); FP_DECL_Q(R);
+
+	__FP_UNPACK_D(IN, rs1);
+	FP_CONV(Q,D,4,2,A,IN);
+	__FP_UNPACK_D(IN, rs2);
+	FP_CONV(Q,D,4,2,B,IN);
+	FP_MUL_Q(R, A, B);
+	__FP_PACK_Q(rd, R);
+	return 1;
+}
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/math-emu/fdtoq.c linux/arch/sparc/math-emu/fdtoq.c
--- v2.1.96/linux/arch/sparc/math-emu/fdtoq.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc/math-emu/fdtoq.c	Tue Apr 14 17:44:19 1998
@@ -0,0 +1,13 @@
+#include "soft-fp.h"
+#include "quad.h"
+#include "double.h"
+
+int FDTOQ(void *rd, void *rs2)
+{
+	FP_DECL_D(A); FP_DECL_Q(R);
+
+	__FP_UNPACK_D(A, rs2);
+	FP_CONV(Q,D,4,2,R,A);
+	__FP_PACK_Q(rd, R);
+	return 1;
+}
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/math-emu/fdtos.c linux/arch/sparc/math-emu/fdtos.c
--- v2.1.96/linux/arch/sparc/math-emu/fdtos.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc/math-emu/fdtos.c	Tue Apr 14 17:44:19 1998
@@ -0,0 +1,13 @@
+#include "soft-fp.h"
+#include "double.h"
+#include "single.h"
+
+int FDTOS(void *rd, void *rs2)
+{
+	FP_DECL_D(A); FP_DECL_S(R);
+
+	__FP_UNPACK_D(A, rs2);
+	FP_CONV(S,D,1,2,R,A);
+	__FP_PACK_S(rd, R);
+	return 1;
+}
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/math-emu/fmovs.c linux/arch/sparc/math-emu/fmovs.c
--- v2.1.96/linux/arch/sparc/math-emu/fmovs.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc/math-emu/fmovs.c	Tue Apr 14 17:44:19 1998
@@ -0,0 +1,5 @@
+int FMOVS(unsigned long *rd, unsigned long *rs2)
+{
+	rd[0] = rs2[0];
+	return 0;
+}
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/math-emu/fnegs.c linux/arch/sparc/math-emu/fnegs.c
--- v2.1.96/linux/arch/sparc/math-emu/fnegs.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc/math-emu/fnegs.c	Tue Apr 14 17:44:19 1998
@@ -0,0 +1,7 @@
+int FNEGS(unsigned long *rd, unsigned long *rs2)
+{
+ 	/* just change the sign bit */
+	rd[0] = rs2[0] ^ 0x80000000UL;
+	return 1;
+}
+                
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/math-emu/fqtod.c linux/arch/sparc/math-emu/fqtod.c
--- v2.1.96/linux/arch/sparc/math-emu/fqtod.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc/math-emu/fqtod.c	Tue Apr 14 17:44:19 1998
@@ -0,0 +1,13 @@
+#include "soft-fp.h"
+#include "quad.h"
+#include "double.h"
+
+int FQTOD(void *rd, void *rs2)
+{
+	FP_DECL_Q(A); FP_DECL_D(R);
+
+	__FP_UNPACK_Q(A, rs2);
+	FP_CONV(D,Q,2,4,R,A);
+	__FP_PACK_D(rd, R);
+	return 1;
+}
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/math-emu/fqtos.c linux/arch/sparc/math-emu/fqtos.c
--- v2.1.96/linux/arch/sparc/math-emu/fqtos.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc/math-emu/fqtos.c	Tue Apr 14 17:44:19 1998
@@ -0,0 +1,13 @@
+#include "soft-fp.h"
+#include "quad.h"
+#include "single.h"
+
+int FQTOS(void *rd, void *rs2)
+{
+	FP_DECL_Q(A); FP_DECL_S(R);
+
+	__FP_UNPACK_Q(A, rs2);
+	FP_CONV(S,Q,1,4,R,A);
+	__FP_PACK_S(rd, R);
+	return 1;
+}
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/math-emu/fsmuld.c linux/arch/sparc/math-emu/fsmuld.c
--- v2.1.96/linux/arch/sparc/math-emu/fsmuld.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc/math-emu/fsmuld.c	Tue Apr 14 17:44:19 1998
@@ -0,0 +1,16 @@
+#include "soft-fp.h"
+#include "double.h"
+#include "single.h"
+
+int FSMULD(void *rd, void *rs2, void *rs1)
+{
+	FP_DECL_S(IN); FP_DECL_D(A); FP_DECL_D(B); FP_DECL_D(R);
+
+	__FP_UNPACK_S(IN, rs1);
+	FP_CONV(D,S,2,1,A,IN);
+	__FP_UNPACK_S(IN, rs2);
+	FP_CONV(D,S,2,1,B,IN);
+	FP_MUL_D(R, A, B);
+	__FP_PACK_D(rd, R);
+	return 1;
+}
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/math-emu/fstod.c linux/arch/sparc/math-emu/fstod.c
--- v2.1.96/linux/arch/sparc/math-emu/fstod.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc/math-emu/fstod.c	Tue Apr 14 17:44:19 1998
@@ -0,0 +1,13 @@
+#include "soft-fp.h"
+#include "double.h"
+#include "single.h"
+
+int FSTOD(void *rd, void *rs2)
+{
+	FP_DECL_S(A); FP_DECL_D(R);
+
+	__FP_UNPACK_S(A, rs2);
+	FP_CONV(D,S,2,1,R,A);
+	__FP_PACK_D(rd, R);
+	return 1;
+}
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/math-emu/fstoq.c linux/arch/sparc/math-emu/fstoq.c
--- v2.1.96/linux/arch/sparc/math-emu/fstoq.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc/math-emu/fstoq.c	Tue Apr 14 17:44:19 1998
@@ -0,0 +1,13 @@
+#include "soft-fp.h"
+#include "quad.h"
+#include "single.h"
+
+int FSTOQ(void *rd, void *rs2)
+{
+	FP_DECL_S(A); FP_DECL_Q(R);
+
+	__FP_UNPACK_S(A, rs2);
+	FP_CONV(Q,S,4,1,R,A);
+	__FP_PACK_Q(rd, R);
+	return 1;
+}
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/math-emu/math.c linux/arch/sparc/math-emu/math.c
--- v2.1.96/linux/arch/sparc/math-emu/math.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc/math-emu/math.c	Tue Apr 14 17:44:19 1998
@@ -0,0 +1,416 @@
+/* 
+ * arch/sparc/math-emu/math.c
+ *
+ * Copyright (C) 1998 Peter Maydell (pmaydell@chiark.greenend.org.uk)
+ * Based on the sparc64 code by Jakub Jelinek.
+ *
+ * This is a good place to start if you're trying to understand the
+ * emulation code, because it's pretty simple. What we do is 
+ * essentially analyse the instruction to work out what the operation
+ * is and which registers are involved. We then execute the appropriate
+ * FXXXX function. [The floating point queue introduces a minor wrinkle;
+ * see below...]
+ * The fxxxxx.c files each emulate a single insn. They look relatively
+ * simple because the complexity is hidden away in an unholy tangle
+ * of preprocessor macros. 
+ *
+ * WARNING : don't look at the macro definitions unless you 
+ * absolutely have to! They're extremely ugly, rather complicated
+ * and a single line in an fxxxx.c file can expand to the equivalent 
+ * of  30 lines or more of C. Of course, any error in those 30 lines 
+ * is reported by the compiler as an error in the single line with the
+ * macro usage...
+ * Question: should we replace them with inline functions?
+ *
+ * The first layer of macros is single.h, double.h, quad.h. Generally
+ * these files define macros for working with floating point numbers
+ * of the three IEEE formats. FP_ADD_D(R,A,B) is for adding doubles,
+ * for instance. These macros are usually defined as calls to more
+ * generic macros (in this case _FP_ADD(D,2,R,X,Y) where the number
+ * of machine words required to store the given IEEE format is passed
+ * as a parameter. [double.h and co check the number of bits in a word
+ * and define FP_ADD_D & co appropriately]. 
+ * The generic macros are defined in op-common.h. This is where all
+ * the grotty stuff like handling NaNs is coded. To handle the possible
+ * word sizes macros in op-common.h use macros like _FP_FRAC_SLL_##wc()
+ * where wc is the 'number of machine words' parameter (here 2). 
+ * These are defined in the third layer of macros: op-1.h, op-2.h
+ * and op-4.h. These handle operations on floating point numbers composed
+ * of 1,2 and 4 machine words respectively. [For example, on sparc64
+ * doubles are one machine word so macros in double.h eventually use
+ * constructs in op-1.h, but on sparc32 they use op-2.h definitions.]
+ * soft-fp.h is on the same level as op-common.h, and defines some
+ * macros which are independent of both word size and FP format.
+ * Finally, sfp-machine.h is the machine dependent part of the 
+ * code: it defines the word size and what type a word is. It also
+ * defines how _FP_MUL_MEAT_t() maps to _FP_MUL_MEAT_n_* : op-n.h
+ * provide several possible flavours of multiply algorithm, most
+ * of which require that you supply some form of asm or C primitive to
+ * do the actual multiply. (such asm primitives should be defined
+ * in sfp-machine.h too). udivmodti4.c is the same sort of thing.
+ *
+ * There may be some errors here because I'm working from a
+ * SPARC architecture manual V9, and what I really want is V8...
+ * Also, the insns which can generate exceptions seem to be a
+ * greater subset of the FPops than for V9 (for example, FCMPED
+ * has to be emulated on V8). So I think I'm going to have
+ * to emulate them all just to be on the safe side...
+ *
+ * Emulation routines originate from soft-fp package, which is
+ * part of glibc and has appropriate copyrights in it (allegedly).
+ *
+ * NB: on sparc int == long == 4 bytes, long long == 8 bytes.
+ * Most bits of the kernel seem to go for long rather than int,
+ * so we follow that practice...
+ */
+
+/* WISHLIST:
+ *
+ * + Replace all the macros with inline functions. These should
+ * have the same effect but be much easier to work with.
+ *
+ * + Emulate the IEEE exception flags. We don't currently do this
+ * because a) it would require significant alterations to
+ * the emulation macros [see the comments about _FP_NEG()
+ * in op-common.c and note that we'd need to invent a convention
+ * for passing in the flags to FXXXX fns and returning them] and 
+ * b) SPARClinux doesn't let users access the flags anyway 
+ * [contrast Solaris, which allows you to examine, clear or set
+ * the flags, and request that exceptions cause SIGFPE 
+ * [which you then set up a signal handler for, obviously...]].
+ * Erm, (b) may quite possibly be garbage. %fsr is user-writable
+ * so you don't need a syscall. There may or may not be library
+ * support.
+ *
+ * + Emulation of FMULQ, FDIVQ, FSQRTQ, FDMULQ needs to be 
+ * written!
+ * 
+ * + reindent code to conform to Linux kernel standard :->
+ *
+ * + work out whether all the compile-time warnings are bogus
+ *
+ * + check that conversion to/from integers works
+ * 
+ * + check with the SPARC architecture manual to see if we resolve
+ * the implementation-dependent bits of the IEEE spec in the
+ * same manner as the hardware.
+ *
+ * + more test cases for the test script always welcome!
+ *
+ * + illegal opcodes currently cause SIGFPEs. We should arrange
+ * to tell the traps.c code to SIGILL instead. Currently,
+ * everywhere that we return 0 should cause SIGILL, I think.
+ * SIGFPE should only be caused if we set an IEEE exception bit
+ * and the relevant trap bit is also set. (this means that 
+ * traps.c should do this; also it should handle the case of
+ * IEEE exception generated directly by the hardware.)
+ * Should illegal_fp_register (which is a flavour of fp exception)
+ * cause SIGFPE or  SIGILL?
+ *
+ * + the test script needs to be extended to handle the quadword
+ * and comparison insns.
+ *
+ * + _FP_DIV_MEAT_2_udiv_64() appears to work but it should be
+ * checked by somebody who understands the algorithm :->
+ * 
+ * + fpsave() saves the FP queue but fpload() doesn't reload it.
+ * Therefore when we context switch or change FPU ownership
+ * we have to check to see if the queue had anything in it and
+ * emulate it if it did. This is going to be a pain. 
+ */
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <asm/uaccess.h>
+
+
+#define FLOATFUNC(x) extern int x(void *,void *,void *)
+
+/* Current status: we don't properly emulate the difficult quadword
+ * insns (MUL, DIV, SQRT).
+ * There are also some ops involving the FP registers which we don't
+ * emulate: the branch on FP condition flags and the load/store to
+ * FP regs or FSR. I'm assuming that these will never generate traps
+ * (not unreasonable if there's an FPU at all; comments in the NetBSD
+ * kernel source agree on this point). If we wanted to allow
+ * purely software-emulation of the FPU with FPU totally disabled
+ * or non-existent, we'd have to emulate these as well. We'd also
+ * need to alter the fp_disabled trap handler to call the math-emu
+ * code appropriately. The structure of do_one_mathemu() is also
+ * inappropriate for these ops (as it has no way to alter the pc, 
+ * for a start) and it might be better to special-case them in do_mathemu().
+ * Oh, and you'd need to alter the traps.c code so it didn't try to
+ * fpsave() and fpload(). If there's genuinely no FPU then there's 
+ * probably bits of kernel stuff that just won't work anyway...
+ */
+
+/* The Vn labels indicate what version of the SPARC architecture gas thinks
+ * each insn is. This is from the binutils source :-> 
+ */
+/* quadword instructions */
+FLOATFUNC(FSQRTQ);                                /* v8 NYI */
+FLOATFUNC(FADDQ);                                 /* v8 */
+FLOATFUNC(FSUBQ);                                 /* v8 */
+FLOATFUNC(FMULQ);                                 /* v8 NYI */
+FLOATFUNC(FDIVQ);                                 /* v8 NYI */
+FLOATFUNC(FDMULQ);                                /* v8 NYI */
+FLOATFUNC(FQTOS);                                 /* v8 */
+FLOATFUNC(FQTOD);                                 /* v8 */
+FLOATFUNC(FITOQ);                                 /* v8 */
+FLOATFUNC(FSTOQ);                                 /* v8 */
+FLOATFUNC(FDTOQ);                                 /* v8 */
+FLOATFUNC(FQTOI);                                 /* v8 */
+FLOATFUNC(FCMPQ);                                 /* v8 */
+FLOATFUNC(FCMPEQ);                                /* v8 */
+/* single/double instructions (subnormal): should all work */
+FLOATFUNC(FSQRTS);                                /* v7 */
+FLOATFUNC(FSQRTD);                                /* v7 */
+FLOATFUNC(FADDS);                                 /* v6 */
+FLOATFUNC(FADDD);                                 /* v6 */
+FLOATFUNC(FSUBS);                                 /* v6 */
+FLOATFUNC(FSUBD);                                 /* v6 */
+FLOATFUNC(FMULS);                                 /* v6 */
+FLOATFUNC(FMULD);                                 /* v6 */
+FLOATFUNC(FDIVS);                                 /* v6 */
+FLOATFUNC(FDIVD);                                 /* v6 */
+FLOATFUNC(FSMULD);                                /* v8 */
+FLOATFUNC(FDTOS);                                 /* v6 */
+FLOATFUNC(FSTOD);                                 /* v6 */
+FLOATFUNC(FSTOI);                                 /* v6 */
+FLOATFUNC(FDTOI);                                 /* v6 */
+FLOATFUNC(FABSS);                                 /* v6 */
+FLOATFUNC(FCMPS);                                 /* v6 */
+FLOATFUNC(FCMPES);                                /* v6 */
+FLOATFUNC(FCMPD);                                 /* v6 */
+FLOATFUNC(FCMPED);                                /* v6 */
+FLOATFUNC(FMOVS);                                 /* v6 */
+FLOATFUNC(FNEGS);                                 /* v6 */
+FLOATFUNC(FITOS);                                 /* v6 */
+FLOATFUNC(FITOD);                                 /* v6 */
+
+static int do_one_mathemu(u32 insn, unsigned long *fsr, unsigned long *fregs);   
+
+/* Unlike the Sparc64 version (which has a struct fpustate), we
+ * pass the taskstruct corresponding to the task which currently owns the
+ * FPU. This is partly because we don't have the fpustate struct and
+ * partly because the task owning the FPU isn't always current (as is
+ * the case for the Sparc64 port). This is probably SMP-related...
+ * This function returns 1 if all queued insns were emulated successfully.
+ * The test for unimplemented FPop in kernel mode has been moved into
+ * kernel/traps.c for simplicity.
+ */
+int do_mathemu(struct pt_regs *regs, struct task_struct *fpt)
+{
+   /* regs->pc isn't necessarily the PC at which the offending insn is sitting.
+    * The FPU maintains a queue of FPops which cause traps. 
+    * When it hits an instruction that requires that the trapped op succeeded
+    * (usually because it reads a reg. that the trapped op wrote) then it
+    * causes this exception. We need to emulate all the insns on the queue
+    * and then allow the op to proceed.
+    * This code should also handle the case where the trap was precise,
+    * in which case the queue length is zero and regs->pc points at the 
+    * single FPop to be emulated. (this case is untested, though :->) 
+    * You'll need this case if you want to be able to emulate all FPops
+    * because the FPU either doesn't exist or has been software-disabled.
+    * [The UltraSPARC makes FP a precise trap; this isn't as stupid as it 
+    * might sound because the Ultra does funky things with a superscalar
+    * architecture.]
+    */
+   
+   /* You wouldn't believe how often I typed 'ftp' when I meant 'fpt' :-> */
+
+   int i;
+   int retcode = 0;                               /* assume all succeed */
+   unsigned long insn;
+   
+#ifdef DEBUG_MATHEMU   
+   printk("In do_mathemu()... pc is %08lx\n", regs->pc);
+   printk("fpqdepth is %ld\n",fpt->tss.fpqdepth);
+   for (i = 0; i < fpt->tss.fpqdepth; i++)
+      printk("%d: %08lx at %08lx\n",i,fpt->tss.fpqueue[i].insn, (unsigned long)fpt->tss.fpqueue[i].insn_addr);
+#endif      
+
+   if (fpt->tss.fpqdepth == 0) {                   /* no queue, guilty insn is at regs->pc */
+#ifdef DEBUG_MATHEMU   
+      printk("precise trap at %08lx\n", regs->pc);
+#endif
+      if (!get_user(insn, (u32 *)regs->pc)) {
+         retcode = do_one_mathemu(insn, &fpt->tss.fsr, fpt->tss.float_regs);
+         if (retcode) {
+            /* in this case we need to fix up PC & nPC */
+            regs->pc = regs->npc;
+            regs->npc += 4;
+         }
+      }
+      return retcode;
+   }
+
+   /* Normal case: need to empty the queue... */
+   for (i = 0; i < fpt->tss.fpqdepth; i++)
+   {
+      retcode = do_one_mathemu(fpt->tss.fpqueue[i].insn, &(fpt->tss.fsr), fpt->tss.float_regs);
+      if (!retcode)                               /* insn failed, no point doing any more */
+         break;
+   }
+   /* Now empty the queue and clear the queue_not_empty flag */
+   fpt->tss.fsr &= ~0x3000;
+   fpt->tss.fpqdepth = 0;
+   
+   return retcode;
+}
+
+static int do_one_mathemu(u32 insn, unsigned long *fsr, unsigned long *fregs)
+{
+   /* Emulate the given insn, updating fsr and fregs appropriately. */
+   int type = 0; 
+   /* 01 is single, 10 is double, 11 is quad, 
+    * 000011 is rs1, 001100 is rs2, 110000 is rd (00 in rd is fcc)
+    * 111100000000 tells which ftt that may happen in 
+    * (this field not used on sparc32 code, as we can't 
+    * extract trap type info for ops on the FP queue) 
+    */
+   int freg;
+   int (*func)(void *,void *,void *) = NULL;
+   void *rs1 = NULL, *rs2 = NULL, *rd = NULL;   
+
+#ifdef DEBUG_MATHEMU
+   printk("In do_mathemu(), emulating %08lx\n", insn);
+#endif   
+      
+   if ((insn & 0xc1f80000) == 0x81a00000) /* FPOP1 */ {
+      switch ((insn >> 5) & 0x1ff) {
+         /* QUAD - ftt == 3 */
+         case 0x001: type = 0x314; func = FMOVS; break;
+         case 0x005: type = 0x314; func = FNEGS; break;
+         case 0x009: type = 0x314; func = FABSS; break;
+         case 0x02b: type = 0x33c; func = FSQRTQ; break;
+         case 0x043: type = 0x33f; func = FADDQ; break;
+         case 0x047: type = 0x33f; func = FSUBQ; break;
+         case 0x04b: type = 0x33f; func = FMULQ; break;
+         case 0x04f: type = 0x33f; func = FDIVQ; break;
+         case 0x06e: type = 0x33a; func = FDMULQ; break;
+         case 0x0c7: type = 0x31c; func = FQTOS; break;
+         case 0x0cb: type = 0x32c; func = FQTOD; break;
+         case 0x0cc: type = 0x334; func = FITOQ; break;
+         case 0x0cd: type = 0x334; func = FSTOQ; break;
+         case 0x0ce: type = 0x338; func = FDTOQ; break;
+         case 0x0d3: type = 0x31c; func = FQTOI; break;
+            /* SUBNORMAL - ftt == 2 */
+         case 0x029: type = 0x214; func = FSQRTS; break;
+         case 0x02a: type = 0x228; func = FSQRTD; break;
+         case 0x041: type = 0x215; func = FADDS; break;
+         case 0x042: type = 0x22a; func = FADDD; break;
+         case 0x045: type = 0x215; func = FSUBS; break;
+         case 0x046: type = 0x22a; func = FSUBD; break;
+         case 0x049: type = 0x215; func = FMULS; break;
+         case 0x04a: type = 0x22a; func = FMULD; break;
+         case 0x04d: type = 0x215; func = FDIVS; break;
+         case 0x04e: type = 0x22a; func = FDIVD; break;
+         case 0x069: type = 0x225; func = FSMULD; break;
+         case 0x0c6: type = 0x218; func = FDTOS; break;
+         case 0x0c9: type = 0x224; func = FSTOD; break;
+         case 0x0d1: type = 0x214; func = FSTOI; break;
+         case 0x0d2: type = 0x218; func = FDTOI; break;
+         default: 
+#ifdef DEBUG_MATHEMU         
+         	printk("unknown FPop1: %03lx\n",(insn>>5)&0x1ff);
+#endif         
+      }
+   }
+   else if ((insn & 0xc1f80000) == 0x81a80000) /* FPOP2 */ {
+      switch ((insn >> 5) & 0x1ff) {
+         case 0x051: type = 0x305; func = FCMPS; break;
+         case 0x052: type = 0x30a; func = FCMPD; break;
+         case 0x053: type = 0x30f; func = FCMPQ; break;
+         case 0x055: type = 0x305; func = FCMPES; break;
+         case 0x056: type = 0x30a; func = FCMPED; break;
+         case 0x057: type = 0x30f; func = FCMPEQ; break;
+         default: 
+#ifdef DEBUG_MATHEMU         
+         	printk("unknown FPop2: %03lx\n",(insn>>5)&0x1ff);
+#endif         	
+      }
+   }
+   
+   if (!type) { /* oops, didn't recognise that FPop */
+      printk("attempt to emulate unrecognised FPop!\n");
+      return 0;
+   }
+   
+   /* Decode the registers to be used */
+   freg = (*fsr >> 14) & 0xf;
+
+   *fsr &= ~0x1c000;                              /* clear the traptype bits */
+    
+   freg = ((insn >> 14) & 0x1f);
+   switch (type & 0x3)                            /* is rs1 single, double or quad? */
+   {
+      case 3:
+         if (freg & 3)                            /* quadwords must have bits 4&5 of the */
+         {                                        /* encoded reg. number set to zero. */
+            *fsr |= (6 << 14);                  
+            return 0;                             /* simulate invalid_fp_register exception */
+         }
+         /* fall through */
+      case 2:
+         if (freg & 1)                            /* doublewords must have bit 5 zeroed */
+         {
+            *fsr |= (6 << 14);
+            return 0;
+         }
+   }
+   rs1 = (void *)&fregs[freg];
+   freg = (insn & 0x1f);
+   switch ((type >> 2) & 0x3)
+   {                                              /* same again for rs2 */
+      case 3:
+         if (freg & 3)                            /* quadwords must have bits 4&5 of the */
+         {                                        /* encoded reg. number set to zero. */
+            *fsr |= (6 << 14);                  
+            return 0;                             /* simulate invalid_fp_register exception */
+         }
+         /* fall through */
+      case 2:
+         if (freg & 1)                            /* doublewords must have bit 5 zeroed */
+         {
+            *fsr |= (6 << 14);
+            return 0;
+         }
+   }
+   rs2 = (void *)&fregs[freg];
+   freg = ((insn >> 25) & 0x1f);
+   switch ((type >> 4) & 0x3)                     /* and finally rd. This one's a bit different */
+   {
+      case 0:                                     /* dest is fcc. (this must be FCMPQ or FCMPEQ) */
+         if (freg)                                /* V8 has only one set of condition codes, so */
+         {                                        /* anything but 0 in the rd field is an error */
+            *fsr |= (6 << 14);                    /* (should probably flag as invalid opcode */
+            return 0;                             /* but SIGFPE will do :-> ) */
+         }
+         rd = (void *)(fsr);                      /* FCMPQ and FCMPEQ are special and only  */
+         break;                                   /* set bits they're supposed to :-> */
+      case 3:
+         if (freg & 3)                            /* quadwords must have bits 4&5 of the */
+         {                                        /* encoded reg. number set to zero. */
+            *fsr |= (6 << 14);
+            return 0;                             /* simulate invalid_fp_register exception */
+         }
+         /* fall through */
+      case 2:
+         if (freg & 1)                            /* doublewords must have bit 5 zeroed */
+         {
+            *fsr |= (6 << 14);
+            return 0;
+         }
+         /* fall through */
+      case 1:
+         rd = (void *)&fregs[freg];
+         break;
+   }
+#ifdef DEBUG_MATHEMU   
+   printk("executing insn...\n");
+#endif   
+   func(rd, rs2, rs1);                            /* do the Right Thing */
+   return 1;                                      /* success! */
+}
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/math-emu/sfp-machine.h linux/arch/sparc/math-emu/sfp-machine.h
--- v2.1.96/linux/arch/sparc/math-emu/sfp-machine.h	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc/math-emu/sfp-machine.h	Tue Apr 14 17:44:19 1998
@@ -0,0 +1,363 @@
+/* Machine-dependent software floating-point definitions.  Sparc version.
+   Copyright (C) 1997 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If
+   not, write to the Free Software Foundation, Inc.,
+   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  
+
+   Actually, this is a sparc (32bit) version, written based on the
+   i386 and sparc64 versions, by me, 
+   Peter Maydell (pmaydell@chiark.greenend.org.uk).
+   Comments are by and large also mine, although they may be inaccurate.
+
+   In picking out asm fragments I've gone with the lowest common
+   denominator, which also happens to be the hardware I have :->
+   That is, a SPARC without hardware multiply and divide.
+ */
+
+
+/* basic word size definitions */
+#define _FP_W_TYPE_SIZE		32
+#define _FP_W_TYPE		unsigned long
+#define _FP_WS_TYPE		signed long
+#define _FP_I_TYPE		long
+
+/* You can optionally code some things like addition in asm. For
+ * example, i386 defines __FP_FRAC_ADD_2 as asm. If you don't
+ * then you get a fragment of C code [if you change an #ifdef 0
+ * in op-2.h] or a call to add_ssaaaa (see below).
+ * Good places to look for asm fragments to use are gcc and glibc.
+ * gcc's longlong.h is useful.
+ */
+
+/* We need to know how to multiply and divide. If the host word size
+ * is >= 2*fracbits you can use FP_MUL_MEAT_n_imm(t,R,X,Y) which
+ * codes the multiply with whatever gcc does to 'a * b'.
+ * _FP_MUL_MEAT_n_wide(t,R,X,Y,f) is used when you have an asm 
+ * function that can multiply two 1W values and get a 2W result. 
+ * Otherwise you're stuck with _FP_MUL_MEAT_n_hard(t,R,X,Y) which
+ * does bitshifting to avoid overflow.
+ * For division there is FP_DIV_MEAT_n_imm(t,R,X,Y,f) for word size
+ * >= 2*fracbits, where f is either _FP_DIV_HELP_imm or 
+ * _FP_DIV_HELP_ldiv (see op-1.h).
+ * _FP_DIV_MEAT_udiv() is if you have asm to do 2W/1W => (1W, 1W).
+ * [GCC and glibc have longlong.h which has the asm macro udiv_qrnnd
+ * to do this.]
+ * In general, 'n' is the number of words required to hold the type,
+ * and 't' is either S, D or Q for single/double/quad.
+ *           -- PMM
+ */
+/* Example: SPARC64:
+ * #define _FP_MUL_MEAT_S(R,X,Y)	_FP_MUL_MEAT_1_imm(S,R,X,Y)
+ * #define _FP_MUL_MEAT_D(R,X,Y)	_FP_MUL_MEAT_1_wide(D,R,X,Y,umul_ppmm)
+ * #define _FP_MUL_MEAT_Q(R,X,Y)	_FP_MUL_MEAT_2_wide(Q,R,X,Y,umul_ppmm)
+ *
+ * #define _FP_DIV_MEAT_S(R,X,Y)	_FP_DIV_MEAT_1_imm(S,R,X,Y,_FP_DIV_HELP_imm)
+ * #define _FP_DIV_MEAT_D(R,X,Y)	_FP_DIV_MEAT_1_udiv(D,R,X,Y)
+ * #define _FP_DIV_MEAT_Q(R,X,Y)	_FP_DIV_MEAT_2_udiv_64(Q,R,X,Y)
+ *
+ * Example: i386:
+ * #define _FP_MUL_MEAT_S(R,X,Y)   _FP_MUL_MEAT_1_wide(S,R,X,Y,_i386_mul_32_64)
+ * #define _FP_MUL_MEAT_D(R,X,Y)   _FP_MUL_MEAT_2_wide(D,R,X,Y,_i386_mul_32_64)
+ *
+ * #define _FP_DIV_MEAT_S(R,X,Y)   _FP_DIV_MEAT_1_udiv(S,R,X,Y,_i386_div_64_32)
+ * #define _FP_DIV_MEAT_D(R,X,Y)   _FP_DIV_MEAT_2_udiv_64(D,R,X,Y)
+ */
+#define _FP_MUL_MEAT_S(R,X,Y)   _FP_MUL_MEAT_1_wide(S,R,X,Y,umul_ppmm)
+#define _FP_MUL_MEAT_D(R,X,Y)   _FP_MUL_MEAT_2_wide(D,R,X,Y,umul_ppmm)
+/* FIXME: This is not implemented, but should be soon */
+#define _FP_MUL_MEAT_Q(R,X,Y)   _FP_FRAC_SET_4(R, _FP_ZEROFRAC_4)
+#define _FP_DIV_MEAT_S(R,X,Y)   _FP_DIV_MEAT_1_udiv(S,R,X,Y)
+#define _FP_DIV_MEAT_D(R,X,Y)   _FP_DIV_MEAT_2_udiv_64(D,R,X,Y)
+/* FIXME: This is not implemented, but should be soon */
+#define _FP_DIV_MEAT_Q(R,X,Y)   _FP_FRAC_SET_4(R, _FP_ZEROFRAC_4)
+
+/* These macros define what NaN looks like. They're supposed to expand to 
+ * a comma-separated set of 32bit unsigned ints that encode NaN.
+ */
+#define _FP_NANFRAC_S		_FP_QNANBIT_S
+#define _FP_NANFRAC_D		_FP_QNANBIT_D, 0
+#define _FP_NANFRAC_Q           _FP_QNANBIT_Q, 0, 0, 0
+
+#define _FP_KEEPNANFRACP 1
+
+/* This macro appears to be called when both X and Y are NaNs, and 
+ * has to choose one and copy it to R. i386 goes for the larger of the
+ * two, sparc64 just picks Y. I don't understand this at all so I'll
+ * go with sparc64 because it's shorter :->   -- PMM 
+ */
+#define _FP_CHOOSENAN(fs, wc, R, X, Y)				\
+  do {								\
+    R##_s = Y##_s;						\
+    _FP_FRAC_COPY_##wc(R,Y);					\
+    R##_c = FP_CLS_NAN;						\
+  } while (0)
+  
+#define __FP_UNPACK_RAW_1(fs, X, val)				\
+  do {								\
+    union _FP_UNION_##fs *_flo =				\
+    	(union _FP_UNION_##fs *)val;				\
+								\
+    X##_f = _flo->bits.frac;					\
+    X##_e = _flo->bits.exp;					\
+    X##_s = _flo->bits.sign;					\
+  } while (0)
+
+#define __FP_PACK_RAW_1(fs, val, X)				\
+  do {								\
+    union _FP_UNION_##fs *_flo =				\
+    	(union _FP_UNION_##fs *)val;				\
+								\
+    _flo->bits.frac = X##_f;					\
+    _flo->bits.exp  = X##_e;					\
+    _flo->bits.sign = X##_s;					\
+  } while (0)
+  
+#define __FP_UNPACK_RAW_2(fs, X, val)			\
+  do {							\
+    union _FP_UNION_##fs *_flo =			\
+    	(union _FP_UNION_##fs *)val;			\
+							\
+    X##_f0 = _flo->bits.frac0;				\
+    X##_f1 = _flo->bits.frac1;				\
+    X##_e  = _flo->bits.exp;				\
+    X##_s  = _flo->bits.sign;				\
+  } while (0)
+
+#define __FP_PACK_RAW_2(fs, val, X)			\
+  do {							\
+    union _FP_UNION_##fs *_flo =			\
+    	(union _FP_UNION_##fs *)val;			\
+							\
+    _flo->bits.frac0 = X##_f0;				\
+    _flo->bits.frac1 = X##_f1;				\
+    _flo->bits.exp   = X##_e;				\
+    _flo->bits.sign  = X##_s;				\
+  } while (0)
+
+#define __FP_UNPACK_RAW_4(fs, X, val)			\
+  do {							\
+    union _FP_UNION_##fs *_flo =			\
+    	(union _FP_UNION_##fs *)val;			\
+							\
+    X##_f[0] = _flo->bits.frac0;			\
+    X##_f[1] = _flo->bits.frac1;			\
+    X##_f[2] = _flo->bits.frac2;			\
+    X##_f[3] = _flo->bits.frac3;			\
+    X##_e  = _flo->bits.exp;				\
+    X##_s  = _flo->bits.sign;				\
+  } while (0)
+
+#define __FP_PACK_RAW_4(fs, val, X)			\
+  do {							\
+    union _FP_UNION_##fs *_flo =			\
+    	(union _FP_UNION_##fs *)val;			\
+							\
+    _flo->bits.frac0 = X##_f[0];			\
+    _flo->bits.frac1 = X##_f[1];			\
+    _flo->bits.frac2 = X##_f[2];			\
+    _flo->bits.frac3 = X##_f[3];			\
+    _flo->bits.exp   = X##_e;				\
+    _flo->bits.sign  = X##_s;				\
+  } while (0)
+
+#define __FP_UNPACK_S(X,val)		\
+  do {					\
+    __FP_UNPACK_RAW_1(S,X,val);		\
+    _FP_UNPACK_CANONICAL(S,1,X);	\
+  } while (0)
+
+#define __FP_PACK_S(val,X)		\
+  do {					\
+    _FP_PACK_CANONICAL(S,1,X);		\
+    __FP_PACK_RAW_1(S,val,X);		\
+  } while (0)
+
+#define __FP_UNPACK_D(X,val)		\
+  do {					\
+    __FP_UNPACK_RAW_2(D,X,val);		\
+    _FP_UNPACK_CANONICAL(D,2,X);	\
+  } while (0)
+
+#define __FP_PACK_D(val,X)		\
+  do {					\
+    _FP_PACK_CANONICAL(D,2,X);		\
+    __FP_PACK_RAW_2(D,val,X);		\
+  } while (0)
+
+#define __FP_UNPACK_Q(X,val)		\
+  do {					\
+    __FP_UNPACK_RAW_4(Q,X,val);		\
+    _FP_UNPACK_CANONICAL(Q,4,X);	\
+  } while (0)
+
+#define __FP_PACK_Q(val,X)		\
+  do {					\
+    _FP_PACK_CANONICAL(Q,4,X);		\
+    __FP_PACK_RAW_4(Q,val,X);		\
+  } while (0)
+
+/* the asm fragments go here: all these are taken from glibc-2.0.5's stdlib/longlong.h */
+
+#include <linux/types.h>
+#include <asm/byteorder.h>
+
+/* add_ssaaaa is used in op-2.h and should be equivalent to
+ * #define add_ssaaaa(sh,sl,ah,al,bh,bl) (sh = ah+bh+ (( sl = al+bl) < al))
+ * add_ssaaaa(high_sum, low_sum, high_addend_1, low_addend_1,
+ * high_addend_2, low_addend_2) adds two UWtype integers, composed by
+ * HIGH_ADDEND_1 and LOW_ADDEND_1, and HIGH_ADDEND_2 and LOW_ADDEND_2
+ * respectively.  The result is placed in HIGH_SUM and LOW_SUM.  Overflow
+ * (i.e. carry out) is not stored anywhere, and is lost.
+ */
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+  __asm__ ("addcc %r4,%5,%1
+        addx %r2,%3,%0"                                                 \
+           : "=r" ((USItype)(sh)),                                      \
+             "=&r" ((USItype)(sl))                                      \
+           : "%rJ" ((USItype)(ah)),                                     \
+             "rI" ((USItype)(bh)),                                      \
+             "%rJ" ((USItype)(al)),                                     \
+             "rI" ((USItype)(bl))                                       \
+           : "cc")
+
+
+/* sub_ddmmss is used in op-2.h and udivmodti4.c and should be equivalent to
+ * #define sub_ddmmss(sh, sl, ah, al, bh, bl) (sh = ah-bh - ((sl = al-bl) > al))
+ * sub_ddmmss(high_difference, low_difference, high_minuend, low_minuend,
+ * high_subtrahend, low_subtrahend) subtracts two two-word UWtype integers,
+ * composed by HIGH_MINUEND_1 and LOW_MINUEND_1, and HIGH_SUBTRAHEND_2 and
+ * LOW_SUBTRAHEND_2 respectively.  The result is placed in HIGH_DIFFERENCE
+ * and LOW_DIFFERENCE.  Overflow (i.e. carry out) is not stored anywhere,
+ * and is lost.
+ */
+
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+  __asm__ ("subcc %r4,%5,%1
+        subx %r2,%3,%0"                                                 \
+           : "=r" ((USItype)(sh)),                                      \
+             "=&r" ((USItype)(sl))                                      \
+           : "rJ" ((USItype)(ah)),                                      \
+             "rI" ((USItype)(bh)),                                      \
+             "rJ" ((USItype)(al)),                                      \
+             "rI" ((USItype)(bl))                                       \
+           : "cc")
+
+
+/* asm fragments for mul and div */	 
+/* umul_ppmm(high_prod, low_prod, multipler, multiplicand) multiplies two
+ * UWtype integers MULTIPLER and MULTIPLICAND, and generates a two UWtype
+ * word product in HIGH_PROD and LOW_PROD.
+ * These look ugly because the sun4/4c don't have umul/udiv/smul/sdiv in
+ * hardware. 
+ */
+#define umul_ppmm(w1, w0, u, v) \
+  __asm__ ("! Inlined umul_ppmm
+        wr      %%g0,%2,%%y     ! SPARC has 0-3 delay insn after a wr
+        sra     %3,31,%%g2      ! Don't move this insn
+        and     %2,%%g2,%%g2    ! Don't move this insn
+        andcc   %%g0,0,%%g1     ! Don't move this insn
+        mulscc  %%g1,%3,%%g1
+        mulscc  %%g1,%3,%%g1
+        mulscc  %%g1,%3,%%g1
+        mulscc  %%g1,%3,%%g1
+        mulscc  %%g1,%3,%%g1
+        mulscc  %%g1,%3,%%g1
+        mulscc  %%g1,%3,%%g1
+        mulscc  %%g1,%3,%%g1
+        mulscc  %%g1,%3,%%g1
+        mulscc  %%g1,%3,%%g1
+        mulscc  %%g1,%3,%%g1
+        mulscc  %%g1,%3,%%g1
+        mulscc  %%g1,%3,%%g1
+        mulscc  %%g1,%3,%%g1
+        mulscc  %%g1,%3,%%g1
+        mulscc  %%g1,%3,%%g1
+        mulscc  %%g1,%3,%%g1
+        mulscc  %%g1,%3,%%g1
+        mulscc  %%g1,%3,%%g1
+        mulscc  %%g1,%3,%%g1
+        mulscc  %%g1,%3,%%g1
+        mulscc  %%g1,%3,%%g1
+        mulscc  %%g1,%3,%%g1
+        mulscc  %%g1,%3,%%g1
+        mulscc  %%g1,%3,%%g1
+        mulscc  %%g1,%3,%%g1
+        mulscc  %%g1,%3,%%g1
+        mulscc  %%g1,%3,%%g1
+        mulscc  %%g1,%3,%%g1
+        mulscc  %%g1,%3,%%g1
+        mulscc  %%g1,%3,%%g1
+        mulscc  %%g1,%3,%%g1
+        mulscc  %%g1,0,%%g1
+        add     %%g1,%%g2,%0
+        rd      %%y,%1"                                                 \
+           : "=r" ((USItype)(w1)),                                      \
+             "=r" ((USItype)(w0))                                       \
+           : "%rI" ((USItype)(u)),                                      \
+             "r" ((USItype)(v))                                         \
+           : "%g1", "%g2", "cc")
+
+/* udiv_qrnnd(quotient, remainder, high_numerator, low_numerator,
+ * denominator) divides a UDWtype, composed by the UWtype integers
+ * HIGH_NUMERATOR and LOW_NUMERATOR, by DENOMINATOR and places the quotient
+ * in QUOTIENT and the remainder in REMAINDER.  HIGH_NUMERATOR must be less
+ * than DENOMINATOR for correct operation.  If, in addition, the most
+ * significant bit of DENOMINATOR must be 1, then the pre-processor symbol
+ * UDIV_NEEDS_NORMALIZATION is defined to 1.
+ */
+
+#define udiv_qrnnd(q, r, n1, n0, d) \
+  __asm__ ("! Inlined udiv_qrnnd
+        mov     32,%%g1
+        subcc   %1,%2,%%g0
+1:      bcs     5f
+         addxcc %0,%0,%0        ! shift n1n0 and a q-bit in lsb
+        sub     %1,%2,%1        ! this kills msb of n
+        addx    %1,%1,%1        ! so this can't give carry
+        subcc   %%g1,1,%%g1
+2:      bne     1b
+         subcc  %1,%2,%%g0
+        bcs     3f
+         addxcc %0,%0,%0        ! shift n1n0 and a q-bit in lsb
+        b       3f
+         sub    %1,%2,%1        ! this kills msb of n
+4:      sub     %1,%2,%1
+5:      addxcc  %1,%1,%1
+        bcc     2b
+         subcc  %%g1,1,%%g1
+! Got carry from n.  Subtract next step to cancel this carry.
+        bne     4b
+         addcc  %0,%0,%0        ! shift n1n0 and a 0-bit in lsb
+        sub     %1,%2,%1
+3:      xnor    %0,0,%0
+        ! End of inline udiv_qrnnd"                                     \
+           : "=&r" ((USItype) (q)),                                     \
+             "=&r" ((USItype) (r))                                      \
+           : "r" ((USItype) (d)),                                       \
+             "1" ((USItype) (n1)),                                      \
+             "0" ((USItype) (n0)) : "%g1", "cc")
+
+#define UDIV_NEEDS_NORMALIZATION 0
+
+#define abort()								\
+	return 0
+
+#ifdef __BIG_ENDIAN
+#define __BYTE_ORDER __BIG_ENDIAN
+#else
+#define __BYTE_ORDER __LITTLE_ENDIAN
+#endif
+
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/mm/Makefile linux/arch/sparc/mm/Makefile
--- v2.1.96/linux/arch/sparc/mm/Makefile	Mon Jan 12 15:15:43 1998
+++ linux/arch/sparc/mm/Makefile	Tue Apr 14 17:44:20 1998
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.27 1997/11/07 15:01:27 jj Exp $
+# $Id: Makefile,v 1.30 1998/03/09 14:03:53 jj Exp $
 # Makefile for the linux Sparc-specific parts of the memory manager.
 #
 # Note! Dependencies are done automagically by 'make dep', which also
@@ -8,9 +8,17 @@
 # Note 2! The CFLAGS definition is now in the main makefile...
 
 O_TARGET := mm.o
-O_OBJS   := fault.o init.o sun4c.o srmmu.o hypersparc.o viking.o \
-	    tsunami.o loadmmu.o generic.o asyncd.o extable.o \
-	    turbosparc.o iommu.o io-unit.o
+O_OBJS   := fault.o init.o loadmmu.o generic.o asyncd.o extable.o btfixup.o
+ifeq ($(CONFIG_SUN4),y)
+O_OBJS	 += nosrmmu.o
+else
+O_OBJS	 += srmmu.o iommu.o io-unit.o hypersparc.o viking.o tsunami.o turbosparc.o
+endif
+ifdef SMP
+O_OBJS   += nosun4c.o
+else
+O_OBJS   += sun4c.o
+endif
 
 include $(TOPDIR)/Rules.make
 
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/mm/btfixup.c linux/arch/sparc/mm/btfixup.c
--- v2.1.96/linux/arch/sparc/mm/btfixup.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc/mm/btfixup.c	Tue Apr 14 17:44:20 1998
@@ -0,0 +1,334 @@
+/* $Id: btfixup.c,v 1.7 1998/03/09 14:03:56 jj Exp $
+ * btfixup.c: Boot time code fixup and relocator, so that
+ * we can get rid of most indirect calls to achieve single
+ * image sun4c and srmmu kernel.
+ *
+ * Copyright (C) 1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <asm/btfixup.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/oplib.h>
+#include <asm/system.h>
+
+#define BTFIXUP_OPTIMIZE_NOP
+#define BTFIXUP_OPTIMIZE_OTHER
+
+extern char *srmmu_name;
+static char version[] __initdata = "Boot time fixup v1.6. 4/Mar/98 Jakub Jelinek (jj@ultra.linux.cz). Patching kernel for ";
+#ifdef CONFIG_SUN4
+static char str_sun4c[] __initdata = "sun4\n";
+#else
+static char str_sun4c[] __initdata = "sun4c\n";
+#endif
+static char str_srmmu[] __initdata = "srmmu[%s]/";
+static char str_iommu[] __initdata = "iommu\n";
+static char str_iounit[] __initdata = "io-unit\n";
+
+static int visited __initdata = 0;
+extern unsigned int ___btfixup_start[], ___btfixup_end[], __init_begin[], __init_end[], __init_text_end[];
+extern unsigned int _stext[], _end[], __start___ksymtab[], __stop___ksymtab[];
+static char wrong_f[] __initdata = "Trying to set f fixup %p to invalid function %08x\n";
+static char wrong_b[] __initdata = "Trying to set b fixup %p to invalid function %08x\n";
+static char wrong_s[] __initdata = "Trying to set s fixup %p to invalid value %08x\n";
+static char wrong_h[] __initdata = "Trying to set h fixup %p to invalid value %08x\n";
+static char wrong_a[] __initdata = "Trying to set a fixup %p to invalid value %08x\n";
+static char wrong[] __initdata = "Wrong address for %c fixup %p\n";
+static char insn_f[] __initdata = "Fixup f %p refers to weird instructions at %p[%08x,%08x]\n";
+static char insn_b[] __initdata = "Fixup b %p doesn't refer to a SETHI at %p[%08x]\n";
+static char insn_s[] __initdata = "Fixup s %p doesn't refer to an OR at %p[%08x]\n";
+static char insn_h[] __initdata = "Fixup h %p doesn't refer to a SETHI at %p[%08x]\n";
+static char insn_a[] __initdata = "Fixup a %p doesn't refer to a SETHI nor OR at %p[%08x]\n";
+static char insn_i[] __initdata = "Fixup i %p doesn't refer to a valid instruction at %p[%08x]\n";
+static char fca_und[] __initdata = "flush_cache_all undefined in btfixup()\n";
+static char wrong_setaddr[] __initdata = "Garbled CALL/INT patch at %p[%08x,%08x,%08x]=%08x\n";
+
+#ifdef BTFIXUP_OPTIMIZE_OTHER
+__initfunc(static void set_addr(unsigned int *addr, unsigned int q1, int fmangled, unsigned int value))
+{
+	if (!fmangled)
+		*addr = value;
+	else {
+		unsigned int *q = (unsigned int *)q1;
+		if (*addr == 0x01000000) {
+			/* Noped */
+			*q = value;
+		} else if (addr[-1] == *q) {
+			/* Moved */
+			addr[-1] = value;
+			*q = value;
+		} else {
+			prom_printf(wrong_setaddr, addr-1, addr[-1], *addr, *q, value);
+			prom_halt();
+		}
+	}
+}
+#else
+static __inline__ void set_addr(unsigned int *addr, unsigned int q1, int fmangled, unsigned int value)
+{
+	*addr = value;
+}
+#endif
+
+__initfunc(void btfixup(void))
+{
+	unsigned int *p, *q;
+	int type, count;
+	unsigned insn;
+	unsigned *addr;
+	int fmangled = 0;
+	void (*flush_cacheall)(void);
+	
+	if (!visited) {
+		visited++;
+		printk(version);
+		if (ARCH_SUN4C_SUN4)
+			printk(str_sun4c);
+		else {
+			printk(str_srmmu, srmmu_name);
+			if (sparc_cpu_model == sun4d)
+				printk(str_iounit);
+			else
+				printk(str_iommu);
+		}
+	}
+	for (p = ___btfixup_start; p < ___btfixup_end; ) {
+		count = p[2];
+		q = p + 3;
+		switch (type = *(unsigned char *)p) {
+		case 'f': 
+			count = p[3];
+			q = p + 4;
+			if (((p[0] & 1) || p[1]) 
+			    && ((p[1] & 3) || (unsigned *)(p[1]) < _stext || (unsigned *)(p[1]) >= _end)) {
+				prom_printf(wrong_f, p, p[1]);
+				prom_halt();
+			}
+			break;
+		case 'b':
+			if (p[1] < (unsigned long)__init_begin || p[1] >= (unsigned long)__init_text_end || (p[1] & 3)) {
+				prom_printf(wrong_b, p, p[1]);
+				prom_halt();
+			}
+			break;
+		case 's':
+			if (p[1] + 0x1000 >= 0x2000) {
+				prom_printf(wrong_s, p, p[1]);
+				prom_halt();
+			}
+			break;
+		case 'h':
+			if (p[1] & 0x3ff) {
+				prom_printf(wrong_h, p, p[1]);
+				prom_halt();
+			}
+			break;
+		case 'a':
+			if (p[1] + 0x1000 >= 0x2000 && (p[1] & 0x3ff)) {
+				prom_printf(wrong_a, p, p[1]);
+				prom_halt();
+			}
+			break;
+		}
+		if (p[0] & 1) {
+			p[0] &= ~1;
+			while (count) {
+				fmangled = 0;
+				addr = (unsigned *)*q;
+				if (addr < _stext || addr >= _end) {
+					prom_printf(wrong, type, p);
+					prom_halt();
+				}
+				insn = *addr;
+#ifdef BTFIXUP_OPTIMIZE_OTHER				
+				if (type != 'f' && q[1]) {
+					insn = *(unsigned int *)q[1];
+					if (!insn || insn == 1)
+						insn = *addr;
+					else
+						fmangled = 1;
+				}
+#endif
+				switch (type) {
+				case 'f':	/* CALL */
+					if (addr >= __start___ksymtab && addr < __stop___ksymtab) {
+						*addr = p[1];
+						break;
+					} else if (!q[1]) {
+						if ((insn & 0xc1c00000) == 0x01000000) { /* SETHI */
+							*addr = (insn & 0xffc00000) | (p[1] >> 10); break;
+						} else if ((insn & 0xc1f82000) == 0x80102000) { /* OR X, %LO(i), Y */
+							*addr = (insn & 0xffffe000) | (p[1] & 0x3ff); break;
+						} else if ((insn & 0xc0000000) != 0x40000000) { /* !CALL */
+				bad_f:
+							prom_printf(insn_f, p, addr, insn, addr[1]);
+							prom_halt();
+						}
+					} else if (q[1] != 1)
+						addr[1] = q[1];
+					if (p[2] == BTFIXUPCALL_NORM) {
+				norm_f:	
+						*addr = 0x40000000 | ((p[1] - (unsigned)addr) >> 2);
+						q[1] = 0;
+						break;
+					}
+#ifndef BTFIXUP_OPTIMIZE_NOP
+					goto norm_f;
+#else
+					if (!(addr[1] & 0x80000000)) {
+						if ((addr[1] & 0xc1c00000) != 0x01000000)	/* !SETHI */
+							goto bad_f; /* CALL, Bicc, FBfcc, CBccc are weird in delay slot, aren't they? */
+					} else {
+						if ((addr[1] & 0x01800000) == 0x01800000) {
+							if ((addr[1] & 0x01f80000) == 0x01e80000) {
+								/* RESTORE */
+								goto norm_f; /* It is dangerous to patch that */
+							}
+							goto bad_f;
+						}
+						if ((addr[1] & 0xffffe003) == 0x9e03e000) {
+							/* ADD %O7, XX, %o7 */
+							int displac = (addr[1] << 19);
+							
+							displac = (displac >> 21) + 2;
+							*addr = (0x10800000) + (displac & 0x3fffff);
+							q[1] = addr[1];
+							addr[1] = p[2];
+							break;
+						}
+						if ((addr[1] & 0x201f) == 0x200f || (addr[1] & 0x7c000) == 0x3c000)
+							goto norm_f; /* Someone is playing bad tricks with us: rs1 or rs2 is o7 */
+						if ((addr[1] & 0x3e000000) == 0x1e000000)
+							goto norm_f; /* rd is %o7. We'd better take care. */
+					}
+					if (p[2] == BTFIXUPCALL_NOP) {
+						*addr = 0x01000000;
+						q[1] = 1;
+						break;
+					}
+#ifndef BTFIXUP_OPTIMIZE_OTHER
+					goto norm_f;
+#else
+					if (addr[1] == 0x01000000) {	/* NOP in the delay slot */
+						q[1] = addr[1];
+						*addr = p[2];
+						break;
+					}
+					if ((addr[1] & 0xc0000000) != 0xc0000000) {
+						/* Not a memory operation */
+						if ((addr[1] & 0x30000000) == 0x10000000) {
+							/* Ok, non-memory op with rd %oX */
+							if ((addr[1] & 0x3e000000) == 0x1c000000)
+								goto bad_f; /* Aiee. Someone is playing strange %sp tricks */
+							if ((addr[1] & 0x3e000000) > 0x12000000 ||
+							    ((addr[1] & 0x3e000000) == 0x12000000 &&
+							     p[2] != BTFIXUPCALL_STO1O0 && p[2] != BTFIXUPCALL_SWAPO0O1) ||
+							    ((p[2] & 0xffffe000) == BTFIXUPCALL_RETINT(0))) {
+								/* Nobody uses the result. We can nop it out. */
+								*addr = p[2];
+								q[1] = addr[1];
+								addr[1] = 0x01000000;
+								break;
+							}
+							if ((addr[1] & 0xf1ffffe0) == 0x90100000) {
+								/* MOV %reg, %Ox */
+								if ((addr[1] & 0x3e000000) == 0x10000000 &&
+								    (p[2] & 0x7c000) == 0x20000) {
+								    	/* Ok, it is call xx; mov reg, %o0 and call optimizes
+								    	   to doing something on %o0. Patch the patch. */
+									*addr = (p[2] & ~0x7c000) | ((addr[1] & 0x1f) << 14);
+									q[1] = addr[1];
+									addr[1] = 0x01000000;
+									break;
+								}
+								if ((addr[1] & 0x3e000000) == 0x12000000 &&
+								    p[2] == BTFIXUPCALL_STO1O0) {
+								    	*addr = (p[2] & ~0x3e000000) | ((addr[1] & 0x1f) << 25);
+								    	q[1] = addr[1];
+								    	addr[1] = 0x01000000;
+								    	break;
+								}
+							}
+						}
+					}
+					*addr = addr[1];
+					q[1] = addr[1];
+					addr[1] = p[2];
+					break;
+#endif /* BTFIXUP_OPTIMIZE_OTHER */
+#endif /* BTFIXUP_OPTIMIZE_NOP */
+				case 'b':	/* BLACKBOX */
+					/* Has to be sethi i, xx */
+					if ((insn & 0xc1c00000) != 0x01000000) {
+						prom_printf(insn_b, p, addr, insn);
+						prom_halt();
+					} else {
+						void (*do_fixup)(unsigned *);
+						
+						do_fixup = (void (*)(unsigned *))p[1];
+						do_fixup(addr);
+					}
+					break;
+				case 's':	/* SIMM13 */
+					/* Has to be or %g0, i, xx */
+					if ((insn & 0xc1ffe000) != 0x80102000) {
+						prom_printf(insn_s, p, addr, insn);
+						prom_halt();
+					}
+					set_addr(addr, q[1], fmangled, (insn & 0xffffe000) | (p[1] & 0x1fff));
+					break;
+				case 'h':	/* SETHI */
+					/* Has to be sethi i, xx */
+					if ((insn & 0xc1c00000) != 0x01000000) {
+						prom_printf(insn_h, p, addr, insn);
+						prom_halt();
+					}
+					set_addr(addr, q[1], fmangled, (insn & 0xffc00000) | (p[1] >> 10));
+					break;
+				case 'a':	/* HALF */
+					/* Has to be sethi i, xx or or %g0, i, xx */
+					if ((insn & 0xc1c00000) != 0x01000000 &&
+					    (insn & 0xc1ffe000) != 0x80102000) {
+						prom_printf(insn_a, p, addr, insn);
+						prom_halt();
+					}
+					if (p[1] & 0x3ff)
+						set_addr(addr, q[1], fmangled, 
+							(insn & 0x3e000000) | 0x80102000 | (p[1] & 0x1fff));
+					else
+						set_addr(addr, q[1], fmangled, 
+							(insn & 0x3e000000) | 0x01000000 | (p[1] >> 10));
+					break;
+				case 'i':	/* INT */
+					if ((insn & 0xc1c00000) == 0x01000000) /* %HI */
+						set_addr(addr, q[1], fmangled, (insn & 0xffc00000) | (p[1] >> 10));
+					else if ((insn & 0x80002000) == 0x80002000 &&
+					         (insn & 0x01800000) != 0x01800000) /* %LO */
+						set_addr(addr, q[1], fmangled, (insn & 0xffffe000) | (p[1] & 0x3ff));
+					else {
+						prom_printf(insn_i, p, addr, insn);
+						prom_halt();
+					}
+					break;
+				}
+				count -= 2;
+				q += 2;
+			}
+		} else
+			p = q + count;
+	}
+#ifdef __SMP__
+	flush_cacheall = (void (*)(void))BTFIXUPVAL_CALL(local_flush_cache_all);
+#else
+	flush_cacheall = (void (*)(void))BTFIXUPVAL_CALL(flush_cache_all);
+#endif
+	if (!flush_cacheall) {
+		prom_printf(fca_und);
+		prom_halt();
+	}
+	(*flush_cacheall)();
+}
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/mm/fault.c linux/arch/sparc/mm/fault.c
--- v2.1.96/linux/arch/sparc/mm/fault.c	Thu May 15 16:48:02 1997
+++ linux/arch/sparc/mm/fault.c	Tue Apr 14 17:44:20 1998
@@ -1,4 +1,4 @@
-/* $Id: fault.c,v 1.92 1997/05/15 21:14:21 davem Exp $
+/* $Id: fault.c,v 1.93 1998/03/25 10:43:16 jj Exp $
  * fault.c:  Page fault handlers for the Sparc.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -271,7 +271,7 @@
 #endif
 		tsk->tss.sig_address = address;
 		tsk->tss.sig_desc = SUBSIG_NOMAPPING;
-		send_sig(SIGSEGV, tsk, 1);
+		force_sig(SIGSEGV, tsk);
 		goto out;
 	}
 	unhandled_fault (address, tsk, regs);
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/mm/hypersparc.S linux/arch/sparc/mm/hypersparc.S
--- v2.1.96/linux/arch/sparc/mm/hypersparc.S	Mon Jan 12 15:15:43 1998
+++ linux/arch/sparc/mm/hypersparc.S	Tue Apr 14 17:44:20 1998
@@ -1,4 +1,4 @@
-/* $Id: hypersparc.S,v 1.12 1997/11/27 15:42:30 jj Exp $
+/* $Id: hypersparc.S,v 1.13 1998/02/13 15:35:09 jj Exp $
  * hypersparc.S: High speed Hypersparc mmu/cache operations.
  *
  * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
@@ -301,14 +301,13 @@
 	cmp	%o3, -1
 	be	hypersparc_flush_tlb_range_out
 #endif
-	 srl	%o1, SRMMU_PGDIR_SHIFT, %o1
+	 sethi	%hi(~((1 << SRMMU_PGDIR_SHIFT) - 1)), %o4
 	sta	%o3, [%g1] ASI_M_MMUREGS
-	sll	%o1, SRMMU_PGDIR_SHIFT, %o1
-	sethi	%hi(1 << SRMMU_PGDIR_SHIFT), %o4
+	and	%o1, %o4, %o1
 	add	%o1, 0x200, %o1
 	sta	%g0, [%o1] ASI_M_FLUSH_PROBE
 1:
-	add	%o1, %o4, %o1
+	sub	%o1, %o4, %o1
 	cmp	%o1, %o2
 	blu,a	1b
 	 sta	%g0, [%o1] ASI_M_FLUSH_PROBE
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/mm/init.c linux/arch/sparc/mm/init.c
--- v2.1.96/linux/arch/sparc/mm/init.c	Tue Mar 17 22:18:14 1998
+++ linux/arch/sparc/mm/init.c	Tue Apr 14 17:44:20 1998
@@ -1,8 +1,9 @@
-/*  $Id: init.c,v 1.50 1998/01/10 18:19:42 ecd Exp $
+/*  $Id: init.c,v 1.59 1998/03/27 06:59:57 davem Exp $
  *  linux/arch/sparc/mm/init.c
  *
  *  Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
  *  Copyright (C) 1995 Eddie C. Dost (ecd@skynet.be)
+ *  Copyright (C) 1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
  */
 
 #include <linux/config.h>
@@ -30,11 +31,18 @@
 #include <asm/pgtable.h>
 #include <asm/vaddrs.h>
 
+/* Turn this off if you suspect some place in some physical memory hole
+   might get into page tables (something would be broken very much). */
+   
+#define FREE_UNUSED_MEM_MAP
+
 extern void show_net_buffers(void);
 
 struct sparc_phys_banks sp_banks[SPARC_PHYS_BANKS];
 unsigned long sparc_unmapped_base;
 
+struct pgtable_cache_struct pgt_quicklists;
+
 /* References to section boundaries */
 extern char __init_begin, __init_end, etext;
 
@@ -65,26 +73,38 @@
 
 void show_mem(void)
 {
-	int i,free = 0,total = 0,reserved = 0;
-	int shared = 0;
+	int free = 0,total = 0,reserved = 0;
+	int shared = 0, cached = 0;
+	struct page *page, *end;
 
 	printk("\nMem-info:\n");
 	show_free_areas();
 	printk("Free swap:       %6dkB\n",nr_swap_pages<<(PAGE_SHIFT-10));
-	i = max_mapnr;
-	while (i-- > 0) {
+	for (page = mem_map, end = mem_map + max_mapnr;
+	     page < end; page++) {
+		if (PageSkip(page)) {
+			if (page->next_hash < page)
+				break;
+			page = page->next_hash;
+		}
 		total++;
-		if (PageReserved(mem_map + i))
+		if (PageReserved(page))
 			reserved++;
-		else if (!atomic_read(&mem_map[i].count))
+		else if (PageSwapCache(page))
+			cached++;
+		else if (!atomic_read(&page->count))
 			free++;
 		else
-			shared += atomic_read(&mem_map[i].count) - 1;
+			shared += atomic_read(&page->count) - 1;
 	}
 	printk("%d pages of RAM\n",total);
 	printk("%d free pages\n",free);
 	printk("%d reserved pages\n",reserved);
 	printk("%d pages shared\n",shared);
+	printk("%d pages swap cached\n",cached);
+	printk("%ld page tables cached\n",pgtable_cache_size);
+	if (sparc_cpu_model == sun4m || sparc_cpu_model == sun4d)
+		printk("%ld page dirs cached\n", pgd_cache_size);
 	show_buffers();
 #ifdef CONFIG_NET
 	show_net_buffers();
@@ -128,19 +148,23 @@
 	switch(sparc_cpu_model) {
 	case sun4c:
 	case sun4e:
+	case sun4:
 		start_mem = sun4c_paging_init(start_mem, end_mem);
 		sparc_unmapped_base = 0xe0000000;
+		BTFIXUPSET_SETHI(sparc_unmapped_base, 0xe0000000);
 		break;
 	case sun4m:
 	case sun4d:
 		start_mem = srmmu_paging_init(start_mem, end_mem);
 		sparc_unmapped_base = 0x50000000;
+		BTFIXUPSET_SETHI(sparc_unmapped_base, 0x50000000);
 		break;
 
 	case ap1000:
 #if CONFIG_AP1000
 		start_mem = apmmu_paging_init(start_mem, end_mem);
 		sparc_unmapped_base = 0x50000000;
+		BTFIXUPSET_SETHI(sparc_unmapped_base, 0x50000000);
 		break;
 #endif
 
@@ -168,6 +192,7 @@
 	protection_map[13] = PAGE_READONLY;
 	protection_map[14] = PAGE_SHARED;
 	protection_map[15] = PAGE_SHARED;
+	btfixup();
 	return device_scan(start_mem);
 }
 
@@ -175,7 +200,7 @@
 
 extern void srmmu_frob_mem_map(unsigned long);
 
-int physmem_mapped_contig = 1;
+int physmem_mapped_contig __initdata = 1;
 
 __initfunc(static void taint_real_pages(unsigned long start_mem, unsigned long end_mem))
 {
@@ -210,7 +235,8 @@
 	int codepages = 0;
 	int datapages = 0;
 	int initpages = 0; 
-	unsigned long tmp2, addr;
+	unsigned long addr;
+	struct page *page, *end;
 
 	/* Saves us work later. */
 	memset((void *) ZERO_PAGE, 0, PAGE_SIZE);
@@ -220,33 +246,60 @@
 	high_memory = (void *) end_mem;
 
 	start_mem = PAGE_ALIGN(start_mem);
-	num_physpages = (start_mem - KERNBASE) >> PAGE_SHIFT;
+	num_physpages = 0;
 
 	addr = KERNBASE;
 	while(addr < start_mem) {
 #ifdef CONFIG_BLK_DEV_INITRD
-		if (initrd_below_start_ok && addr >= initrd_start && addr < initrd_end) {
+		if (initrd_below_start_ok && addr >= initrd_start && addr < initrd_end)
 			mem_map[MAP_NR(addr)].flags &= ~(1<<PG_reserved);
-			num_physpages--;
-		} else
+		else
 #endif	
 			mem_map[MAP_NR(addr)].flags |= (1<<PG_reserved);
 		addr += PAGE_SIZE;
 	}
 
 	taint_real_pages(start_mem, end_mem);
+	
+#ifdef FREE_UNUSED_MEM_MAP
+	end = mem_map + max_mapnr;
+	for (page = mem_map; page < end; page++) {
+		if (PageSkip(page)) {
+			unsigned long low, high;
+
+			low = PAGE_ALIGN((unsigned long)(page+1));
+			if (page->next_hash < page)
+				high = ((unsigned long)end) & PAGE_MASK;
+			else
+				high = ((unsigned long)page->next_hash) & PAGE_MASK;
+			while (low < high) {
+				mem_map[MAP_NR(low)].flags &= ~(1<<PG_reserved);
+				low += PAGE_SIZE;
+			}
+		}
+	}
+#endif
+	
 	for (addr = PAGE_OFFSET; addr < end_mem; addr += PAGE_SIZE) {
+		if (PageSkip(mem_map + MAP_NR(addr))) {
+			unsigned long next = mem_map[MAP_NR(addr)].next_hash - mem_map;
+
+			next = (next << PAGE_SHIFT) + PAGE_OFFSET;
+			if (next < addr || next >= end_mem)
+				break;
+			addr = next;
+		}
+		num_physpages++;
 		if(PageReserved(mem_map + MAP_NR(addr))) {
 			if ((addr < (unsigned long) &etext) && (addr >= KERNBASE))
 				codepages++;
-                        else if((addr >= (unsigned long)&__init_begin && addr < (unsigned long)&__init_end))
-                                initpages++;
-                        else if((addr < start_mem) && (addr >= KERNBASE))
+			else if((addr >= (unsigned long)&__init_begin && addr < (unsigned long)&__init_end))
+				initpages++;
+			else if((addr < start_mem) && (addr >= KERNBASE))
 				datapages++;
 			continue;
 		}
 		atomic_set(&mem_map[MAP_NR(addr)].count, 1);
-		num_physpages++;
 #ifdef CONFIG_BLK_DEV_INITRD
 		if (!initrd_start ||
 		    (addr < initrd_start || addr >= initrd_end))
@@ -254,14 +307,12 @@
 			free_page(addr);
 	}
 
-	tmp2 = nr_free_pages << PAGE_SHIFT;
-
-	printk("Memory: %luk available (%dk kernel code, %dk data, %dk init) [%08lx,%08lx]\n",
-	       tmp2 >> 10,
+	printk("Memory: %dk available (%dk kernel code, %dk data, %dk init) [%08lx,%08lx]\n",
+	       nr_free_pages << (PAGE_SHIFT-10),
 	       codepages << (PAGE_SHIFT-10),
 	       datapages << (PAGE_SHIFT-10), 
 	       initpages << (PAGE_SHIFT-10),
-	       PAGE_OFFSET, end_mem);
+	       (unsigned long)PAGE_OFFSET, end_mem);
 
 	freepages.min = nr_free_pages >> 7;
 	if(freepages.min < 16)
@@ -284,20 +335,25 @@
 
 void si_meminfo(struct sysinfo *val)
 {
-	int i;
+	struct page *page, *end;
 
-	i = MAP_NR(high_memory);
 	val->totalram = 0;
 	val->sharedram = 0;
 	val->freeram = nr_free_pages << PAGE_SHIFT;
 	val->bufferram = buffermem;
-	while (i-- > 0)  {
-		if (PageReserved(mem_map + i))
+	for (page = mem_map, end = mem_map + max_mapnr;
+	     page < end; page++) {
+		if (PageSkip(page)) {
+			if (page->next_hash < page)
+				break;
+			page = page->next_hash;
+		}
+		if (PageReserved(page))
 			continue;
 		val->totalram++;
-		if (!atomic_read(&mem_map[i].count))
+		if (!atomic_read(&page->count))
 			continue;
-		val->sharedram += atomic_read(&mem_map[i].count) - 1;
+		val->sharedram += atomic_read(&page->count) - 1;
 	}
 	val->totalram <<= PAGE_SHIFT;
 	val->sharedram <<= PAGE_SHIFT;
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/mm/io-unit.c linux/arch/sparc/mm/io-unit.c
--- v2.1.96/linux/arch/sparc/mm/io-unit.c	Mon Jan 12 15:15:43 1998
+++ linux/arch/sparc/mm/io-unit.c	Tue Apr 14 17:44:20 1998
@@ -1,7 +1,7 @@
-/* $Id: io-unit.c,v 1.5 1997/12/22 16:09:26 jj Exp $
+/* $Id: io-unit.c,v 1.10 1998/03/03 12:31:14 jj Exp $
  * io-unit.c:  IO-UNIT specific routines for memory management.
  *
- * Copyright (C) 1997 Jakub Jelinek    (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1997,1998 Jakub Jelinek    (jj@sunsite.mff.cuni.cz)
  */
  
 #include <linux/config.h>
@@ -13,28 +13,41 @@
 #include <asm/io.h>
 #include <asm/io-unit.h>
 #include <asm/mxcc.h>
+#include <asm/spinlock.h>
+#include <asm/bitops.h>
+
+/* #define IOUNIT_DEBUG */
+#ifdef IOUNIT_DEBUG
+#define IOD(x) printk(x)
+#else
+#define IOD(x) do { } while (0)
+#endif
 
 #define LONG_ALIGN(x) (((x)+(sizeof(long))-1)&~((sizeof(long))-1))
 
 #define IOPERM        (IOUPTE_CACHE | IOUPTE_WRITE | IOUPTE_VALID)
-#define MKIOPTE(phys) ((((phys)>>4) & IOUPTE_PAGE) | IOPERM)
+#define MKIOPTE(phys) __iopte((((phys)>>4) & IOUPTE_PAGE) | IOPERM)
 
-unsigned long sun4d_dma_base;
-unsigned long sun4d_dma_vbase;
-unsigned long sun4d_dma_size;
 __initfunc(unsigned long
 iounit_init(int sbi_node, int io_node, unsigned long memory_start,
 	    unsigned long memory_end, struct linux_sbus *sbus))
 {
 	iopte_t *xpt, *xptend;
-	unsigned long paddr;
 	struct iounit_struct *iounit;
 	struct linux_prom_registers iommu_promregs[PROMREG_MAX];
 	
 	memory_start = LONG_ALIGN(memory_start);
 	iounit = (struct iounit_struct *)memory_start;
-	memory_start += sizeof(struct iounit_struct);
-
+	memory_start = LONG_ALIGN(memory_start + sizeof(struct iounit_struct));
+	
+	memset(iounit, 0, sizeof(*iounit));
+	iounit->limit[0] = IOUNIT_BMAP1_START;
+	iounit->limit[1] = IOUNIT_BMAP2_START;
+	iounit->limit[2] = IOUNIT_BMAPM_START;
+	iounit->limit[3] = IOUNIT_BMAPM_END;
+	iounit->rotor[1] = IOUNIT_BMAP2_START;
+	iounit->rotor[2] = IOUNIT_BMAPM_START;
+	
 	prom_getproperty(sbi_node, "reg", (void *) iommu_promregs,
 			 sizeof(iommu_promregs));
 	prom_apply_generic_ranges(io_node, 0, iommu_promregs, 3);
@@ -46,11 +59,6 @@
 	sbus->iommu = (struct iommu_struct *)iounit;
 	iounit->page_table = xpt;
 	
-	/* Initialize new table. */
-	paddr = IOUNIT_DMA_BASE - sun4d_dma_base;
-	for (xptend = xpt + (sun4d_dma_size >> PAGE_SHIFT);
-	     xpt < xptend; paddr++)
-		*xpt++ = MKIOPTE(paddr);
 	for (xptend = iounit->page_table + (16 * PAGE_SIZE) / sizeof(iopte_t);
 	     xpt < xptend;)
 	     	*xpt++ = 0;
@@ -58,36 +66,108 @@
 	return memory_start;
 }
 
+/* One has to hold iounit->lock to call this */
+static unsigned long iounit_get_area(struct iounit_struct *iounit, unsigned long vaddr, int size)
+{
+	int i, j, k, npages;
+	unsigned long rotor, scan, limit;
+	iopte_t iopte;
+
+        npages = ((vaddr & ~PAGE_MASK) + size + (PAGE_SIZE-1)) >> PAGE_SHIFT;
+
+	/* A tiny bit of magic ingredience :) */
+	switch (npages) {
+	case 1: i = 0x0231; break;
+	case 2: i = 0x0132; break;
+	default: i = 0x0213; break;
+	}
+	
+	IOD(("iounit_get_area(%08lx,%d[%d])=", vaddr, size, npages));
+	
+next:	j = (i & 15);
+	rotor = iounit->rotor[j - 1];
+	limit = iounit->limit[j];
+	scan = rotor;
+nexti:	scan = find_next_zero_bit(iounit->bmap, limit, scan);
+	if (scan + npages > limit) {
+		if (limit != rotor) {
+			limit = rotor;
+			scan = iounit->limit[j - 1];
+			goto nexti;
+		}
+		i >>= 4;
+		if (!(i & 15))
+			panic("iounit_get_area: Couldn't find free iopte slots for (%08lx,%d)\n", vaddr, size);
+		goto next;
+	}
+	for (k = 1, scan++; k < npages; k++)
+		if (test_bit(scan++, iounit->bmap))
+			goto nexti;
+	iounit->rotor[j - 1] = (scan < limit) ? scan : iounit->limit[j - 1];
+	scan -= npages;
+	iopte = MKIOPTE(mmu_v2p(vaddr & PAGE_MASK));
+	vaddr = IOUNIT_DMA_BASE + (scan << PAGE_SHIFT) + (vaddr & ~PAGE_MASK);
+	for (k = 0; k < npages; k++, iopte = __iopte(iopte_val(iopte) + 0x100), scan++) {
+		set_bit(scan, iounit->bmap);
+		iounit->page_table[scan] = iopte;
+	}
+	IOD(("%08lx\n", vaddr));
+	return vaddr;
+}
+
 static __u32 iounit_get_scsi_one(char *vaddr, unsigned long len, struct linux_sbus *sbus)
 {
-	/* Viking MXCC is IO coherent, just need to translate the address to DMA handle */
-#ifdef IOUNIT_DEBUG
-	if ((((unsigned long) vaddr) & PAGE_MASK) < sun4d_dma_vaddr || 
-	    (((unsigned long) vaddr) & PAGE_MASK) + len > sun4d_dma_vbase + sun4d_dma_size)
-			panic("Using non-DMA memory for iounit_get_scsi_one");
-#endif	
-	return (__u32)(sun4d_dma_base + mmu_v2p((long)vaddr));
+	unsigned long ret, flags;
+	struct iounit_struct *iounit = (struct iounit_struct *)sbus->iommu;
+	
+	spin_lock_irqsave(&iounit->lock, flags);
+	ret = iounit_get_area(iounit, (unsigned long)vaddr, len);
+	spin_unlock_irqrestore(&iounit->lock, flags);
+	return ret;
 }
 
 static void iounit_get_scsi_sgl(struct mmu_sglist *sg, int sz, struct linux_sbus *sbus)
 {
-	/* Viking MXCC is IO coherent, just need to translate the address to DMA handle */
+	unsigned long flags;
+	struct iounit_struct *iounit = (struct iounit_struct *)sbus->iommu;
+
+	/* FIXME: Cache some resolved pages - often several sg entries are to the same page */
+	spin_lock_irqsave(&iounit->lock, flags);
 	for (; sz >= 0; sz--) {
-#ifdef IOUNIT_DEBUG
-		unsigned long page = ((unsigned long) sg[sz].addr) & PAGE_MASK;
-		if (page < sun4d_dma_vbase || page + sg[sz].len > sun4d_dma_vbase + sun4d_dma_size)
-			panic("Using non-DMA memory for iounit_get_scsi_sgl");
-#endif	
-		sg[sz].dvma_addr = (__u32) (sun4d_dma_base + mmu_v2p((long)sg[sz].addr));;
+		sg[sz].dvma_addr = iounit_get_area(iounit, (unsigned long)sg[sz].addr, sg[sz].len);
 	}
+	spin_unlock_irqrestore(&iounit->lock, flags);
 }
 
 static void iounit_release_scsi_one(__u32 vaddr, unsigned long len, struct linux_sbus *sbus)
 {
+	unsigned long flags;
+	struct iounit_struct *iounit = (struct iounit_struct *)sbus->iommu;
+	
+	spin_lock_irqsave(&iounit->lock, flags);
+	len = ((vaddr & ~PAGE_MASK) + len + (PAGE_SIZE-1)) >> PAGE_SHIFT;
+	vaddr = (vaddr - IOUNIT_DMA_BASE) >> PAGE_SHIFT;
+	IOD(("iounit_release %08lx-%08lx\n", (long)vaddr, (long)len+vaddr));
+	for (len += vaddr; vaddr < len; vaddr++)
+		clear_bit(vaddr, iounit->bmap);
+	spin_unlock_irqrestore(&iounit->lock, flags);
 }
 
 static void iounit_release_scsi_sgl(struct mmu_sglist *sg, int sz, struct linux_sbus *sbus)
 {
+	unsigned long flags;
+	unsigned long vaddr, len;
+	struct iounit_struct *iounit = (struct iounit_struct *)sbus->iommu;
+	
+	spin_lock_irqsave(&iounit->lock, flags);
+	for (; sz >= 0; sz--) {
+		len = ((sg[sz].dvma_addr & ~PAGE_MASK) + sg[sz].len + (PAGE_SIZE-1)) >> PAGE_SHIFT;
+		vaddr = (sg[sz].dvma_addr - IOUNIT_DMA_BASE) >> PAGE_SHIFT;
+		IOD(("iounit_release %08lx-%08lx\n", (long)vaddr, (long)len+vaddr));
+		for (len += vaddr; vaddr < len; vaddr++)
+			clear_bit(vaddr, iounit->bmap);
+	}
+	spin_unlock_irqrestore(&iounit->lock, flags);
 }
 
 #ifdef CONFIG_SBUS
@@ -135,24 +215,26 @@
 
 static char *iounit_lockarea(char *vaddr, unsigned long len)
 {
+/* FIXME: Write this */
 	return vaddr;
 }
 
 static void iounit_unlockarea(char *vaddr, unsigned long len)
 {
+/* FIXME: Write this */
 }
 
 __initfunc(void ld_mmu_iounit(void))
 {
-	mmu_lockarea = iounit_lockarea;
-	mmu_unlockarea = iounit_unlockarea;
+	BTFIXUPSET_CALL(mmu_lockarea, iounit_lockarea, BTFIXUPCALL_RETO0);
+	BTFIXUPSET_CALL(mmu_unlockarea, iounit_unlockarea, BTFIXUPCALL_NOP);
 
-	mmu_get_scsi_one = iounit_get_scsi_one;
-	mmu_get_scsi_sgl = iounit_get_scsi_sgl;
-	mmu_release_scsi_one = iounit_release_scsi_one;
-	mmu_release_scsi_sgl = iounit_release_scsi_sgl;
+	BTFIXUPSET_CALL(mmu_get_scsi_one, iounit_get_scsi_one, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(mmu_get_scsi_sgl, iounit_get_scsi_sgl, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(mmu_release_scsi_one, iounit_release_scsi_one, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(mmu_release_scsi_sgl, iounit_release_scsi_sgl, BTFIXUPCALL_NORM);
 
 #ifdef CONFIG_SBUS
-	mmu_map_dma_area = iounit_map_dma_area;
+	BTFIXUPSET_CALL(mmu_map_dma_area, iounit_map_dma_area, BTFIXUPCALL_NORM);
 #endif
 }
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/mm/iommu.c linux/arch/sparc/mm/iommu.c
--- v2.1.96/linux/arch/sparc/mm/iommu.c	Mon Jan 12 15:15:43 1998
+++ linux/arch/sparc/mm/iommu.c	Tue Apr 14 17:44:20 1998
@@ -1,10 +1,10 @@
-/* $Id: iommu.c,v 1.4 1997/11/21 17:31:31 jj Exp $
+/* $Id: iommu.c,v 1.7 1998/02/22 10:32:26 ecd Exp $
  * iommu.c:  IOMMU specific routines for memory management.
  *
  * Copyright (C) 1995 David S. Miller  (davem@caip.rutgers.edu)
  * Copyright (C) 1995 Peter A. Zaitcev (zaitcev@ithil.mcst.ru)
  * Copyright (C) 1996 Eddie C. Dost    (ecd@skynet.be)
- * Copyright (C) 1997 Jakub Jelinek    (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1997,1998 Jakub Jelinek    (jj@sunsite.mff.cuni.cz)
  */
  
 #include <linux/config.h>
@@ -18,8 +18,10 @@
 
 /* srmmu.c */
 extern int viking_mxcc_present;
-extern void (*flush_page_for_dma)(unsigned long page);
+BTFIXUPDEF_CALL(void, flush_page_for_dma, unsigned long)
+#define flush_page_for_dma(page) BTFIXUP_CALL(flush_page_for_dma)(page)
 extern int flush_page_for_dma_global;
+static int viking_flush = 0;
 /* viking.S */
 extern void viking_flush_page(unsigned long page);
 extern void viking_mxcc_flush_page(unsigned long page);
@@ -113,7 +115,7 @@
 			viking_mxcc_flush_page(start);
 			start += PAGE_SIZE;
 		}
-	} else if(flush_page_for_dma == viking_flush_page) {
+	} else if (viking_flush) {
 		unsigned long start = (unsigned long) iommu->page_table;
 		unsigned long end = (start + ptsize);
 		while(start < end) {
@@ -199,7 +201,7 @@
 	pgprot_t dvma_prot;
 	struct iommu_struct *iommu = SBus_chain->iommu;
 	iopte_t *iopte = iommu->page_table;
-	iopte_t *iopte_first = iopte;
+	iopte_t *first;
 
 	if(viking_mxcc_present)
 		dvma_prot = __pgprot(SRMMU_CACHE | SRMMU_ET_PTE | SRMMU_PRIV);
@@ -207,6 +209,7 @@
 		dvma_prot = __pgprot(SRMMU_ET_PTE | SRMMU_PRIV);
 
 	iopte += ((addr - iommu->start) >> PAGE_SHIFT);
+	first = iopte;
 	end = PAGE_ALIGN((addr + len));
 	while(addr < end) {
 		page = get_free_page(GFP_KERNEL);
@@ -223,21 +226,20 @@
 			ptep = pte_offset(pmdp, addr);
 
 			set_pte(ptep, pte_val(mk_pte(page, dvma_prot)));
-
 			iopte_val(*iopte++) = MKIOPTE(mmu_v2p(page));
 		}
 		addr += PAGE_SIZE;
 	}
 	flush_cache_all();
 	if(viking_mxcc_present) {
-		unsigned long start = ((unsigned long) iopte_first) & PAGE_MASK;
+		unsigned long start = ((unsigned long) first) & PAGE_MASK;
 		unsigned long end = PAGE_ALIGN(((unsigned long) iopte));
 		while(start < end) {
 			viking_mxcc_flush_page(start);
 			start += PAGE_SIZE;
 		}
-	} else if(flush_page_for_dma == viking_flush_page) {
-		unsigned long start = ((unsigned long) iopte_first) & PAGE_MASK;
+	} else if(viking_flush) {
+		unsigned long start = ((unsigned long) first) & PAGE_MASK;
 		unsigned long end = PAGE_ALIGN(((unsigned long) iopte));
 		while(start < end) {
 			viking_flush_page(start);
@@ -260,25 +262,26 @@
 
 __initfunc(void ld_mmu_iommu(void))
 {
-	mmu_lockarea = iommu_lockarea;
-	mmu_unlockarea = iommu_unlockarea;
+	viking_flush = (BTFIXUPVAL_CALL(flush_page_for_dma) == (unsigned long)viking_flush_page);
+	BTFIXUPSET_CALL(mmu_lockarea, iommu_lockarea, BTFIXUPCALL_RETO0);
+	BTFIXUPSET_CALL(mmu_unlockarea, iommu_unlockarea, BTFIXUPCALL_NOP);
 
-	if (!flush_page_for_dma) {
+	if (!BTFIXUPVAL_CALL(flush_page_for_dma)) {
 		/* IO coherent chip */
-		mmu_get_scsi_one = iommu_get_scsi_one_noflush;
-		mmu_get_scsi_sgl = iommu_get_scsi_sgl_noflush;
+		BTFIXUPSET_CALL(mmu_get_scsi_one, iommu_get_scsi_one_noflush, BTFIXUPCALL_RETO0);
+		BTFIXUPSET_CALL(mmu_get_scsi_sgl, iommu_get_scsi_sgl_noflush, BTFIXUPCALL_NORM);
 	} else if (flush_page_for_dma_global) {
 		/* flush_page_for_dma flushes everything, no matter of what page is it */
-		mmu_get_scsi_one = iommu_get_scsi_one_gflush;
-		mmu_get_scsi_sgl = iommu_get_scsi_sgl_gflush;
+		BTFIXUPSET_CALL(mmu_get_scsi_one, iommu_get_scsi_one_gflush, BTFIXUPCALL_NORM);
+		BTFIXUPSET_CALL(mmu_get_scsi_sgl, iommu_get_scsi_sgl_gflush, BTFIXUPCALL_NORM);
 	} else {
-		mmu_get_scsi_one = iommu_get_scsi_one_pflush;
-		mmu_get_scsi_sgl = iommu_get_scsi_sgl_pflush;
+		BTFIXUPSET_CALL(mmu_get_scsi_one, iommu_get_scsi_one_pflush, BTFIXUPCALL_NORM);
+		BTFIXUPSET_CALL(mmu_get_scsi_sgl, iommu_get_scsi_sgl_pflush, BTFIXUPCALL_NORM);
 	}
-	mmu_release_scsi_one = iommu_release_scsi_one;
-	mmu_release_scsi_sgl = iommu_release_scsi_sgl;
+	BTFIXUPSET_CALL(mmu_release_scsi_one, iommu_release_scsi_one, BTFIXUPCALL_NOP);
+	BTFIXUPSET_CALL(mmu_release_scsi_sgl, iommu_release_scsi_sgl, BTFIXUPCALL_NOP);
 
 #ifdef CONFIG_SBUS
-	mmu_map_dma_area = iommu_map_dma_area;
+	BTFIXUPSET_CALL(mmu_map_dma_area, iommu_map_dma_area, BTFIXUPCALL_NORM);
 #endif
 }
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/mm/loadmmu.c linux/arch/sparc/mm/loadmmu.c
--- v2.1.96/linux/arch/sparc/mm/loadmmu.c	Mon Apr 14 16:28:08 1997
+++ linux/arch/sparc/mm/loadmmu.c	Tue Apr 14 17:44:20 1998
@@ -1,9 +1,10 @@
-/* $Id: loadmmu.c,v 1.46 1997/04/10 05:12:51 davem Exp $
+/* $Id: loadmmu.c,v 1.50 1998/02/05 14:19:02 jj Exp $
  * loadmmu.c:  This code loads up all the mm function pointers once the
  *             machine type has been determined.  It also sets the static
  *             mmu values such as PAGE_NONE, etc.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
  */
 
 #include <linux/kernel.h>
@@ -16,6 +17,7 @@
 #include <asm/pgtable.h>
 #include <asm/a.out.h>
 #include <asm/mmu_context.h>
+#include <asm/oplib.h>
 
 unsigned long page_offset = 0xf0000000;
 unsigned long stack_top = 0xf0000000 - PAGE_SIZE;
@@ -24,132 +26,8 @@
 struct ctx_list ctx_free;
 struct ctx_list ctx_used;
 
-unsigned long (*alloc_kernel_stack)(struct task_struct *tsk);
-void (*free_kernel_stack)(unsigned long stack);
-struct task_struct *(*alloc_task_struct)(void);
-void (*free_task_struct)(struct task_struct *tsk);
-
-void (*quick_kernel_fault)(unsigned long);
-
-void (*init_new_context)(struct mm_struct *mm);
-void (*destroy_context)(struct mm_struct *mm);
-
-/* translate between physical and virtual addresses */
-unsigned long (*mmu_v2p)(unsigned long);
-unsigned long (*mmu_p2v)(unsigned long);
-
-char *(*mmu_lockarea)(char *, unsigned long);
-void  (*mmu_unlockarea)(char *, unsigned long);
-
-__u32 (*mmu_get_scsi_one)(char *, unsigned long, struct linux_sbus *sbus);
-void  (*mmu_get_scsi_sgl)(struct mmu_sglist *, int, struct linux_sbus *sbus);
-void  (*mmu_release_scsi_one)(__u32, unsigned long, struct linux_sbus *sbus);
-void  (*mmu_release_scsi_sgl)(struct mmu_sglist *, int, struct linux_sbus *sbus);
-
-void  (*mmu_map_dma_area)(unsigned long addr, int len);
-
-void (*update_mmu_cache)(struct vm_area_struct *vma, unsigned long address, pte_t pte);
-
-#ifdef __SMP__
-void (*local_flush_cache_all)(void);
-void (*local_flush_cache_mm)(struct mm_struct *);
-void (*local_flush_cache_range)(struct mm_struct *, unsigned long start,
-				unsigned long end);
-void (*local_flush_cache_page)(struct vm_area_struct *, unsigned long address);
-
-void (*local_flush_tlb_all)(void);
-void (*local_flush_tlb_mm)(struct mm_struct *);
-void (*local_flush_tlb_range)(struct mm_struct *, unsigned long start,
-			      unsigned long end);
-void (*local_flush_tlb_page)(struct vm_area_struct *, unsigned long address);
-void (*local_flush_page_to_ram)(unsigned long address);
-void (*local_flush_sig_insns)(struct mm_struct *mm, unsigned long insn_addr);
-#endif
-
-void (*flush_cache_all)(void);
-void (*flush_cache_mm)(struct mm_struct *);
-void (*flush_cache_range)(struct mm_struct *, unsigned long start,
-			  unsigned long end);
-void (*flush_cache_page)(struct vm_area_struct *, unsigned long address);
-
-void (*flush_tlb_all)(void);
-void (*flush_tlb_mm)(struct mm_struct *);
-void (*flush_tlb_range)(struct mm_struct *, unsigned long start,
-			unsigned long end);
-void (*flush_tlb_page)(struct vm_area_struct *, unsigned long address);
-
-void (*flush_page_to_ram)(unsigned long page);
-
-void (*flush_sig_insns)(struct mm_struct *mm, unsigned long insn_addr);
-
-void (*set_pte)(pte_t *pteptr, pte_t pteval);
-
-unsigned int pmd_shift, pmd_size, pmd_mask;
-unsigned int (*pmd_align)(unsigned int);
-unsigned int pgdir_shift, pgdir_size, pgdir_mask;
-unsigned int (*pgdir_align)(unsigned int);
-unsigned int ptrs_per_pte, ptrs_per_pmd, ptrs_per_pgd;
 unsigned int pg_iobits;
 
-pgprot_t page_none, page_shared, page_copy, page_readonly, page_kernel;
-
-unsigned long (*pte_page)(pte_t);
-unsigned long (*pmd_page)(pmd_t);
-unsigned long (*pgd_page)(pgd_t);
-
-void (*sparc_update_rootmmu_dir)(struct task_struct *, pgd_t *pgdir);
-unsigned long (*(vmalloc_start))(void);
-void (*switch_to_context)(struct task_struct *tsk);
-
-int (*pte_none)(pte_t);
-int (*pte_present)(pte_t);
-void (*pte_clear)(pte_t *);
-
-int (*pmd_none)(pmd_t);
-int (*pmd_bad)(pmd_t);
-int (*pmd_present)(pmd_t);
-void (*pmd_clear)(pmd_t *);
-
-int (*pgd_none)(pgd_t);
-int (*pgd_bad)(pgd_t);
-int (*pgd_present)(pgd_t);
-void (*pgd_clear)(pgd_t *);
-
-pte_t (*mk_pte)(unsigned long, pgprot_t);
-pte_t (*mk_pte_phys)(unsigned long, pgprot_t);
-pte_t (*mk_pte_io)(unsigned long, pgprot_t, int);
-void (*pgd_set)(pgd_t *, pmd_t *);
-pte_t (*pte_modify)(pte_t, pgprot_t);
-pgd_t * (*pgd_offset)(struct mm_struct *, unsigned long);
-pmd_t * (*pmd_offset)(pgd_t *, unsigned long);
-pte_t * (*pte_offset)(pmd_t *, unsigned long);
-void (*pte_free_kernel)(pte_t *);
-pte_t * (*pte_alloc_kernel)(pmd_t *, unsigned long);
-
-void (*pmd_free_kernel)(pmd_t *);
-pmd_t * (*pmd_alloc_kernel)(pgd_t *, unsigned long);
-void (*pte_free)(pte_t *);
-pte_t * (*pte_alloc)(pmd_t *, unsigned long);
-
-void (*pmd_free)(pmd_t *);
-pmd_t * (*pmd_alloc)(pgd_t *, unsigned long);
-void (*pgd_free)(pgd_t *);
-
-pgd_t * (*pgd_alloc)(void);
-
-int (*pte_write)(pte_t);
-int (*pte_dirty)(pte_t);
-int (*pte_young)(pte_t);
-
-pte_t (*pte_wrprotect)(pte_t);
-pte_t (*pte_mkclean)(pte_t);
-pte_t (*pte_mkold)(pte_t);
-pte_t (*pte_mkwrite)(pte_t);
-pte_t (*pte_mkdirty)(pte_t);
-pte_t (*pte_mkyoung)(pte_t);
-
-char *(*mmu_info)(void);
-
 extern void ld_mmu_sun4c(void);
 extern void ld_mmu_srmmu(void);
 
@@ -157,6 +35,7 @@
 {
 	switch(sparc_cpu_model) {
 	case sun4c:
+	case sun4:
 		ld_mmu_sun4c();
 		break;
 	case sun4m:
@@ -169,9 +48,8 @@
 		break;
 #endif
 	default:
-		printk("load_mmu:MMU support not available for this architecture\n");
-		printk("load_mmu:sparc_cpu_model = %d\n", (int) sparc_cpu_model);
-		printk("load_mmu:Halting...\n");
-		panic("load_mmu()");
+		prom_printf("load_mmu: %d unsupported\n", (int)sparc_cpu_model);
+		prom_halt();
 	}
+	btfixup();
 }
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/mm/nosrmmu.c linux/arch/sparc/mm/nosrmmu.c
--- v2.1.96/linux/arch/sparc/mm/nosrmmu.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc/mm/nosrmmu.c	Tue Apr 14 17:44:20 1998
@@ -0,0 +1,50 @@
+/* $Id: nosrmmu.c,v 1.1 1998/03/09 14:04:15 jj Exp $
+ * nosrmmu.c: This file is a bunch of dummies for sun4 compiles, 
+ *         so that it does not need srmmu and avoid ifdefs.
+ *
+ * Copyright (C) 1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ */
+
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/init.h>
+#include <asm/mbus.h>
+
+static char shouldnothappen[] __initdata = "SUN4 kernel can only run on SUN4\n";
+
+enum mbus_module srmmu_modtype;
+
+__initfunc(static void should_not_happen(void))
+{
+	prom_printf(shouldnothappen);
+	prom_halt();
+}
+
+__initfunc(void srmmu_frob_mem_map(unsigned long start_mem))
+{
+	should_not_happen();
+}
+
+__initfunc(unsigned long srmmu_paging_init(unsigned long start_mem, unsigned long end_mem))
+{
+	should_not_happen();
+	return 0;
+}
+
+__initfunc(void ld_mmu_srmmu(void))
+{
+	should_not_happen();
+}
+
+void srmmu_mapioaddr(unsigned long physaddr, unsigned long virt_addr, int bus_type, int rdonly)
+{
+}
+
+void srmmu_unmapioaddr(unsigned long virt_addr)
+{
+}
+
+__initfunc(void srmmu_end_memory(unsigned long memory_size, unsigned long *mem_end_p))
+{
+	return 0;
+}
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/mm/nosun4c.c linux/arch/sparc/mm/nosun4c.c
--- v2.1.96/linux/arch/sparc/mm/nosun4c.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc/mm/nosun4c.c	Tue Apr 14 17:44:20 1998
@@ -0,0 +1,77 @@
+/* $Id: nosun4c.c,v 1.1 1998/03/09 14:04:16 jj Exp $
+ * nosun4c.c: This file is a bunch of dummies for SMP compiles, 
+ *         so that it does not need sun4c and avoid ifdefs.
+ *
+ * Copyright (C) 1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ */
+
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/init.h>
+#include <asm/pgtable.h>
+
+static char shouldnothappen[] __initdata = "32bit SMP kernel only supports sun4m and sun4d\n";
+
+/* Dummies */
+struct sun4c_mmu_ring {
+	unsigned long xxx1[3];
+	unsigned char xxx2[2];
+	int xxx3;
+};
+struct sun4c_mmu_ring sun4c_kernel_ring;
+struct sun4c_mmu_ring sun4c_kfree_ring;
+unsigned long sun4c_kernel_faults;
+unsigned long *sun4c_memerr_reg;
+
+__initfunc(static void should_not_happen(void))
+{
+	prom_printf(shouldnothappen);
+	prom_halt();
+}
+
+__initfunc(unsigned long sun4c_paging_init(unsigned long start_mem, unsigned long end_mem))
+{
+	should_not_happen();
+	return 0;
+}
+
+__initfunc(void ld_mmu_sun4c(void))
+{
+	should_not_happen();
+}
+
+void sun4c_mapioaddr(unsigned long physaddr, unsigned long virt_addr, int bus_type, int rdonly)
+{
+}
+
+void sun4c_unmapioaddr(unsigned long virt_addr)
+{
+}
+
+void sun4c_complete_all_stores(void)
+{
+}
+
+pgd_t *sun4c_pgd_offset(struct mm_struct * mm, unsigned long address)
+{
+	return NULL;
+}
+
+pte_t *sun4c_pte_offset(pmd_t * dir, unsigned long address)
+{
+	return NULL;
+}
+
+void sun4c_update_mmu_cache(struct vm_area_struct *vma, unsigned long address, pte_t pte)
+{
+}
+
+__initfunc(void sun4c_probe_vac(void))
+{
+	should_not_happen();
+}
+
+__initfunc(void sun4c_probe_memerr_reg(void))
+{
+	should_not_happen();
+}
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/mm/srmmu.c linux/arch/sparc/mm/srmmu.c
--- v2.1.96/linux/arch/sparc/mm/srmmu.c	Tue Mar 10 10:03:30 1998
+++ linux/arch/sparc/mm/srmmu.c	Tue Apr 14 17:44:20 1998
@@ -1,10 +1,10 @@
-/* $Id: srmmu.c,v 1.156 1997/11/28 14:23:42 jj Exp $
+/* $Id: srmmu.c,v 1.170 1998/03/09 14:04:01 jj Exp $
  * srmmu.c:  SRMMU specific routines for memory management.
  *
  * Copyright (C) 1995 David S. Miller  (davem@caip.rutgers.edu)
  * Copyright (C) 1995 Peter A. Zaitcev (zaitcev@ithil.mcst.ru)
  * Copyright (C) 1996 Eddie C. Dost    (ecd@skynet.be)
- * Copyright (C) 1997 Jakub Jelinek    (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
  */
 
 #include <linux/config.h>
@@ -30,6 +30,7 @@
 #include <asm/a.out.h>
 #include <asm/mmu_context.h>
 #include <asm/io-unit.h>
+#include <asm/spinlock.h>
 
 /* Now the cpu specific definitions. */
 #include <asm/viking.h>
@@ -39,6 +40,11 @@
 #include <asm/swift.h>
 #include <asm/turbosparc.h>
 
+#include <asm/btfixup.h>
+
+/* #define DEBUG_MAP_KERNEL */
+/* #define PAGESKIP_DEBUG */
+
 enum mbus_module srmmu_modtype;
 unsigned int hwbug_bitmask;
 int vac_cache_size;
@@ -47,10 +53,6 @@
 
 extern unsigned long sparc_iobase_vaddr;
 
-extern unsigned long sun4d_dma_base;
-extern unsigned long sun4d_dma_size;
-extern unsigned long sun4d_dma_vbase;
-
 #ifdef __SMP__
 #define FLUSH_BEGIN(mm)
 #define FLUSH_END
@@ -60,16 +62,24 @@
 #endif
 
 static int phys_mem_contig;
-long page_contig_offset;
+BTFIXUPDEF_SETHI(page_contig_offset)
+
+BTFIXUPDEF_CALL(void, ctxd_set, ctxd_t *, pgd_t *)
+BTFIXUPDEF_CALL(void, pmd_set, pmd_t *, pte_t *)
+
+#define ctxd_set(ctxp,pgdp) BTFIXUP_CALL(ctxd_set)(ctxp,pgdp)
+#define pmd_set(pmdp,ptep) BTFIXUP_CALL(pmd_set)(pmdp,ptep)
 
-static void (*ctxd_set)(ctxd_t *ctxp, pgd_t *pgdp);
-static void (*pmd_set)(pmd_t *pmdp, pte_t *ptep);
+BTFIXUPDEF_CALL(void, flush_page_for_dma, unsigned long)
+BTFIXUPDEF_CALL(void, flush_chunk, unsigned long)
 
-void (*flush_page_for_dma)(unsigned long page);
+#define flush_page_for_dma(page) BTFIXUP_CALL(flush_page_for_dma)(page)
 int flush_page_for_dma_global = 1;
-static void (*flush_chunk)(unsigned long chunk);
+#define flush_chunk(chunk) BTFIXUP_CALL(flush_chunk)(chunk)
 #ifdef __SMP__
-static void (*local_flush_page_for_dma)(unsigned long page);
+BTFIXUPDEF_CALL(void, local_flush_page_for_dma, unsigned long)
+
+#define local_flush_page_for_dma(page) BTFIXUP_CALL(local_flush_page_for_dma)(page)
 #endif
 
 static struct srmmu_stats {
@@ -79,7 +89,7 @@
 	int invmm;
 } module_stats;
 
-static char *srmmu_name;
+char *srmmu_name;
 
 ctxd_t *srmmu_ctx_table_phys;
 ctxd_t *srmmu_context_table;
@@ -96,8 +106,8 @@
 #define SRMMU_HASHSZ	256
 
 /* Not static, viking.S uses it. */
-struct srmmu_trans *srmmu_v2p_hash[SRMMU_HASHSZ];
-static struct srmmu_trans *srmmu_p2v_hash[SRMMU_HASHSZ];
+unsigned long srmmu_v2p_hash[SRMMU_HASHSZ];
+static unsigned long srmmu_p2v_hash[SRMMU_HASHSZ];
 
 #define srmmu_ahashfn(addr)	((addr) >> 24)
 
@@ -111,20 +121,17 @@
  */
 static inline unsigned long srmmu_v2p(unsigned long vaddr)
 {
-	struct srmmu_trans *tp = srmmu_v2p_hash[srmmu_ahashfn(vaddr)];
-
-	if(tp)
-		return (vaddr - tp->vbase + tp->pbase);
-	else
-		return 0xffffffffUL;
+	unsigned long off = srmmu_v2p_hash[srmmu_ahashfn(vaddr)];
+	
+	return (vaddr + off);
 }
 
 static inline unsigned long srmmu_p2v(unsigned long paddr)
 {
-	struct srmmu_trans *tp = srmmu_p2v_hash[srmmu_ahashfn(paddr)];
-
-	if(tp)
-		return (paddr - tp->pbase + tp->vbase);
+	unsigned long off = srmmu_p2v_hash[srmmu_ahashfn(paddr)];
+	
+	if (off != 0xffffffffUL)
+		return (paddr - off);
 	else
 		return 0xffffffffUL;
 }
@@ -132,16 +139,47 @@
 /* Physical memory on most SS1000/SC2000 can be contiguous, so we handle that case
  * as a special case to make things faster.
  */
+/* FIXME: gcc is stupid here and generates very very bad code in this
+ * heavily used routine. So we help it a bit. */
 static inline unsigned long srmmu_c_v2p(unsigned long vaddr)
 {
+#if KERNBASE != 0xf0000000
 	if (vaddr >= KERNBASE) return vaddr - KERNBASE;
-	return (vaddr - page_contig_offset);
+	return vaddr - BTFIXUP_SETHI(page_contig_offset);
+#else
+	register unsigned long kernbase;
+	
+	__asm__ ("sethi %%hi(0xf0000000), %0" : "=r"(kernbase));
+	return vaddr - ((vaddr >= kernbase) ? kernbase : BTFIXUP_SETHI(page_contig_offset));
+#endif
 }
 
 static inline unsigned long srmmu_c_p2v(unsigned long paddr)
 {
+#if KERNBASE != 0xf0000000
 	if (paddr < (0xfd000000 - KERNBASE)) return paddr + KERNBASE;
-	return (paddr + page_contig_offset);
+	return (paddr + BTFIXUP_SETHI(page_contig_offset));
+#else
+	register unsigned long kernbase;
+	register unsigned long limit;
+	
+	__asm__ ("sethi %%hi(0x0d000000), %0" : "=r"(limit));
+	__asm__ ("sethi %%hi(0xf0000000), %0" : "=r"(kernbase));
+
+	return paddr + ((paddr < limit) ? kernbase : BTFIXUP_SETHI(page_contig_offset));
+#endif
+}
+
+/* On boxes where there is no lots_of_ram, KERNBASE is mapped to PA<0> and highest
+   PA is below 0x0d000000, we can optimize even more :) */
+static inline unsigned long srmmu_s_v2p(unsigned long vaddr)
+{
+	return vaddr - PAGE_OFFSET;
+}
+
+static inline unsigned long srmmu_s_p2v(unsigned long paddr)
+{
+	return paddr + PAGE_OFFSET;
 }
 
 /* In general all page table modifications should use the V8 atomic
@@ -157,19 +195,43 @@
 /* Functions really use this, not srmmu_swap directly. */
 #define srmmu_set_entry(ptr, newentry) srmmu_swap((unsigned long *) (ptr), (newentry))
 
+#ifdef PAGESKIP_DEBUG
+#define PGSKIP_DEBUG(from,to) prom_printf("PG_skip %ld->%ld\n", (long)(from), (long)(to)); printk("PG_skip %ld->%ld\n", (long)(from), (long)(to))
+#else
+#define PGSKIP_DEBUG(from,to) do { } while (0)
+#endif
+
 __initfunc(void srmmu_frob_mem_map(unsigned long start_mem))
 {
-	unsigned long bank_start, bank_end;
+	unsigned long bank_start, bank_end = 0;
 	unsigned long addr;
 	int i;
 
 	/* First, mark all pages as invalid. */
 	for(addr = PAGE_OFFSET; MAP_NR(addr) < max_mapnr; addr += PAGE_SIZE)
 		mem_map[MAP_NR(addr)].flags |= (1<<PG_reserved);
+		
+	/* Next, pg[0-3] is sun4c cruft, so we can free it... */
+	mem_map[MAP_NR(pg0)].flags &= ~(1<<PG_reserved);
+	mem_map[MAP_NR(pg1)].flags &= ~(1<<PG_reserved);
+	mem_map[MAP_NR(pg2)].flags &= ~(1<<PG_reserved);
+	mem_map[MAP_NR(pg3)].flags &= ~(1<<PG_reserved);
 
 	start_mem = PAGE_ALIGN(start_mem);
 	for(i = 0; srmmu_map[i].size; i++) {
 		bank_start = srmmu_map[i].vbase;
+		
+		if (i && bank_start - bank_end > 2 * PAGE_SIZE) {
+			mem_map[MAP_NR(bank_end)].flags |= (1<<PG_skip);
+			mem_map[MAP_NR(bank_end)].next_hash = mem_map + MAP_NR(bank_start);
+			PGSKIP_DEBUG(MAP_NR(bank_end), MAP_NR(bank_start));
+			if (bank_end > KERNBASE && bank_start < KERNBASE) {
+				mem_map[0].flags |= (1<<PG_skip);
+				mem_map[0].next_hash = mem_map + MAP_NR(bank_start);
+				PGSKIP_DEBUG(0, MAP_NR(bank_start));
+			}
+		}
+		
 		bank_end = bank_start + srmmu_map[i].size;
 		while(bank_start < bank_end) {
 			if((bank_start >= KERNBASE) &&
@@ -180,23 +242,28 @@
 			mem_map[MAP_NR(bank_start)].flags &= ~(1<<PG_reserved);
 			bank_start += PAGE_SIZE;
 		}
+		
+		if (bank_end == 0xfd000000)
+			bank_end = PAGE_OFFSET;
 	}
-	if (sparc_cpu_model == sun4d) {
-		for (addr = PAGE_OFFSET; MAP_NR(addr) < max_mapnr; addr += PAGE_SIZE)
-			if (addr < sun4d_dma_vbase || addr >= sun4d_dma_vbase + sun4d_dma_size)
-				clear_bit(PG_DMA, &mem_map[MAP_NR(addr)].flags);
+	
+	if (bank_end < KERNBASE) {
+		mem_map[MAP_NR(bank_end)].flags |= (1<<PG_skip);
+		mem_map[MAP_NR(bank_end)].next_hash = mem_map + MAP_NR(KERNBASE);
+		PGSKIP_DEBUG(MAP_NR(bank_end), MAP_NR(KERNBASE));
+	} else if (MAP_NR(bank_end) < max_mapnr) {
+		mem_map[MAP_NR(bank_end)].flags |= (1<<PG_skip);
+		if (mem_map[0].flags & (1 << PG_skip)) {
+			mem_map[MAP_NR(bank_end)].next_hash = mem_map[0].next_hash;
+			PGSKIP_DEBUG(MAP_NR(bank_end), mem_map[0].next_hash - mem_map);
+		} else {
+			mem_map[MAP_NR(bank_end)].next_hash = mem_map;
+			PGSKIP_DEBUG(MAP_NR(bank_end), 0);
+		}
 	}
 }
 
 /* The very generic SRMMU page table operations. */
-static unsigned int srmmu_pmd_align(unsigned int addr) { return SRMMU_PMD_ALIGN(addr); }
-static unsigned int srmmu_pgdir_align(unsigned int addr) { return SRMMU_PGDIR_ALIGN(addr); }
-
-static unsigned long srmmu_vmalloc_start(void)
-{
-	return SRMMU_VMALLOC_START;
-}
-
 static inline int srmmu_device_memory(unsigned long x) 
 {
 	return ((x & 0xF0000000) != 0);
@@ -220,44 +287,53 @@
 static unsigned long srmmu_c_pte_page(pte_t pte)
 { return srmmu_device_memory(pte_val(pte))?~0:srmmu_c_p2v((pte_val(pte) & SRMMU_PTE_PMASK) << 4); }
 
-static int srmmu_pte_none(pte_t pte)          
+static unsigned long srmmu_s_pgd_page(pgd_t pgd)
+{ return srmmu_device_memory(pgd_val(pgd))?~0:srmmu_s_p2v((pgd_val(pgd) & SRMMU_PTD_PMASK) << 4); }
+
+static unsigned long srmmu_s_pmd_page(pmd_t pmd)
+{ return srmmu_device_memory(pmd_val(pmd))?~0:srmmu_s_p2v((pmd_val(pmd) & SRMMU_PTD_PMASK) << 4); }
+
+static unsigned long srmmu_s_pte_page(pte_t pte)
+{ return srmmu_device_memory(pte_val(pte))?~0:srmmu_s_p2v((pte_val(pte) & SRMMU_PTE_PMASK) << 4); }
+
+static inline int srmmu_pte_none(pte_t pte)
 { return !(pte_val(pte) & 0xFFFFFFF); }
-static int srmmu_pte_present(pte_t pte)
+static inline int srmmu_pte_present(pte_t pte)
 { return ((pte_val(pte) & SRMMU_ET_MASK) == SRMMU_ET_PTE); }
 
-static void srmmu_pte_clear(pte_t *ptep)      { set_pte(ptep, __pte(0)); }
+static inline void srmmu_pte_clear(pte_t *ptep)      { set_pte(ptep, __pte(0)); }
 
-static int srmmu_pmd_none(pmd_t pmd)          
+static inline int srmmu_pmd_none(pmd_t pmd)
 { return !(pmd_val(pmd) & 0xFFFFFFF); }
-static int srmmu_pmd_bad(pmd_t pmd)
+static inline int srmmu_pmd_bad(pmd_t pmd)
 { return (pmd_val(pmd) & SRMMU_ET_MASK) != SRMMU_ET_PTD; }
 
-static int srmmu_pmd_present(pmd_t pmd)
+static inline int srmmu_pmd_present(pmd_t pmd)
 { return ((pmd_val(pmd) & SRMMU_ET_MASK) == SRMMU_ET_PTD); }
 
-static void srmmu_pmd_clear(pmd_t *pmdp)      { set_pte((pte_t *)pmdp, __pte(0)); }
+static inline void srmmu_pmd_clear(pmd_t *pmdp)      { set_pte((pte_t *)pmdp, __pte(0)); }
 
-static int srmmu_pgd_none(pgd_t pgd)          
+static inline int srmmu_pgd_none(pgd_t pgd)          
 { return !(pgd_val(pgd) & 0xFFFFFFF); }
 
-static int srmmu_pgd_bad(pgd_t pgd)
+static inline int srmmu_pgd_bad(pgd_t pgd)
 { return (pgd_val(pgd) & SRMMU_ET_MASK) != SRMMU_ET_PTD; }
 
-static int srmmu_pgd_present(pgd_t pgd)
+static inline int srmmu_pgd_present(pgd_t pgd)
 { return ((pgd_val(pgd) & SRMMU_ET_MASK) == SRMMU_ET_PTD); }
 
-static void srmmu_pgd_clear(pgd_t * pgdp)     { set_pte((pte_t *)pgdp, __pte(0)); }
+static inline void srmmu_pgd_clear(pgd_t * pgdp)     { set_pte((pte_t *)pgdp, __pte(0)); }
 
-static int srmmu_pte_write(pte_t pte)         { return pte_val(pte) & SRMMU_WRITE; }
-static int srmmu_pte_dirty(pte_t pte)         { return pte_val(pte) & SRMMU_DIRTY; }
-static int srmmu_pte_young(pte_t pte)         { return pte_val(pte) & SRMMU_REF; }
-
-static pte_t srmmu_pte_wrprotect(pte_t pte)   { return __pte(pte_val(pte) & ~SRMMU_WRITE);}
-static pte_t srmmu_pte_mkclean(pte_t pte)     { return __pte(pte_val(pte) & ~SRMMU_DIRTY);}
-static pte_t srmmu_pte_mkold(pte_t pte)       { return __pte(pte_val(pte) & ~SRMMU_REF);}
-static pte_t srmmu_pte_mkwrite(pte_t pte)     { return __pte(pte_val(pte) | SRMMU_WRITE);}
-static pte_t srmmu_pte_mkdirty(pte_t pte)     { return __pte(pte_val(pte) | SRMMU_DIRTY);}
-static pte_t srmmu_pte_mkyoung(pte_t pte)     { return __pte(pte_val(pte) | SRMMU_REF);}
+static inline int srmmu_pte_write(pte_t pte)         { return pte_val(pte) & SRMMU_WRITE; }
+static inline int srmmu_pte_dirty(pte_t pte)         { return pte_val(pte) & SRMMU_DIRTY; }
+static inline int srmmu_pte_young(pte_t pte)         { return pte_val(pte) & SRMMU_REF; }
+
+static inline pte_t srmmu_pte_wrprotect(pte_t pte)   { return __pte(pte_val(pte) & ~SRMMU_WRITE);}
+static inline pte_t srmmu_pte_mkclean(pte_t pte)     { return __pte(pte_val(pte) & ~SRMMU_DIRTY);}
+static inline pte_t srmmu_pte_mkold(pte_t pte)       { return __pte(pte_val(pte) & ~SRMMU_REF);}
+static inline pte_t srmmu_pte_mkwrite(pte_t pte)     { return __pte(pte_val(pte) | SRMMU_WRITE);}
+static inline pte_t srmmu_pte_mkdirty(pte_t pte)     { return __pte(pte_val(pte) | SRMMU_DIRTY);}
+static inline pte_t srmmu_pte_mkyoung(pte_t pte)     { return __pte(pte_val(pte) | SRMMU_REF);}
 
 /*
  * Conversion functions: convert a page and protection to a page entry,
@@ -269,6 +345,9 @@
 static pte_t srmmu_c_mk_pte(unsigned long page, pgprot_t pgprot)
 { return __pte(((srmmu_c_v2p(page)) >> 4) | pgprot_val(pgprot)); }
 
+static pte_t srmmu_s_mk_pte(unsigned long page, pgprot_t pgprot)
+{ return __pte(((srmmu_s_v2p(page)) >> 4) | pgprot_val(pgprot)); }
+
 static pte_t srmmu_mk_pte_phys(unsigned long page, pgprot_t pgprot)
 { return __pte(((page) >> 4) | pgprot_val(pgprot)); }
 
@@ -307,41 +386,64 @@
 	set_pte((pte_t *)pmdp, (SRMMU_ET_PTD | (srmmu_c_v2p((unsigned long) ptep) >> 4)));
 }
 
-static pte_t srmmu_pte_modify(pte_t pte, pgprot_t newprot)
+static void srmmu_s_ctxd_set(ctxd_t *ctxp, pgd_t *pgdp)
+{ 
+	set_pte((pte_t *)ctxp, (SRMMU_ET_PTD | (srmmu_s_v2p((unsigned long) pgdp) >> 4)));
+}
+
+static void srmmu_s_pgd_set(pgd_t * pgdp, pmd_t * pmdp)
+{
+	set_pte((pte_t *)pgdp, (SRMMU_ET_PTD | (srmmu_s_v2p((unsigned long) pmdp) >> 4)));
+}
+
+static void srmmu_s_pmd_set(pmd_t * pmdp, pte_t * ptep)
+{
+	set_pte((pte_t *)pmdp, (SRMMU_ET_PTD | (srmmu_s_v2p((unsigned long) ptep) >> 4)));
+}
+
+static inline pte_t srmmu_pte_modify(pte_t pte, pgprot_t newprot)
 {
 	return __pte((pte_val(pte) & SRMMU_CHG_MASK) | pgprot_val(newprot));
 }
 
 /* to find an entry in a top-level page table... */
-static pgd_t *srmmu_pgd_offset(struct mm_struct * mm, unsigned long address)
+static inline pgd_t *srmmu_pgd_offset(struct mm_struct * mm, unsigned long address)
 {
-	return mm->pgd + ((address >> SRMMU_PGDIR_SHIFT) & (SRMMU_PTRS_PER_PGD - 1));
+	return mm->pgd + (address >> SRMMU_PGDIR_SHIFT);
 }
 
 /* Find an entry in the second-level page table.. */
-static pmd_t *srmmu_pmd_offset(pgd_t * dir, unsigned long address)
+static inline pmd_t *srmmu_pmd_offset(pgd_t * dir, unsigned long address)
 {
 	return (pmd_t *) srmmu_pgd_page(*dir) + ((address >> SRMMU_PMD_SHIFT) & (SRMMU_PTRS_PER_PMD - 1));
 }
 
 /* Find an entry in the third-level page table.. */ 
-static pte_t *srmmu_pte_offset(pmd_t * dir, unsigned long address)
+static inline pte_t *srmmu_pte_offset(pmd_t * dir, unsigned long address)
 {
 	return (pte_t *) srmmu_pmd_page(*dir) + ((address >> PAGE_SHIFT) & (SRMMU_PTRS_PER_PTE - 1));
 }
 
-/* Find an entry in the second-level page table.. */
-static pmd_t *srmmu_c_pmd_offset(pgd_t * dir, unsigned long address)
+static inline pmd_t *srmmu_c_pmd_offset(pgd_t * dir, unsigned long address)
 {
 	return (pmd_t *) srmmu_c_pgd_page(*dir) + ((address >> SRMMU_PMD_SHIFT) & (SRMMU_PTRS_PER_PMD - 1));
 }
 
-/* Find an entry in the third-level page table.. */ 
-static pte_t *srmmu_c_pte_offset(pmd_t * dir, unsigned long address)
+static inline pte_t *srmmu_c_pte_offset(pmd_t * dir, unsigned long address)
 {
 	return (pte_t *) srmmu_c_pmd_page(*dir) + ((address >> PAGE_SHIFT) & (SRMMU_PTRS_PER_PTE - 1));
 }
 
+static inline pmd_t *srmmu_s_pmd_offset(pgd_t * dir, unsigned long address)
+{
+	return (pmd_t *) srmmu_s_pgd_page(*dir) + ((address >> SRMMU_PMD_SHIFT) & (SRMMU_PTRS_PER_PMD - 1));
+}
+
+static inline pte_t *srmmu_s_pte_offset(pmd_t * dir, unsigned long address)
+{
+	return (pte_t *) srmmu_s_pmd_page(*dir) + ((address >> PAGE_SHIFT) & (SRMMU_PTRS_PER_PTE - 1));
+}
+
 /* This must update the context table entry for this process. */
 static void srmmu_update_rootmmu_dir(struct task_struct *tsk, pgd_t *pgdp) 
 {
@@ -352,334 +454,146 @@
 	}
 }
 
-static inline void srmmu_putpage(unsigned long page)
-{
-	free_page(page);
-}
-
-#define LC_HIGH_WATER	128
-#define BC_HIGH_WATER	32
-
-static unsigned long *lcnks = 0;
-static unsigned long *bcnks = 0;
-static int lcwater = 0;
-static int bcwater = 0;
-static int chunk_pages = 0;
-static int clct_pages = 0;
-
-#define RELAX_JIFFIES	16
-
-static int lcjiffies;
-static int bcjiffies;
-
-struct chunk {
-	struct chunk *next;
-	struct chunk *prev;
-	struct chunk *npage;
-	struct chunk *ppage;
-	int count;
-};
-
-static int garbage_calls = 0;
-
-#define OTHER_PAGE(p,q)	(((unsigned long)(p) ^ (unsigned long)(q)) & PAGE_MASK)
-
-static int garbage_collect(unsigned long **cnks, int n, int cpp)
+static inline pte_t *srmmu_get_pte_fast(void)
 {
-	struct chunk *root = (struct chunk *)*cnks;
-	struct chunk *p, *q, *curr, *next;
-	int water = n;
-
-	next = root->next;
-	curr = root->prev = root->next = root->npage = root->ppage = root;
-	root->count = 1;
-
-	garbage_calls++;
-
-	while (--n) {
-		p = next;
-		next = next->next;
-
-		if (OTHER_PAGE(p, curr)) {
-
-			q = curr->npage;
-			while (q != curr) {
-				if (!OTHER_PAGE(p, q))
-					break;
-				q = q->npage;
-			}
-
-			if (q == curr) {
-
-				(p->npage = curr->npage)->ppage = p;
-				curr->npage = p;
-				p->ppage = curr;
-
-				p->next = p->prev = p;
-				p->count = 1;
-
-				curr = p;
-
-				continue;
-			}
-			curr = q;
-		}
-
-		(p->next = curr->next)->prev = p;
-		curr->next = p;
-		p->prev = curr;
-
-		if (++curr->count == cpp) {
-
-			q = curr->npage;
-			if (curr == q) {
-
-				srmmu_putpage((unsigned long)curr & PAGE_MASK);
-				water -= cpp;
-
-				clct_pages++;
-				chunk_pages--;
-
-				if (--n) {
-					p = next;
-					next = next->next;
-
-					curr = root->prev =
-						root->next = root->npage =
-						root->ppage = root = p;
-					root->count = 1;
-
-					continue;
-				}
-				return 0;
-			}
-
-			if (curr == root)
-				root = q;
-
-			curr->ppage->npage = q;
-			q->ppage = curr->ppage;
-
-			srmmu_putpage((unsigned long)curr & PAGE_MASK);
-			water -= cpp;
-
-			clct_pages++;
-			chunk_pages--;
-
-			curr = q;
-		}
-	}
-
-	p = root;
-	while (p->npage != root) {
-		p->prev->next = p->npage;
-		p = p->npage;
+	struct page *ret;
+	
+	spin_lock(&pte_spinlock);
+	if ((ret = (struct page *)pte_quicklist) != NULL) {
+		unsigned int mask = (unsigned int)ret->pprev_hash;
+		unsigned int tmp, off;
+		
+		if (mask & 0xff)
+			for (tmp = 0x001, off = 0; (mask & tmp) == 0; tmp <<= 1, off += 256);
+		else
+			for (tmp = 0x100, off = 2048; (mask & tmp) == 0; tmp <<= 1, off += 256);
+		(unsigned int)ret->pprev_hash = mask & ~tmp;
+		if (!(mask & ~tmp))
+			pte_quicklist = (unsigned long *)ret->next_hash;
+		ret = (struct page *)(PAGE_OFFSET + (ret->map_nr << PAGE_SHIFT) + off);
+		pgtable_cache_size--;
 	}
-
-	*cnks = (unsigned long *)root;
-	return water;
+	spin_unlock(&pte_spinlock);
+	return (pte_t *)ret;
 }
 
-static unsigned long *get_small_chunk(void)
+static inline pte_t *srmmu_get_pte_slow(void)
 {
-	unsigned long *rval;
-	unsigned long flags;
-
-	save_and_cli(flags);
-	if(lcwater) {
-		lcwater--;
-		rval = lcnks;
-		lcnks = (unsigned long *) *rval;
-	} else {
-		rval = (unsigned long *) __get_free_page(GFP_KERNEL);
-
-		if(!rval) {
-			restore_flags(flags);
-			return 0;
-		}
-		chunk_pages++;
-
-		lcnks = (rval + 64);
-
-		/* Cache stomping, I know... */
-		*(rval + 64) = (unsigned long) (rval + 128);
-		*(rval + 128) = (unsigned long) (rval + 192);
-		*(rval + 192) = (unsigned long) (rval + 256);
-		*(rval + 256) = (unsigned long) (rval + 320);
-		*(rval + 320) = (unsigned long) (rval + 384);
-		*(rval + 384) = (unsigned long) (rval + 448);
-		*(rval + 448) = (unsigned long) (rval + 512);
-		*(rval + 512) = (unsigned long) (rval + 576);
-		*(rval + 576) = (unsigned long) (rval + 640);
-		*(rval + 640) = (unsigned long) (rval + 704);
-		*(rval + 704) = (unsigned long) (rval + 768);
-		*(rval + 768) = (unsigned long) (rval + 832);
-		*(rval + 832) = (unsigned long) (rval + 896);
-		*(rval + 896) = (unsigned long) (rval + 960);
-		*(rval + 960) = 0;
-		lcwater = 15;
+	pte_t *ret;
+	struct page *page;
+	
+	ret = (pte_t *)get_free_page(GFP_KERNEL);
+	if (ret) {
+		page = mem_map + MAP_NR(ret);
+		flush_chunk((unsigned long)ret);
+		(unsigned int)page->pprev_hash = 0xfffe;
+		spin_lock(&pte_spinlock);
+		(unsigned long *)page->next_hash = pte_quicklist;
+		pte_quicklist = (unsigned long *)page;
+		pgtable_cache_size += 15;
+	}
+	return ret;
+}
+
+static inline pgd_t *srmmu_get_pgd_fast(void)
+{
+	struct page *ret;
+
+	spin_lock(&pgd_spinlock);	
+	if ((ret = (struct page *)pgd_quicklist) != NULL) {
+		unsigned int mask = (unsigned int)ret->pprev_hash;
+		unsigned int tmp, off;
+		
+		for (tmp = 0x001, off = 0; (mask & tmp) == 0; tmp <<= 1, off += 1024);
+		(unsigned int)ret->pprev_hash = mask & ~tmp;
+		if (!(mask & ~tmp))
+			pgd_quicklist = (unsigned long *)ret->next_hash;
+		ret = (struct page *)(PAGE_OFFSET + (ret->map_nr << PAGE_SHIFT) + off);
+		pgd_cache_size--;
 	}
-	lcjiffies = jiffies;
-	restore_flags(flags);
-	memset(rval, 0, 256);
-	flush_chunk((unsigned long)rval);
-	return rval;
+	spin_unlock(&pgd_spinlock);
+	return (pte_t *)ret;
 }
 
-static inline void free_small_chunk(unsigned long *it)
+static inline pgd_t *srmmu_get_pgd_slow(void)
 {
-	unsigned long flags;
-
-	save_and_cli(flags);
-	*it = (unsigned long) lcnks;
-	lcnks = it;
-	lcwater++;
-
-	if ((lcwater > LC_HIGH_WATER) &&
-	    (jiffies > lcjiffies + RELAX_JIFFIES))
-		lcwater = garbage_collect(&lcnks, lcwater, 16);
-
-	restore_flags(flags);
-}
-
-static unsigned long *get_big_chunk(void)
-{
-	unsigned long *rval;
-	unsigned long flags;
-
-	save_and_cli(flags);
-	if(bcwater) {
-		bcwater--;
-		rval = bcnks;
-		bcnks = (unsigned long *) *rval;
-	} else {
-		rval = (unsigned long *) __get_free_page(GFP_KERNEL);
-
-		if(!rval) {
-			restore_flags(flags);
-			return 0;
-		}
-		chunk_pages++;
-
-		bcnks = (rval + 256);
-
-		/* Cache stomping, I know... */
-		*(rval + 256) = (unsigned long) (rval + 512);
-		*(rval + 512) = (unsigned long) (rval + 768);
-		*(rval + 768) = 0;
-		bcwater = 3;
+	pgd_t *ret;
+	struct page *page;
+	
+	ret = (pgd_t *)__get_free_page(GFP_KERNEL);
+	if (ret) {
+		pgd_t *init = pgd_offset(&init_mm, 0);
+		memset(ret + (0 * PTRS_PER_PGD), 0, USER_PTRS_PER_PGD * sizeof(pgd_t));
+		memcpy(ret + (0 * PTRS_PER_PGD) + USER_PTRS_PER_PGD, init + USER_PTRS_PER_PGD,
+						(PTRS_PER_PGD - USER_PTRS_PER_PGD) * sizeof(pgd_t));
+		memset(ret + (1 * PTRS_PER_PGD), 0, USER_PTRS_PER_PGD * sizeof(pgd_t));
+		memcpy(ret + (1 * PTRS_PER_PGD) + USER_PTRS_PER_PGD, init + USER_PTRS_PER_PGD,
+						(PTRS_PER_PGD - USER_PTRS_PER_PGD) * sizeof(pgd_t));
+		memset(ret + (2 * PTRS_PER_PGD), 0, USER_PTRS_PER_PGD * sizeof(pgd_t));
+		memcpy(ret + (2 * PTRS_PER_PGD) + USER_PTRS_PER_PGD, init + USER_PTRS_PER_PGD,
+						(PTRS_PER_PGD - USER_PTRS_PER_PGD) * sizeof(pgd_t));
+		memset(ret + (3 * PTRS_PER_PGD), 0, USER_PTRS_PER_PGD * sizeof(pgd_t));
+		memcpy(ret + (3 * PTRS_PER_PGD) + USER_PTRS_PER_PGD, init + USER_PTRS_PER_PGD,
+						(PTRS_PER_PGD - USER_PTRS_PER_PGD) * sizeof(pgd_t));
+		page = mem_map + MAP_NR(ret);
+		flush_chunk((unsigned long)ret);
+		(unsigned int)page->pprev_hash = 0xe;
+		spin_lock(&pgd_spinlock);
+		(unsigned long *)page->next_hash = pgd_quicklist;
+		pgd_quicklist = (unsigned long *)page;
+		pgd_cache_size += 3;
+		spin_unlock(&pgd_spinlock);
 	}
-	bcjiffies = jiffies;
-	restore_flags(flags);
-	memset(rval, 0, 1024);
-	flush_chunk((unsigned long)rval);
-	return rval;
+	return ret;
 }
 
-static inline void free_big_chunk(unsigned long *it)
-{
-	unsigned long flags;
-
-	save_and_cli(flags);
-	*it = (unsigned long) bcnks;
-	bcnks = it;
-	bcwater++;
-
-	if ((bcwater > BC_HIGH_WATER) &&
-	    (jiffies > bcjiffies + RELAX_JIFFIES))
-		bcwater = garbage_collect(&bcnks, bcwater, 4);
-
-	restore_flags(flags);
-}
-
-#define NEW_PGD() (pgd_t *) get_big_chunk()
-#define NEW_PMD() (pmd_t *) get_small_chunk()
-#define NEW_PTE() (pte_t *) get_small_chunk()
-#define FREE_PGD(chunk) free_big_chunk((unsigned long *)(chunk))
-#define FREE_PMD(chunk) free_small_chunk((unsigned long *)(chunk))
-#define FREE_PTE(chunk) free_small_chunk((unsigned long *)(chunk))
-
-/*
- * Allocate and free page tables. The xxx_kernel() versions are
- * used to allocate a kernel page table - this turns on ASN bits
- * if any, and marks the page tables reserved.
- */
-static void srmmu_pte_free_kernel(pte_t *pte)
+static void srmmu_free_pte_slow(pte_t *pte)
 {
-	FREE_PTE(pte);
 }
 
-static pte_t *srmmu_pte_alloc_kernel(pmd_t *pmd, unsigned long address)
+static void srmmu_free_pgd_slow(pgd_t *pgd)
 {
-	address = (address >> PAGE_SHIFT) & (SRMMU_PTRS_PER_PTE - 1);
-	if(srmmu_pmd_none(*pmd)) {
-		pte_t *page = NEW_PTE();
-		if(srmmu_pmd_none(*pmd)) {
-			if(page) {
-				pmd_set(pmd, page);
-				return page + address;
-			}
-			pmd_set(pmd, BAD_PAGETABLE);
-			return NULL;
-		}
-		FREE_PTE(page);
-	}
-	if(srmmu_pmd_bad(*pmd)) {
-		printk("Bad pmd in pte_alloc: %08lx\n", pmd_val(*pmd));
-		pmd_set(pmd, BAD_PAGETABLE);
-		return NULL;
-	}
-	return (pte_t *) pmd_page(*pmd) + address;
 }
 
-static void srmmu_pmd_free_kernel(pmd_t *pmd)
+static inline void srmmu_pte_free(pte_t *pte)
 {
-	FREE_PMD(pmd);
-}
+	struct page *page = mem_map + MAP_NR(pte);
 
-static pmd_t *srmmu_pmd_alloc_kernel(pgd_t *pgd, unsigned long address)
-{
-	address = (address >> SRMMU_PMD_SHIFT) & (SRMMU_PTRS_PER_PMD - 1);
-	if(srmmu_pgd_none(*pgd)) {
-		pmd_t *page;
-		page = NEW_PMD();
-		if(srmmu_pgd_none(*pgd)) {
-			if(page) {
-				pgd_set(pgd, page);
-				return page + address;
-			}
-			pgd_set(pgd, (pmd_t *) BAD_PAGETABLE);
-			return NULL;
-		}
-		FREE_PMD(page);
+	spin_lock(&pte_spinlock);	
+	if (!page->pprev_hash) {
+		(unsigned long *)page->next_hash = pte_quicklist;
+		pte_quicklist = (unsigned long *)page;
 	}
-	if(srmmu_pgd_bad(*pgd)) {
-		printk("Bad pgd in pmd_alloc: %08lx\n", pgd_val(*pgd));
-		pgd_set(pgd, (pmd_t *) BAD_PAGETABLE);
-		return NULL;
-	}
-	return (pmd_t *) pgd_page(*pgd) + address;
-}
-
-static void srmmu_pte_free(pte_t *pte)
-{
-	FREE_PTE(pte);
+	(unsigned int)page->pprev_hash |= (1 << ((((unsigned long)pte) >> 8) & 15));
+	pgtable_cache_size++;
+	spin_unlock(&pte_spinlock);
 }
 
 static pte_t *srmmu_pte_alloc(pmd_t * pmd, unsigned long address)
 {
 	address = (address >> PAGE_SHIFT) & (SRMMU_PTRS_PER_PTE - 1);
 	if(srmmu_pmd_none(*pmd)) {
-		pte_t *page = NEW_PTE();
+		pte_t *page = srmmu_get_pte_fast();
+		
+		if (page) {
+			pmd_set(pmd, page);
+			return page + address;
+		}
+		page = srmmu_get_pte_slow();
 		if(srmmu_pmd_none(*pmd)) {
 			if(page) {
+				spin_unlock(&pte_spinlock);
 				pmd_set(pmd, page);
 				return page + address;
 			}
 			pmd_set(pmd, BAD_PAGETABLE);
 			return NULL;
 		}
-		FREE_PTE(page);
+		if (page) {
+			(unsigned int)(((struct page *)pte_quicklist)->pprev_hash) = 0xffff;
+			pgtable_cache_size++;
+			spin_unlock(&pte_spinlock);
+		}
 	}
 	if(srmmu_pmd_bad(*pmd)) {
 		printk("Bad pmd in pte_alloc: %08lx\n", pmd_val(*pmd));
@@ -692,23 +606,34 @@
 /* Real three-level page tables on SRMMU. */
 static void srmmu_pmd_free(pmd_t * pmd)
 {
-	FREE_PMD(pmd);
+	return srmmu_pte_free((pte_t *)pmd);
 }
 
 static pmd_t *srmmu_pmd_alloc(pgd_t * pgd, unsigned long address)
 {
 	address = (address >> SRMMU_PMD_SHIFT) & (SRMMU_PTRS_PER_PMD - 1);
 	if(srmmu_pgd_none(*pgd)) {
-		pmd_t *page = NEW_PMD();
+		pmd_t *page = (pmd_t *)srmmu_get_pte_fast();
+		
+		if (page) {
+			pgd_set(pgd, page);
+			return page + address;
+		}
+		page = (pmd_t *)srmmu_get_pte_slow();
 		if(srmmu_pgd_none(*pgd)) {
 			if(page) {
+				spin_unlock(&pte_spinlock);
 				pgd_set(pgd, page);
 				return page + address;
 			}
 			pgd_set(pgd, (pmd_t *) BAD_PAGETABLE);
 			return NULL;
 		}
-		FREE_PMD(page);
+		if (page) {
+			(unsigned int)(((struct page *)pte_quicklist)->pprev_hash) = 0xffff;
+			pgtable_cache_size++;
+			spin_unlock(&pte_spinlock);
+		}
 	}
 	if(srmmu_pgd_bad(*pgd)) {
 		printk("Bad pgd in pmd_alloc: %08lx\n", pgd_val(*pgd));
@@ -720,12 +645,58 @@
 
 static void srmmu_pgd_free(pgd_t *pgd)
 {
-	FREE_PGD(pgd);
+	struct page *page = mem_map + MAP_NR(pgd);
+
+	spin_lock(&pgd_spinlock);
+	if (!page->pprev_hash) {
+		(unsigned long *)page->next_hash = pgd_quicklist;
+		pgd_quicklist = (unsigned long *)page;
+	}
+	(unsigned int)page->pprev_hash |= (1 << ((((unsigned long)pgd) >> 10) & 3));
+	pgd_cache_size++;
+	spin_unlock(&pgd_spinlock);
 }
 
 static pgd_t *srmmu_pgd_alloc(void)
 {
-	return NEW_PGD();
+	pgd_t *ret;
+	
+	ret = srmmu_get_pgd_fast();
+	if (ret) return ret;
+	return srmmu_get_pgd_slow();
+}
+
+
+static void srmmu_set_pgdir(unsigned long address, pgd_t entry)
+{
+	struct task_struct * p;
+	struct page *page;
+
+	read_lock(&tasklist_lock);
+	for_each_task(p) {
+		if (!p->mm)
+			continue;
+		*pgd_offset(p->mm,address) = entry;
+	}
+	read_unlock(&tasklist_lock);
+	spin_lock(&pgd_spinlock);
+	address >>= SRMMU_PGDIR_SHIFT;
+	for (page = (struct page *)pgd_quicklist; page; page = page->next_hash) {
+		pgd_t *pgd = (pgd_t *)(PAGE_OFFSET + (page->map_nr << PAGE_SHIFT));
+		unsigned int mask = (unsigned int)page->pprev_hash;
+		
+		if (mask & 1)
+			pgd[address + 0 * SRMMU_PTRS_PER_PGD] = entry;
+		if (mask & 2)
+			pgd[address + 1 * SRMMU_PTRS_PER_PGD] = entry;
+		if (mask & 4)
+			pgd[address + 2 * SRMMU_PTRS_PER_PGD] = entry;
+		if (mask & 8)
+			pgd[address + 3 * SRMMU_PTRS_PER_PGD] = entry;
+		if (mask)
+			flush_chunk((unsigned long)pgd);
+	}
+	spin_unlock(&pgd_spinlock);
 }
 
 static void srmmu_set_pte_cacheable(pte_t *ptep, pte_t pteval)
@@ -926,6 +897,19 @@
 extern void tsunami_flush_tlb_range(struct mm_struct *mm, unsigned long start, unsigned long end);
 extern void tsunami_flush_tlb_page(struct vm_area_struct *vma, unsigned long page);
 
+/* Workaround, until we find what's going on with Swift. When low on memory, it sometimes
+ * loops in fault/handle_mm_fault incl. flush_tlb_page to find out it is already in page tables/
+ * fault again on the same instruction. I really don't understand it, have checked it and contexts
+ * are right, flush_tlb_all is done as well, and it faults again... Strange. -jj
+ */
+static void swift_update_mmu_cache(struct vm_area_struct * vma, unsigned long address, pte_t pte)
+{
+	static unsigned long last;
+
+	if (last == address) viking_hwprobe(address);
+	last = address;
+}
+
 /* Swift flushes.  It has the recommended SRMMU specification flushing
  * facilities, so we can do things in a more fine grained fashion than we
  * could on the tsunami.  Let's watch out for HARDWARE BUGS...
@@ -1191,12 +1175,10 @@
 	cypress_flush_page_to_ram(chunk);
 }
 
-#if NOTUSED
 /* Cypress is also IO cache coherent. */
 static void cypress_flush_page_for_dma(unsigned long page)
 {
 }
-#endif
 
 /* Cypress has unified L2 VIPT, from which both instructions and data
  * are stored.  It does not have an onboard icache of any sort, therefore
@@ -1282,9 +1264,8 @@
 extern void viking_flush_page(unsigned long page);
 extern void viking_mxcc_flush_page(unsigned long page);
 extern void viking_flush_chunk(unsigned long chunk);
-extern void viking_c_flush_page(unsigned long page);
-extern void viking_c_mxcc_flush_page(unsigned long page);
 extern void viking_c_flush_chunk(unsigned long chunk);
+extern void viking_s_flush_chunk(unsigned long chunk);
 extern void viking_mxcc_flush_chunk(unsigned long chunk);
 extern void viking_flush_tlb_all(void);
 extern void viking_flush_tlb_mm(struct mm_struct *mm);
@@ -1481,7 +1462,7 @@
  * looking at the prom's page table directly which is what most
  * other OS's do.  Yuck... this is much better.
  */
-void srmmu_inherit_prom_mappings(unsigned long start,unsigned long end)
+__initfunc(void srmmu_inherit_prom_mappings(unsigned long start,unsigned long end))
 {
 	pgd_t *pgdp;
 	pmd_t *pmdp;
@@ -1539,21 +1520,79 @@
 	}
 }
 
-/* #define DEBUG_MAP_KERNEL */
-
 #ifdef DEBUG_MAP_KERNEL
 #define MKTRACE(foo) prom_printf foo
 #else
 #define MKTRACE(foo)
 #endif
 
-static int lots_of_ram = 0;
-static int large_pte_optimize = 1;
+static int lots_of_ram __initdata = 0;
+static int srmmu_low_pa __initdata = 0;
+static unsigned long end_of_phys_memory __initdata = 0;
+
+__initfunc(void srmmu_end_memory(unsigned long memory_size, unsigned long *end_mem_p))
+{
+	unsigned int sum = 0;
+	unsigned long last = 0xff000000;
+	long first, cur;
+	unsigned long pa;
+	unsigned long total = 0;
+	int i;
+
+	pa = srmmu_hwprobe(KERNBASE + PAGE_SIZE);
+	pa = (pa & SRMMU_PTE_PMASK) << 4;
+	if (!sp_banks[0].base_addr && pa == PAGE_SIZE) {
+		for(i = 0; sp_banks[i].num_bytes != 0; i++) {
+			if (sp_banks[i].base_addr + sp_banks[i].num_bytes > 0x0d000000)
+				break;
+		}
+		if (!sp_banks[i].num_bytes) {
+			srmmu_low_pa = 1;
+			end_of_phys_memory = SRMMU_PGDIR_ALIGN(sp_banks[i-1].base_addr + sp_banks[i-1].num_bytes);
+			*end_mem_p = KERNBASE + end_of_phys_memory;
+			if (sp_banks[0].num_bytes >= (6 * 1024 * 1024) || end_of_phys_memory <= 0x06000000) {
+				/* Make sure there will be enough memory for the whole mem_map (even if sparse) */
+				return;
+			}
+		}
+	}
+	for(i = 0; sp_banks[i].num_bytes != 0; i++) {
+		pa = sp_banks[i].base_addr;
+		first = (pa & (~SRMMU_PGDIR_MASK));
+		cur = (sp_banks[i].num_bytes + first - SRMMU_PGDIR_SIZE);
+		if (cur < 0) cur = 0;
+		if (!first || last != (pa & SRMMU_PGDIR_MASK))
+			total += SRMMU_PGDIR_SIZE;
+		sum += sp_banks[i].num_bytes;
+		if (memory_size) {
+			if (sum > memory_size) {
+				sp_banks[i].num_bytes -=
+					(sum - memory_size);
+				cur = (sp_banks[i].num_bytes + first - SRMMU_PGDIR_SIZE);
+				if (cur < 0) cur = 0;
+				total += SRMMU_PGDIR_ALIGN(cur);
+				sum = memory_size;
+				sp_banks[++i].base_addr = 0xdeadbeef;
+				sp_banks[i].num_bytes = 0;
+				break;
+			}
+		}
+		total += SRMMU_PGDIR_ALIGN(cur);
+		last = (sp_banks[i].base_addr + sp_banks[i].num_bytes - 1) & SRMMU_PGDIR_MASK;
+	}
+	if (total <= 0x0d000000)
+		*end_mem_p = KERNBASE + total;
+	else {
+		*end_mem_p = 0xfd000000;
+		lots_of_ram = 1;
+	}
+	end_of_phys_memory = total;
+}
 
 #define KERNEL_PTE(page_shifted) ((page_shifted)|SRMMU_CACHE|SRMMU_PRIV|SRMMU_VALID)
 
 /* Create a third-level SRMMU 16MB page mapping. */
-static inline void do_large_mapping(unsigned long vaddr, unsigned long phys_base)
+__initfunc(static void do_large_mapping(unsigned long vaddr, unsigned long phys_base))
 {
 	pgd_t *pgdp = srmmu_pgd_offset(init_task.mm, vaddr);
 	unsigned long big_pte;
@@ -1563,47 +1602,6 @@
 	*pgdp = __pgd(big_pte);
 }
 
-/* Create second-level SRMMU 256K medium sized page mappings. */
-static inline void do_medium_mapping(unsigned long vaddr, unsigned long vend,
-				     unsigned long phys_base)
-{
-	pgd_t *pgdp;
-	pmd_t *pmdp;
-	unsigned long medium_pte;
-
-	MKTRACE(("dmm[v<%08lx,%08lx>-->p<%08lx>]", vaddr, vend, phys_base));
-	while(vaddr < vend) {
-		pgdp = srmmu_pgd_offset(init_task.mm, vaddr);
-		pmdp = srmmu_early_pmd_offset(pgdp, vaddr);
-		medium_pte = KERNEL_PTE(phys_base >> 4);
-		*pmdp = __pmd(medium_pte);
-		phys_base += SRMMU_PMD_SIZE;
-		vaddr += SRMMU_PMD_SIZE;
-	}
-}
-
-/* Create a normal set of SRMMU page mappings for the virtual range
- * START to END, using physical pages beginning at PHYS_BASE.
- */
-static inline void do_small_mapping(unsigned long start, unsigned long end,
-				     unsigned long phys_base)
-{
-	pgd_t *pgdp;
-	pmd_t *pmdp;
-	pte_t *ptep;
-
-	MKTRACE(("dsm[v<%08lx,%08lx>-->p<%08lx>]", start, end, phys_base));
-	while(start < end) {
-		pgdp = srmmu_pgd_offset(init_task.mm, start);
-		pmdp = srmmu_early_pmd_offset(pgdp, start);
-		ptep = srmmu_early_pte_offset(pmdp, start);
-
-		*ptep = __pte(KERNEL_PTE(phys_base >> 4));
-		phys_base += PAGE_SIZE;
-		start += PAGE_SIZE;
-	}
-}
-
 /* Look in the sp_bank for the given physical page, return the
  * index number the entry was found in, or -1 for not found.
  */
@@ -1625,7 +1623,7 @@
  * array of char's, each member indicating if that spbank is mapped
  * yet or not.
  */
-static inline int find_free_spbank(char *taken_vector)
+__initfunc(static int find_free_spbank(char *taken_vector))
 {
 	int entry;
 
@@ -1635,78 +1633,28 @@
 	return entry;
 }
 
-/* Same as above, but with a given bank size limit BLIMIT. */
-static inline int find_free_spbank_limited(char *taken_vector, unsigned long limit)
-{
-	int entry;
-
-	for(entry = 0; sp_banks[entry].num_bytes; entry++)
-		if(!taken_vector[entry] &&
-		   (sp_banks[entry].num_bytes < limit))
-			break;
-	return entry;
-}
+static unsigned long map_spbank_last_pa __initdata = 0xff000000;
 
 /* Map sp_bank entry SP_ENTRY, starting at virtual address VBASE.
- * This routine is expected to update the srmmu_map and try as
- * hard as possible to use 16MB level-one SRMMU pte's when at all
- * possible to get short termination and faster translations.
  */
-static inline unsigned long map_spbank(unsigned long vbase, int sp_entry)
+__initfunc(static unsigned long map_spbank(unsigned long vbase, int sp_entry))
 {
-	unsigned long pstart = sp_banks[sp_entry].base_addr;
-	unsigned long vstart = vbase;
-	unsigned long vend = vbase + sp_banks[sp_entry].num_bytes;
+	unsigned long pstart = (sp_banks[sp_entry].base_addr & SRMMU_PGDIR_MASK);
+	unsigned long vstart = (vbase & SRMMU_PGDIR_MASK);
+	unsigned long vend = SRMMU_PGDIR_ALIGN(vbase + sp_banks[sp_entry].num_bytes);
 	static int srmmu_bank = 0;
 
-	/* If physically not aligned on 16MB boundry, just shortcut
-	 * right here by mapping them with 4k normal pages, and bumping
-	 * the next virtual address to the next 16MB boundry.  You can
-	 * get this with various RAM configurations due to the way in
-	 * which the PROM carves out it's own chunks of memory.
-	 */
-	if(pstart & ~SRMMU_PGDIR_MASK) {
-		do_small_mapping(vstart, vend, pstart);
-		vstart = SRMMU_PGDIR_ALIGN(vend);
-		goto finish_up;
-	}
+	MKTRACE(("map_spbank %d[v<%08lx>p<%08lx>s<%08lx>]", sp_entry, vbase, sp_banks[sp_entry].base_addr, sp_banks[sp_entry].num_bytes));
+	MKTRACE(("map_spbank2 %d[p%08lx v%08lx-%08lx]", sp_entry, pstart, vstart, vend));
 	while(vstart < vend) {
-		unsigned long coverage, next_aligned;
-		if(vstart & ~SRMMU_PMD_MASK) {
-			next_aligned = SRMMU_PMD_ALIGN(vstart);
-			if(next_aligned <= vend) {
-				coverage = (next_aligned - vstart);
-				do_small_mapping(vstart, next_aligned, pstart);
-			} else {
-				coverage = (vend - vstart);
-				do_small_mapping(vstart, vend, pstart);
-			}
-		} else if(vstart & ~SRMMU_PGDIR_MASK) {
-			next_aligned = SRMMU_PGDIR_ALIGN(vstart);
-			if(next_aligned <= vend) {
-				coverage = (next_aligned - vstart);
-				do_medium_mapping(vstart, next_aligned, pstart);
-			} else {
-				coverage = (vend - vstart);
-				do_small_mapping(vstart, vend, pstart);
-			}
-		} else {
-			coverage = SRMMU_PGDIR_SIZE;
-			if(large_pte_optimize || ((vstart+coverage)<=vend)) {
-				do_large_mapping(vstart, pstart);
-			} else {
-				coverage = (vend - vstart);
-				do_small_mapping(vstart, vend, pstart);
-			}
-		}
-		vstart += coverage; pstart += coverage;
+		do_large_mapping(vstart, pstart);
+		vstart += SRMMU_PGDIR_SIZE; pstart += SRMMU_PGDIR_SIZE;
 	}
-finish_up:
 	srmmu_map[srmmu_bank].vbase = vbase;
 	srmmu_map[srmmu_bank].pbase = sp_banks[sp_entry].base_addr;
 	srmmu_map[srmmu_bank].size = sp_banks[sp_entry].num_bytes;
-	MKTRACE(("SRMMUBANK[v<%08lx>p<%08lx>s<%08lx>]", vbase, sp_banks[sp_entry].base_addr, sp_banks[sp_entry].num_bytes));
 	srmmu_bank++;
+	map_spbank_last_pa = pstart - SRMMU_PGDIR_SIZE;
 	return vstart;
 }
 
@@ -1721,10 +1669,10 @@
  * is part of a full bank which is at least 4MB in size and begins at
  * 0xf0000000 (ie. KERNBASE).
  */
-static void map_kernel(void)
+static inline void map_kernel(void)
 {
 	unsigned long raw_pte, physpage;
-	unsigned long vaddr, tally, low_base;
+	unsigned long vaddr, low_base;
 	char etaken[SPARC_PHYS_BANKS];
 	int entry;
 
@@ -1735,17 +1683,7 @@
 
 	low_base = KERNBASE;
 
-	/* Step 2: Calculate 'lots_of_ram'. */
-	tally = 0;
-	for(entry = 0; sp_banks[entry].num_bytes; entry++)
-		tally += sp_banks[entry].num_bytes;
-	if(tally > (0xfd000000 - KERNBASE))
-		lots_of_ram = 1;
-	else
-		lots_of_ram = 0;
-	MKTRACE(("tally=%08lx lots_of_ram<%d>\n", tally, lots_of_ram));
-
-	/* Step 3: Fill in KERNBASE base pgd.  Lots of sanity checking here. */
+	/* Step 2: Fill in KERNBASE base pgd.  Lots of sanity checking here. */
 	raw_pte = srmmu_hwprobe(KERNBASE + PAGE_SIZE);
 	if((raw_pte & SRMMU_ET_MASK) != SRMMU_ET_PTE)
 		memprobe_error("Wheee, kernel not mapped at all by boot loader.\n");
@@ -1757,11 +1695,10 @@
 	if(entry == -1 || (sp_banks[entry].base_addr != physpage))
 		memprobe_error("Kernel mapped in non-existant memory.\n");
 	MKTRACE(("map_kernel: map_spbank(vbase=%08x, entry<%d>)[%08lx,%08lx]\n", KERNBASE, entry, sp_banks[entry].base_addr, sp_banks[entry].num_bytes));
-	if(((KERNBASE + (sp_banks[entry].num_bytes)) > 0xfd000000) ||
-	   ((KERNBASE + (sp_banks[entry].num_bytes)) < KERNBASE)) {
+	if (sp_banks[entry].num_bytes > 0x0d000000) {
 		unsigned long orig_base = sp_banks[entry].base_addr;
 		unsigned long orig_len = sp_banks[entry].num_bytes;
-		unsigned long can_map = (0xfd000000 - KERNBASE);
+		unsigned long can_map = 0x0d000000;
 		
 		/* Map a partial bank in this case, adjust the base
 		 * and the length, but don't mark it used.
@@ -1779,7 +1716,7 @@
 	vaddr = map_spbank(KERNBASE, entry);
 	etaken[entry] = 1;
 
-	/* Step 4: Map what we can above KERNBASE. */
+	/* Step 3: Map what we can above KERNBASE. */
 	MKTRACE(("map_kernel: vaddr=%08lx, entering first loop\n", vaddr));
 	for(;;) {
 		unsigned long bank_size;
@@ -1790,8 +1727,14 @@
 		MKTRACE(("<%d> base=%08lx bs=%08lx ", entry, sp_banks[entry].base_addr, bank_size));
 		if(!bank_size)
 			break;
-		if(((vaddr + bank_size) > 0xfd000000) ||
-		   ((vaddr + bank_size) < KERNBASE)) {
+		if (srmmu_low_pa)
+			vaddr = KERNBASE + sp_banks[entry].base_addr;
+		else if (sp_banks[entry].base_addr & (~SRMMU_PGDIR_MASK)) {
+			if (map_spbank_last_pa == (sp_banks[entry].base_addr & SRMMU_PGDIR_MASK))
+				vaddr -= SRMMU_PGDIR_SIZE;
+			vaddr += (sp_banks[entry].base_addr & (~SRMMU_PGDIR_MASK));
+		}
+		if ((vaddr + bank_size - KERNBASE) > 0x0d000000) {
 			unsigned long orig_base = sp_banks[entry].base_addr;
 			unsigned long orig_len = sp_banks[entry].num_bytes;
 			unsigned long can_map = (0xfd000000 - vaddr);
@@ -1808,8 +1751,6 @@
 			MKTRACE(("adjust[%08lx,%08lx]\n", (orig_base + can_map), (orig_len - can_map)));
 			break;
 		}
-		if(!bank_size)
-			break;
 
 		/* Ok, we can map this one, do it. */
 		MKTRACE(("map_spbank(%08lx,entry<%d>) ", vaddr, entry));
@@ -1823,22 +1764,16 @@
 	if(!lots_of_ram)
 		goto check_and_return;
 	
-	/* Step 5: Map the rest (if any) right below KERNBASE. */
+	/* Step 4: Map the rest (if any) right below KERNBASE. */
 	MKTRACE(("map_kernel: doing low mappings... "));
-	tally = 0;
-	for(entry = 0; sp_banks[entry].num_bytes; entry++) {
-		if(!etaken[entry])
-			tally += SRMMU_PGDIR_ALIGN(sp_banks[entry].num_bytes);
-	}
-	if(!tally)
-		memprobe_error("Whee, lots_of_ram yet no low pages to map.\n");
-	low_base = (KERNBASE - tally);
-	MKTRACE(("tally=%08lx low_base=%08lx\n", tally, low_base));
+	low_base = (KERNBASE - end_of_phys_memory + 0x0d000000);
+	MKTRACE(("end_of_phys_memory=%08lx low_base=%08lx\n", end_of_phys_memory, low_base));
 
 	/* Ok, now map 'em. */
 	MKTRACE(("map_kernel: Allocate pt skeleton (%08lx, %08x)\n",low_base,KERNBASE));
 	srmmu_allocate_ptable_skeleton(low_base, KERNBASE);
 	vaddr = low_base;
+	map_spbank_last_pa = 0xff000000;
 	MKTRACE(("map_kernel: vaddr=%08lx Entering second loop for low maps.\n", vaddr));
 	for(;;) {
 		unsigned long bank_size;
@@ -1848,19 +1783,22 @@
 		MKTRACE(("map_kernel: e<%d> base=%08lx bs=%08lx ", entry, sp_banks[entry].base_addr, bank_size));
 		if(!bank_size)
 			break;
+		if (sp_banks[entry].base_addr & (~SRMMU_PGDIR_MASK)) {
+			if (map_spbank_last_pa == (sp_banks[entry].base_addr & SRMMU_PGDIR_MASK))
+				vaddr -= SRMMU_PGDIR_SIZE;
+			vaddr += (sp_banks[entry].base_addr & (~SRMMU_PGDIR_MASK));
+		}
 		if((vaddr + bank_size) > KERNBASE)
 			memprobe_error("Wheee, kernel low mapping overflow.\n");
 		MKTRACE(("map_spbank(%08lx, %d) ", vaddr, entry));
 		vaddr = map_spbank(vaddr, entry);
 		etaken[entry] = 1;
-		tally -= SRMMU_PGDIR_ALIGN(bank_size);
-		MKTRACE(("Now, vaddr=%08lx tally=%08lx\n", vaddr, tally));
+		MKTRACE(("Now, vaddr=%08lx end_of_phys_memory=%08lx\n", vaddr, end_of_phys_memory));
 	}
 	MKTRACE(("\n"));
-	if(tally)
-		memprobe_error("Wheee, did not map all of low mappings.\n");
+
 check_and_return:
-	/* Step 6: Sanity check, make sure we did it all. */
+	/* Step 5: Sanity check, make sure we did it all. */
 	MKTRACE(("check_and_return: "));
 	for(entry = 0; sp_banks[entry].num_bytes; entry++) {
 		MKTRACE(("e[%d]=%d ", entry, etaken[entry]));
@@ -1872,6 +1810,10 @@
 	MKTRACE(("success\n"));
 	init_task.mm->mmap->vm_start = page_offset = low_base;
 	stack_top = page_offset - PAGE_SIZE;
+	BTFIXUPSET_SETHI(page_offset, low_base);
+	BTFIXUPSET_SETHI(stack_top, page_offset - PAGE_SIZE);
+	BTFIXUPSET_SIMM13(user_ptrs_per_pgd, page_offset / SRMMU_PGDIR_SIZE);
+	
 #if 1
 	for(entry = 0; srmmu_map[entry].size; entry++) {
 		printk("[%d]: v[%08lx,%08lx](%lx) p[%08lx]\n", entry,
@@ -1884,90 +1826,73 @@
 
 	/* Now setup the p2v/v2p hash tables. */
 	for(entry = 0; entry < SRMMU_HASHSZ; entry++)
-		srmmu_v2p_hash[entry] = srmmu_p2v_hash[entry] = NULL;
+		srmmu_v2p_hash[entry] = ((0xff - entry) << 24);
+	for(entry = 0; entry < SRMMU_HASHSZ; entry++)
+		srmmu_p2v_hash[entry] = 0xffffffffUL;
 	for(entry = 0; srmmu_map[entry].size; entry++) {
 		unsigned long addr;
 
 		for(addr = srmmu_map[entry].vbase;
 		    addr < (srmmu_map[entry].vbase + srmmu_map[entry].size);
 		    addr += (1 << 24))
-			srmmu_v2p_hash[srmmu_ahashfn(addr)] = &srmmu_map[entry];
+			srmmu_v2p_hash[srmmu_ahashfn(addr)] =
+				srmmu_map[entry].pbase - srmmu_map[entry].vbase;
 		for(addr = srmmu_map[entry].pbase;
 		    addr < (srmmu_map[entry].pbase + srmmu_map[entry].size);
 		    addr += (1 << 24))
-			srmmu_p2v_hash[srmmu_ahashfn(addr)] = &srmmu_map[entry];
+			srmmu_p2v_hash[srmmu_ahashfn(addr)] = 
+				srmmu_map[entry].pbase - srmmu_map[entry].vbase;
 	}
 
-	page_contig_offset = page_offset - (0xfd000000 - KERNBASE);
-	phys_mem_contig = 1;
-	for(entry = 0; srmmu_map[entry].size; entry++)
-		if (srmmu_map[entry].pbase != srmmu_c_v2p (srmmu_map[entry].vbase)) {
-			phys_mem_contig = 0;
-			break;
-		}
-	if (phys_mem_contig) {
-		printk ("SRMMU: Physical memory is contiguous, bypassing VA<->PA hashes\n");
-		pte_page = srmmu_c_pte_page;
-		pmd_page = srmmu_c_pmd_page;
-		pgd_page = srmmu_c_pgd_page;
-		mk_pte = srmmu_c_mk_pte;
-		pte_offset = srmmu_c_pte_offset;
-		pmd_offset = srmmu_c_pmd_offset;
-		if (ctxd_set == srmmu_ctxd_set)
-			ctxd_set = srmmu_c_ctxd_set;
-		pgd_set = srmmu_c_pgd_set;
-		pmd_set = srmmu_c_pmd_set;
-		mmu_v2p = srmmu_c_v2p;
-		mmu_p2v = srmmu_c_p2v;
-		if (flush_chunk == viking_flush_chunk)
-			flush_chunk = viking_c_flush_chunk;
-	}
-	
-	if (sparc_cpu_model == sun4d) {
-		int i, j = -1;
-		unsigned long bank_start, bank_end;
-		
-		sun4d_dma_vbase = 0;
-		sun4d_dma_size = IOUNIT_DMA_SIZE - IOUNIT_DVMA_SIZE;
-		for (i = 0; srmmu_map[i].size; i++) {
-			bank_start = srmmu_map[i].vbase;
-			bank_end = bank_start + srmmu_map[i].size;
-			if (bank_start <= KERNBASE && bank_end > KERNBASE)
-				j = i;
-			else if (srmmu_map[i].size >= sun4d_dma_size) {
-				sun4d_dma_vbase = srmmu_map[i].vbase;
+	BTFIXUPSET_SETHI(page_contig_offset, page_offset - (0xfd000000 - KERNBASE));
+	if (srmmu_low_pa)
+		phys_mem_contig = 0;
+	else {
+		phys_mem_contig = 1;
+		for(entry = 0; srmmu_map[entry].size; entry++)
+			if (srmmu_map[entry].pbase != srmmu_c_v2p (srmmu_map[entry].vbase)) {
+				phys_mem_contig = 0;
 				break;
 			}
-		}
-		if (!sun4d_dma_vbase && j != -1) {
-			if (srmmu_map[j].size >= sun4d_dma_size + 0x1000000)
-				sun4d_dma_vbase = srmmu_map[j].vbase + 0x1000000;
-			else {
-				sun4d_dma_vbase = srmmu_map[j].vbase;
-				if (srmmu_map[j].size < sun4d_dma_size)
-					sun4d_dma_size = srmmu_map[j].size;
-			}
-		}
-		sun4d_dma_base = IOUNIT_DMA_BASE - srmmu_v2p(sun4d_dma_vbase);
 	}
+	if (phys_mem_contig) {
+		printk ("SRMMU: Physical memory is contiguous, bypassing VA<->PA hashes.\n");
+		BTFIXUPSET_CALL(pte_page, srmmu_c_pte_page, BTFIXUPCALL_NORM);
+		BTFIXUPSET_CALL(pmd_page, srmmu_c_pmd_page, BTFIXUPCALL_NORM);
+		BTFIXUPSET_CALL(pgd_page, srmmu_c_pgd_page, BTFIXUPCALL_NORM);
+		BTFIXUPSET_CALL(mk_pte, srmmu_c_mk_pte, BTFIXUPCALL_NORM);
+		BTFIXUPSET_CALL(pte_offset, srmmu_c_pte_offset, BTFIXUPCALL_NORM);
+		BTFIXUPSET_CALL(pmd_offset, srmmu_c_pmd_offset, BTFIXUPCALL_NORM);
+		if (BTFIXUPVAL_CALL(ctxd_set) == (unsigned long)srmmu_ctxd_set)
+			BTFIXUPSET_CALL(ctxd_set, srmmu_c_ctxd_set, BTFIXUPCALL_NORM);
+		BTFIXUPSET_CALL(pgd_set, srmmu_c_pgd_set, BTFIXUPCALL_NORM);
+		BTFIXUPSET_CALL(pmd_set, srmmu_c_pmd_set, BTFIXUPCALL_NORM);
+		BTFIXUPSET_CALL(mmu_v2p, srmmu_c_v2p, BTFIXUPCALL_NORM);
+		BTFIXUPSET_CALL(mmu_p2v, srmmu_c_p2v, BTFIXUPCALL_NORM);
+		if (BTFIXUPVAL_CALL(flush_chunk) == (unsigned long)viking_flush_chunk)
+			BTFIXUPSET_CALL(flush_chunk, viking_c_flush_chunk, BTFIXUPCALL_NORM);
+	} else if (srmmu_low_pa) {
+		printk ("SRMMU: Compact physical memory. Using strightforward VA<->PA translations.\n");
+		BTFIXUPSET_CALL(pte_page, srmmu_s_pte_page, BTFIXUPCALL_NORM);
+		BTFIXUPSET_CALL(pmd_page, srmmu_s_pmd_page, BTFIXUPCALL_NORM);
+		BTFIXUPSET_CALL(pgd_page, srmmu_s_pgd_page, BTFIXUPCALL_NORM);
+		BTFIXUPSET_CALL(mk_pte, srmmu_s_mk_pte, BTFIXUPCALL_NORM);
+		BTFIXUPSET_CALL(pte_offset, srmmu_s_pte_offset, BTFIXUPCALL_NORM);
+		BTFIXUPSET_CALL(pmd_offset, srmmu_s_pmd_offset, BTFIXUPCALL_NORM);
+		if (BTFIXUPVAL_CALL(ctxd_set) == (unsigned long)srmmu_ctxd_set)
+			BTFIXUPSET_CALL(ctxd_set, srmmu_s_ctxd_set, BTFIXUPCALL_NORM);
+		BTFIXUPSET_CALL(pgd_set, srmmu_s_pgd_set, BTFIXUPCALL_NORM);
+		BTFIXUPSET_CALL(pmd_set, srmmu_s_pmd_set, BTFIXUPCALL_NORM);
+		BTFIXUPSET_CALL(mmu_v2p, srmmu_s_v2p, BTFIXUPCALL_NORM);
+		BTFIXUPSET_CALL(mmu_p2v, srmmu_s_p2v, BTFIXUPCALL_NORM);
+		if (BTFIXUPVAL_CALL(flush_chunk) == (unsigned long)viking_flush_chunk)
+			BTFIXUPSET_CALL(flush_chunk, viking_s_flush_chunk, BTFIXUPCALL_NORM);
+	}
+	btfixup();
 
 	return; /* SUCCESS! */
 }
 
-unsigned long srmmu_endmem_fixup(unsigned long mem_end_now)
-{
-	unsigned long tally = 0;
-	int i;
-
-	for(i = 0; sp_banks[i].num_bytes; i++)
-		tally += SRMMU_PGDIR_ALIGN(sp_banks[i].num_bytes);
-	if(tally < (0x0d000000UL)) {
-		return KERNBASE + tally;
-	} else {
-		return 0xfd000000UL;
-	}
-}
-
 /* Paging initialization on the Sparc Reference MMU. */
 extern unsigned long free_area_init(unsigned long, unsigned long);
 extern unsigned long sparc_context_init(unsigned long, int);
@@ -1975,9 +1900,9 @@
 extern int physmem_mapped_contig;
 extern int linux_num_cpus;
 
-void (*poke_srmmu)(void);
+void (*poke_srmmu)(void) __initdata = NULL;
 
-unsigned long srmmu_paging_init(unsigned long start_mem, unsigned long end_mem)
+__initfunc(unsigned long srmmu_paging_init(unsigned long start_mem, unsigned long end_mem))
 {
 	unsigned long ptables_start;
 	int i, cpunode;
@@ -2029,7 +1954,7 @@
 	start_mem = PAGE_ALIGN(mempool);
 
 	flush_cache_all();
-	if(flush_page_for_dma == viking_flush_page) {
+	if(BTFIXUPVAL_CALL(flush_page_for_dma) == (unsigned long)viking_flush_page) {
 		unsigned long start = ptables_start;
 		unsigned long end = start_mem;
 
@@ -2048,37 +1973,22 @@
 	return PAGE_ALIGN(start_mem);
 }
 
-static char srmmuinfo[512];
-
-static char *srmmu_mmu_info(void)
+static int srmmu_mmu_info(char *buf)
 {
-	sprintf(srmmuinfo, "MMU type\t: %s\n"
+	return sprintf(buf, 
+		"MMU type\t: %s\n"
 		"invall\t\t: %d\n"
 		"invmm\t\t: %d\n"
 		"invrnge\t\t: %d\n"
 		"invpg\t\t: %d\n"
 		"contexts\t: %d\n"
-#ifdef USE_CHUNK_ALLOC
-		"big chunks\t: %d\n"
-		"little chunks\t: %d\n"
-		"chunk pages\t: %d\n"
-		"garbage\t\t: %d\n"
-		"garbage hits\t: %d\n"
-#endif
 		, srmmu_name,
 		module_stats.invall,
 		module_stats.invmm,
 		module_stats.invrnge,
 		module_stats.invpg,
 		num_contexts
-#ifdef USE_CHUNK_ALLOC
-		, bcwater, lcwater,
-		chunk_pages,
-		garbage_calls,
-		clct_pages
-#endif
-		);
-	return srmmuinfo;
+	);
 }
 
 static void srmmu_update_mmu_cache(struct vm_area_struct * vma, unsigned long address, pte_t pte)
@@ -2242,7 +2152,7 @@
 	       (int)vac_cache_size, (int)vac_line_size);
 }
 
-static void poke_hypersparc(void)
+__initfunc(static void poke_hypersparc(void))
 {
 	volatile unsigned long clear;
 	unsigned long mreg = srmmu_get_mmureg();
@@ -2271,35 +2181,38 @@
 
 	init_vac_layout();
 
-	set_pte = srmmu_set_pte_nocache_hyper;
-	flush_cache_all = hypersparc_flush_cache_all;
-	flush_cache_mm = hypersparc_flush_cache_mm;
-	flush_cache_range = hypersparc_flush_cache_range;
-	flush_cache_page = hypersparc_flush_cache_page;
-
-	flush_tlb_all = hypersparc_flush_tlb_all;
-	flush_tlb_mm = hypersparc_flush_tlb_mm;
-	flush_tlb_range = hypersparc_flush_tlb_range;
-	flush_tlb_page = hypersparc_flush_tlb_page;
-
-	flush_page_to_ram = hypersparc_flush_page_to_ram;
-	flush_sig_insns = hypersparc_flush_sig_insns;
-	flush_page_for_dma = NULL /* hypersparc_flush_page_for_dma */;
-
-	flush_chunk = hypersparc_flush_chunk; /* local flush _only_ */
-
-	ctxd_set = hypersparc_ctxd_set;
-	switch_to_context = hypersparc_switch_to_context;
-	init_new_context = hypersparc_init_new_context;
-	destroy_context = hypersparc_destroy_context;
-	update_mmu_cache = srmmu_vac_update_mmu_cache;
-	sparc_update_rootmmu_dir = hypersparc_update_rootmmu_dir;
+	BTFIXUPSET_CALL(set_pte, srmmu_set_pte_nocache_hyper, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pte_clear, srmmu_pte_clear, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pmd_clear, srmmu_pmd_clear, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pgd_clear, srmmu_pgd_clear, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_cache_all, hypersparc_flush_cache_all, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_cache_mm, hypersparc_flush_cache_mm, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_cache_range, hypersparc_flush_cache_range, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_cache_page, hypersparc_flush_cache_page, BTFIXUPCALL_NORM);
+
+	BTFIXUPSET_CALL(flush_tlb_all, hypersparc_flush_tlb_all, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_tlb_mm, hypersparc_flush_tlb_mm, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_tlb_range, hypersparc_flush_tlb_range, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_tlb_page, hypersparc_flush_tlb_page, BTFIXUPCALL_NORM);
+
+	BTFIXUPSET_CALL(flush_page_to_ram, hypersparc_flush_page_to_ram, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_sig_insns, hypersparc_flush_sig_insns, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_page_for_dma, hypersparc_flush_page_for_dma, BTFIXUPCALL_NOP);
+
+	BTFIXUPSET_CALL(flush_chunk, hypersparc_flush_chunk, BTFIXUPCALL_NORM); /* local flush _only_ */
+
+	BTFIXUPSET_CALL(ctxd_set, hypersparc_ctxd_set, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(switch_to_context, hypersparc_switch_to_context, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(init_new_context, hypersparc_init_new_context, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(destroy_context, hypersparc_destroy_context, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(update_mmu_cache, srmmu_vac_update_mmu_cache, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(sparc_update_rootmmu_dir, hypersparc_update_rootmmu_dir, BTFIXUPCALL_NORM);
 	poke_srmmu = poke_hypersparc;
 
 	hypersparc_setup_blockops();
 }
 
-static void poke_cypress(void)
+__initfunc(static void poke_cypress(void))
 {
 	unsigned long mreg = srmmu_get_mmureg();
 	unsigned long faddr, tagval;
@@ -2342,25 +2255,28 @@
 {
 	init_vac_layout();
 
-	set_pte = srmmu_set_pte_nocache_cypress;
-	flush_cache_all = cypress_flush_cache_all;
-	flush_cache_mm = cypress_flush_cache_mm;
-	flush_cache_range = cypress_flush_cache_range;
-	flush_cache_page = cypress_flush_cache_page;
-
-	flush_tlb_all = cypress_flush_tlb_all;
-	flush_tlb_mm = cypress_flush_tlb_mm;
-	flush_tlb_page = cypress_flush_tlb_page;
-	flush_tlb_range = cypress_flush_tlb_range;
-
-	flush_chunk = cypress_flush_chunk; /* local flush _only_ */
-
-	flush_page_to_ram = cypress_flush_page_to_ram;
-	flush_sig_insns = cypress_flush_sig_insns;
-	flush_page_for_dma = NULL /* cypress_flush_page_for_dma */;
-	sparc_update_rootmmu_dir = cypress_update_rootmmu_dir;
+	BTFIXUPSET_CALL(set_pte, srmmu_set_pte_nocache_cypress, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pte_clear, srmmu_pte_clear, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pmd_clear, srmmu_pmd_clear, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pgd_clear, srmmu_pgd_clear, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_cache_all, cypress_flush_cache_all, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_cache_mm, cypress_flush_cache_mm, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_cache_range, cypress_flush_cache_range, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_cache_page, cypress_flush_cache_page, BTFIXUPCALL_NORM);
+
+	BTFIXUPSET_CALL(flush_tlb_all, cypress_flush_tlb_all, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_tlb_mm, cypress_flush_tlb_mm, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_tlb_page, cypress_flush_tlb_page, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_tlb_range, cypress_flush_tlb_range, BTFIXUPCALL_NORM);
+
+	BTFIXUPSET_CALL(flush_chunk, cypress_flush_chunk, BTFIXUPCALL_NORM); /* local flush _only_ */
+
+	BTFIXUPSET_CALL(flush_page_to_ram, cypress_flush_page_to_ram, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_sig_insns, cypress_flush_sig_insns, BTFIXUPCALL_NOP);
+	BTFIXUPSET_CALL(flush_page_for_dma, cypress_flush_page_for_dma, BTFIXUPCALL_NOP);
+	BTFIXUPSET_CALL(sparc_update_rootmmu_dir, cypress_update_rootmmu_dir, BTFIXUPCALL_NORM);
 
-	update_mmu_cache = srmmu_vac_update_mmu_cache;
+	BTFIXUPSET_CALL(update_mmu_cache, srmmu_vac_update_mmu_cache, BTFIXUPCALL_NORM);
 	poke_srmmu = poke_cypress;
 }
 
@@ -2388,7 +2304,7 @@
 	init_cypress_common();
 }
 
-static void poke_swift(void)
+__initfunc(static void poke_swift(void))
 {
 	unsigned long mreg = srmmu_get_mmureg();
 
@@ -2456,21 +2372,23 @@
 		break;
 	};
 
-	flush_cache_all = swift_flush_cache_all;
-	flush_cache_mm = swift_flush_cache_mm;
-	flush_cache_page = swift_flush_cache_page;
-	flush_cache_range = swift_flush_cache_range;
-
-	flush_chunk = swift_flush_chunk; /* local flush _only_ */
-
-	flush_tlb_all = swift_flush_tlb_all;
-	flush_tlb_mm = swift_flush_tlb_mm;
-	flush_tlb_page = swift_flush_tlb_page;
-	flush_tlb_range = swift_flush_tlb_range;
-
-	flush_page_to_ram = swift_flush_page_to_ram;
-	flush_sig_insns = swift_flush_sig_insns;
-	flush_page_for_dma = swift_flush_page_for_dma;
+	BTFIXUPSET_CALL(flush_cache_all, swift_flush_cache_all, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_cache_mm, swift_flush_cache_mm, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_cache_page, swift_flush_cache_page, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_cache_range, swift_flush_cache_range, BTFIXUPCALL_NORM);
+
+	BTFIXUPSET_CALL(flush_chunk, swift_flush_chunk, BTFIXUPCALL_NOP); /* local flush _only_ */
+
+	BTFIXUPSET_CALL(flush_tlb_all, swift_flush_tlb_all, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_tlb_mm, swift_flush_tlb_mm, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_tlb_page, swift_flush_tlb_page, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_tlb_range, swift_flush_tlb_range, BTFIXUPCALL_NORM);
+
+	BTFIXUPSET_CALL(flush_page_to_ram, swift_flush_page_to_ram, BTFIXUPCALL_NOP);
+	BTFIXUPSET_CALL(flush_sig_insns, swift_flush_sig_insns, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_page_for_dma, swift_flush_page_for_dma, BTFIXUPCALL_NORM);
+
+	BTFIXUPSET_CALL(update_mmu_cache, swift_update_mmu_cache, BTFIXUPCALL_NORM);
 
 	/* Are you now convinced that the Swift is one of the
 	 * biggest VLSI abortions of all time?  Bravo Fujitsu!
@@ -2484,8 +2402,9 @@
 /* turbosparc.S */
 extern void turbosparc_flush_cache_all(void);
 extern void turbosparc_flush_sig_insns(struct mm_struct *mm, unsigned long insn_addr);
+extern void turbosparc_flush_page_for_dma(unsigned long page);
 
-static void poke_turbosparc(void)
+__initfunc(static void poke_turbosparc(void))
 {
 	unsigned long mreg = srmmu_get_mmureg();
 	unsigned long ccreg;
@@ -2529,31 +2448,31 @@
 	srmmu_name = "Fujitsu TurboSparc";
 	srmmu_modtype = TurboSparc;
 
-	flush_cache_all = turbosparc_flush_cache_all;
-	flush_cache_mm = hypersparc_flush_cache_mm;
-	flush_cache_page = hypersparc_flush_cache_page;
-	flush_cache_range = hypersparc_flush_cache_range;
-
-	flush_tlb_all = hypersparc_flush_tlb_all;
-	flush_tlb_mm = hypersparc_flush_tlb_mm;
-	flush_tlb_page = hypersparc_flush_tlb_page;
-	flush_tlb_range = hypersparc_flush_tlb_range;
+	BTFIXUPSET_CALL(flush_cache_all, turbosparc_flush_cache_all, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_cache_mm, hypersparc_flush_cache_mm, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_cache_page, hypersparc_flush_cache_page, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_cache_range, hypersparc_flush_cache_range, BTFIXUPCALL_NORM);
+
+	BTFIXUPSET_CALL(flush_tlb_all, hypersparc_flush_tlb_all, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_tlb_mm, hypersparc_flush_tlb_mm, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_tlb_page, hypersparc_flush_tlb_page, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_tlb_range, hypersparc_flush_tlb_range, BTFIXUPCALL_NORM);
 
 #ifdef TURBOSPARC_WRITEBACK
-	flush_page_to_ram = hypersparc_flush_page_to_ram;
-	flush_chunk = hypersparc_flush_chunk;
+	BTFIXUPSET_CALL(flush_page_to_ram, hypersparc_flush_page_to_ram, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_chunk, hypersparc_flush_chunk, BTFIXUPCALL_NORM);
 #else
-	flush_page_to_ram = swift_flush_page_to_ram;
-	flush_chunk = swift_flush_chunk;
+	BTFIXUPSET_CALL(flush_page_to_ram, swift_flush_page_to_ram, BTFIXUPCALL_NOP);
+	BTFIXUPSET_CALL(flush_chunk, swift_flush_chunk, BTFIXUPCALL_NOP);
 #endif
 
-	flush_sig_insns = turbosparc_flush_sig_insns;
-	flush_page_for_dma = NULL /* turbosparc_flush_page_for_dma */;
+	BTFIXUPSET_CALL(flush_sig_insns, turbosparc_flush_sig_insns, BTFIXUPCALL_NOP);
+	BTFIXUPSET_CALL(flush_page_for_dma, turbosparc_flush_page_for_dma, BTFIXUPCALL_NOP);
 
 	poke_srmmu = poke_turbosparc;
 }
 
-static void poke_tsunami(void)
+__initfunc(static void poke_tsunami(void))
 {
 	unsigned long mreg = srmmu_get_mmureg();
 
@@ -2574,26 +2493,26 @@
 	srmmu_name = "TI Tsunami";
 	srmmu_modtype = Tsunami;
 
-	flush_cache_all = tsunami_flush_cache_all;
-	flush_cache_mm = tsunami_flush_cache_mm;
-	flush_cache_page = tsunami_flush_cache_page;
-	flush_cache_range = tsunami_flush_cache_range;
-
-	flush_chunk = tsunami_flush_chunk; /* local flush _only_ */
-
-	flush_tlb_all = tsunami_flush_tlb_all;
-	flush_tlb_mm = tsunami_flush_tlb_mm;
-	flush_tlb_page = tsunami_flush_tlb_page;
-	flush_tlb_range = tsunami_flush_tlb_range;
-
-	flush_page_to_ram = tsunami_flush_page_to_ram;
-	flush_sig_insns = tsunami_flush_sig_insns;
-	flush_page_for_dma = tsunami_flush_page_for_dma;
+	BTFIXUPSET_CALL(flush_cache_all, tsunami_flush_cache_all, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_cache_mm, tsunami_flush_cache_mm, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_cache_page, tsunami_flush_cache_page, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_cache_range, tsunami_flush_cache_range, BTFIXUPCALL_NORM);
+
+	BTFIXUPSET_CALL(flush_chunk, tsunami_flush_chunk, BTFIXUPCALL_NOP); /* local flush _only_ */
+
+	BTFIXUPSET_CALL(flush_tlb_all, tsunami_flush_tlb_all, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_tlb_mm, tsunami_flush_tlb_mm, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_tlb_page, tsunami_flush_tlb_page, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_tlb_range, tsunami_flush_tlb_range, BTFIXUPCALL_NORM);
+
+	BTFIXUPSET_CALL(flush_page_to_ram, tsunami_flush_page_to_ram, BTFIXUPCALL_NOP);
+	BTFIXUPSET_CALL(flush_sig_insns, tsunami_flush_sig_insns, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_page_for_dma, tsunami_flush_page_for_dma, BTFIXUPCALL_NORM);
 
 	poke_srmmu = poke_tsunami;
 }
 
-static void poke_viking(void)
+__initfunc(static void poke_viking(void))
 {
 	unsigned long mreg = srmmu_get_mmureg();
 	static int smp_catch = 0;
@@ -2637,13 +2556,14 @@
 
 #ifdef __SMP__
 	/* Avoid unnecessary cross calls. */
-	flush_cache_all = local_flush_cache_all;
-	flush_cache_mm = local_flush_cache_mm;
-	flush_cache_range = local_flush_cache_range;
-	flush_cache_page = local_flush_cache_page;
-	flush_page_to_ram = local_flush_page_to_ram;
-	flush_sig_insns = local_flush_sig_insns;
-	flush_page_for_dma = local_flush_page_for_dma;
+	BTFIXUPCOPY_CALL(flush_cache_all, local_flush_cache_all);
+	BTFIXUPCOPY_CALL(flush_cache_mm, local_flush_cache_mm);
+	BTFIXUPCOPY_CALL(flush_cache_range, local_flush_cache_range);
+	BTFIXUPCOPY_CALL(flush_cache_page, local_flush_cache_page);
+	BTFIXUPCOPY_CALL(flush_page_to_ram, local_flush_page_to_ram);
+	BTFIXUPCOPY_CALL(flush_sig_insns, local_flush_sig_insns);
+	BTFIXUPCOPY_CALL(flush_page_for_dma, local_flush_page_for_dma);
+	btfixup();
 #endif
 }
 
@@ -2664,10 +2584,13 @@
 
 		msi_set_sync();
 
-		set_pte = srmmu_set_pte_nocache_viking;
-		sparc_update_rootmmu_dir = viking_update_rootmmu_dir;
+		BTFIXUPSET_CALL(set_pte, srmmu_set_pte_nocache_viking, BTFIXUPCALL_NORM);
+		BTFIXUPSET_CALL(pte_clear, srmmu_pte_clear, BTFIXUPCALL_NORM);
+		BTFIXUPSET_CALL(pmd_clear, srmmu_pmd_clear, BTFIXUPCALL_NORM);
+		BTFIXUPSET_CALL(pgd_clear, srmmu_pgd_clear, BTFIXUPCALL_NORM);
+		BTFIXUPSET_CALL(sparc_update_rootmmu_dir, viking_update_rootmmu_dir, BTFIXUPCALL_NORM);
 
-		flush_chunk = viking_flush_chunk; /* local flush _only_ */
+		BTFIXUPSET_CALL(flush_chunk, viking_flush_chunk, BTFIXUPCALL_NORM); /* local flush _only_ */
 
 		/* We need this to make sure old viking takes no hits
 		 * on it's cache for dma snoops to workaround the
@@ -2675,7 +2598,7 @@
 		 * This is only necessary because of the new way in
 		 * which we use the IOMMU.
 		 */
-		flush_page_for_dma = viking_flush_page;
+		BTFIXUPSET_CALL(flush_page_for_dma, viking_flush_page, BTFIXUPCALL_NORM);
 		/* Also, this is so far the only chip which actually uses
 		   the page argument to flush_page_for_dma */
 		flush_page_for_dma_global = 0;
@@ -2683,24 +2606,25 @@
 		srmmu_name = "TI Viking/MXCC";
 		viking_mxcc_present = 1;
 
-		flush_chunk = viking_mxcc_flush_chunk; /* local flush _only_ */
+		BTFIXUPSET_CALL(flush_chunk, viking_mxcc_flush_chunk, BTFIXUPCALL_NOP); /* local flush _only_ */
 
 		/* MXCC vikings lack the DMA snooping bug. */
-		flush_page_for_dma = NULL /* viking_flush_page_for_dma */;
+		BTFIXUPSET_CALL(flush_page_for_dma, viking_flush_page_for_dma, BTFIXUPCALL_NOP);
 	}
 
-	flush_cache_all = viking_flush_cache_all;
-	flush_cache_mm = viking_flush_cache_mm;
-	flush_cache_page = viking_flush_cache_page;
-	flush_cache_range = viking_flush_cache_range;
+	/* flush_cache_* are nops */
+	BTFIXUPSET_CALL(flush_cache_all, viking_flush_cache_all, BTFIXUPCALL_NOP);
+	BTFIXUPSET_CALL(flush_cache_mm, viking_flush_cache_mm, BTFIXUPCALL_NOP);
+	BTFIXUPSET_CALL(flush_cache_page, viking_flush_cache_page, BTFIXUPCALL_NOP);
+	BTFIXUPSET_CALL(flush_cache_range, viking_flush_cache_range, BTFIXUPCALL_NOP);
+
+	BTFIXUPSET_CALL(flush_tlb_all, viking_flush_tlb_all, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_tlb_mm, viking_flush_tlb_mm, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_tlb_page, viking_flush_tlb_page, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_tlb_range, viking_flush_tlb_range, BTFIXUPCALL_NORM);
 
-	flush_tlb_all = viking_flush_tlb_all;
-	flush_tlb_mm = viking_flush_tlb_mm;
-	flush_tlb_page = viking_flush_tlb_page;
-	flush_tlb_range = viking_flush_tlb_range;
-
-	flush_page_to_ram = viking_flush_page_to_ram;
-	flush_sig_insns = viking_flush_sig_insns;
+	BTFIXUPSET_CALL(flush_page_to_ram, viking_flush_page_to_ram, BTFIXUPCALL_NOP);
+	BTFIXUPSET_CALL(flush_sig_insns, viking_flush_sig_insns, BTFIXUPCALL_NOP);
 
 	poke_srmmu = poke_viking;
 }
@@ -2798,6 +2722,67 @@
 	srmmu_is_bad();
 }
 
+/* Low and high watermarks for page table cache.
+   The system should try to have pgt_water[0] <= cache elements <= pgt_water[1]
+ */
+extern int pgt_cache_water[2];
+
+void srmmu_check_pgt_cache(void)
+{
+	struct page *page, *page2;
+
+	if (pgtable_cache_size > pgt_cache_water[0]) {
+		spin_lock(&pte_spinlock);
+		for (page2 = NULL, page = (struct page *)pte_quicklist; page;) {
+			if ((unsigned int)page->pprev_hash == 0xffff) {
+				if (page2)
+					page2->next_hash = page->next_hash;
+				else
+					(struct page *)pte_quicklist = page->next_hash;
+				page->next_hash = NULL;
+				page->pprev_hash = NULL;
+				pgtable_cache_size -= 16;
+				free_page(PAGE_OFFSET + (page->map_nr << PAGE_SHIFT));
+				if (page2)
+					page = page2->next_hash;
+				else
+					page = (struct page *)pte_quicklist;
+				if (pgtable_cache_size <= pgt_cache_water[1])
+					break;
+				continue;
+			}
+			page2 = page;
+			page = page->next_hash;
+		}
+		spin_unlock(&pte_spinlock);
+	}
+	if (pgd_cache_size > pgt_cache_water[0] / 4) {
+		spin_lock(&pgd_spinlock);
+		for (page2 = NULL, page = (struct page *)pgd_quicklist; page;) {
+			if ((unsigned int)page->pprev_hash == 0xf) {
+				if (page2)
+					page2->next_hash = page->next_hash;
+				else
+					(struct page *)pgd_quicklist = page->next_hash;
+				page->next_hash = NULL;
+				page->pprev_hash = NULL;
+				pgd_cache_size -= 4;
+				free_page(PAGE_OFFSET + (page->map_nr << PAGE_SHIFT));
+				if (page2)
+					page = page2->next_hash;
+				else
+					page = (struct page *)pgd_quicklist;
+				if (pgd_cache_size <= pgt_cache_water[1] / 4)
+					break;
+				continue;
+			}
+			page2 = page;
+			page = page->next_hash;
+		}
+		spin_unlock(&pgd_spinlock);
+	}
+}
+
 extern unsigned long spwin_mmu_patchme, fwin_mmu_patchme,
 	tsetup_mmu_patchme, rtrap_mmu_patchme;
 
@@ -2810,7 +2795,7 @@
 		iaddr = &(insn); \
 		daddr = &(dest); \
 		*iaddr = SPARC_BRANCH((unsigned long) daddr, (unsigned long) iaddr); \
-        } while(0);
+	} while(0);
 
 __initfunc(static void patch_window_trap_handlers(void))
 {
@@ -2829,7 +2814,7 @@
 /* Local cross-calls. */
 static void smp_flush_page_for_dma(unsigned long page)
 {
-	xc1((smpfunc_t) local_flush_page_for_dma, page);
+	xc1((smpfunc_t) BTFIXUP_CALL(local_flush_page_for_dma), page);
 }
 
 #endif
@@ -2839,98 +2824,107 @@
 {
 	extern void ld_mmu_iommu(void);
 	extern void ld_mmu_iounit(void);
+	extern void ___xchg32_sun4md(void);
 	
 	/* First the constants */
-	pmd_shift = SRMMU_PMD_SHIFT;
-	pmd_size = SRMMU_PMD_SIZE;
-	pmd_mask = SRMMU_PMD_MASK;
-	pgdir_shift = SRMMU_PGDIR_SHIFT;
-	pgdir_size = SRMMU_PGDIR_SIZE;
-	pgdir_mask = SRMMU_PGDIR_MASK;
-
-	ptrs_per_pte = SRMMU_PTRS_PER_PTE;
-	ptrs_per_pmd = SRMMU_PTRS_PER_PMD;
-	ptrs_per_pgd = SRMMU_PTRS_PER_PGD;
-
-	page_none = SRMMU_PAGE_NONE;
-	page_shared = SRMMU_PAGE_SHARED;
-	page_copy = SRMMU_PAGE_COPY;
-	page_readonly = SRMMU_PAGE_RDONLY;
-	page_kernel = SRMMU_PAGE_KERNEL;
+	BTFIXUPSET_SIMM13(pmd_shift, SRMMU_PMD_SHIFT);
+	BTFIXUPSET_SETHI(pmd_size, SRMMU_PMD_SIZE);
+	BTFIXUPSET_SETHI(pmd_mask, SRMMU_PMD_MASK);
+	BTFIXUPSET_SIMM13(pgdir_shift, SRMMU_PGDIR_SHIFT);
+	BTFIXUPSET_SETHI(pgdir_size, SRMMU_PGDIR_SIZE);
+	BTFIXUPSET_SETHI(pgdir_mask, SRMMU_PGDIR_MASK);
+
+	BTFIXUPSET_SIMM13(ptrs_per_pte, SRMMU_PTRS_PER_PTE);
+	BTFIXUPSET_SIMM13(ptrs_per_pmd, SRMMU_PTRS_PER_PMD);
+	BTFIXUPSET_SIMM13(ptrs_per_pgd, SRMMU_PTRS_PER_PGD);
+
+	BTFIXUPSET_INT(page_none, pgprot_val(SRMMU_PAGE_NONE));
+	BTFIXUPSET_INT(page_shared, pgprot_val(SRMMU_PAGE_SHARED));
+	BTFIXUPSET_INT(page_copy, pgprot_val(SRMMU_PAGE_COPY));
+	BTFIXUPSET_INT(page_readonly, pgprot_val(SRMMU_PAGE_RDONLY));
+	BTFIXUPSET_INT(page_kernel, pgprot_val(SRMMU_PAGE_KERNEL));
 	pg_iobits = SRMMU_VALID | SRMMU_WRITE | SRMMU_REF;
-	    
+	
 	/* Functions */
-	set_pte = srmmu_set_pte_cacheable;
-	init_new_context = srmmu_init_new_context;
-	switch_to_context = srmmu_switch_to_context;
-	pmd_align = srmmu_pmd_align;
-	pgdir_align = srmmu_pgdir_align;
-	vmalloc_start = srmmu_vmalloc_start;
-
-	pte_page = srmmu_pte_page;
-	pmd_page = srmmu_pmd_page;
-	pgd_page = srmmu_pgd_page;
-
-	sparc_update_rootmmu_dir = srmmu_update_rootmmu_dir;
-
-	pte_none = srmmu_pte_none;
-	pte_present = srmmu_pte_present;
-	pte_clear = srmmu_pte_clear;
-
-	pmd_none = srmmu_pmd_none;
-	pmd_bad = srmmu_pmd_bad;
-	pmd_present = srmmu_pmd_present;
-	pmd_clear = srmmu_pmd_clear;
-
-	pgd_none = srmmu_pgd_none;
-	pgd_bad = srmmu_pgd_bad;
-	pgd_present = srmmu_pgd_present;
-	pgd_clear = srmmu_pgd_clear;
-
-	mk_pte = srmmu_mk_pte;
-	mk_pte_phys = srmmu_mk_pte_phys;
-	pgd_set = srmmu_pgd_set;
-	mk_pte_io = srmmu_mk_pte_io;
-	pte_modify = srmmu_pte_modify;
-	pgd_offset = srmmu_pgd_offset;
-	pmd_offset = srmmu_pmd_offset;
-	pte_offset = srmmu_pte_offset;
-	pte_free_kernel = srmmu_pte_free_kernel;
-	pmd_free_kernel = srmmu_pmd_free_kernel;
-	pte_alloc_kernel = srmmu_pte_alloc_kernel;
-	pmd_alloc_kernel = srmmu_pmd_alloc_kernel;
-	pte_free = srmmu_pte_free;
-	pte_alloc = srmmu_pte_alloc;
-	pmd_free = srmmu_pmd_free;
-	pmd_alloc = srmmu_pmd_alloc;
-	pgd_free = srmmu_pgd_free;
-	pgd_alloc = srmmu_pgd_alloc;
-
-	pte_write = srmmu_pte_write;
-	pte_dirty = srmmu_pte_dirty;
-	pte_young = srmmu_pte_young;
-	pte_wrprotect = srmmu_pte_wrprotect;
-	pte_mkclean = srmmu_pte_mkclean;
-	pte_mkold = srmmu_pte_mkold;
-	pte_mkwrite = srmmu_pte_mkwrite;
-	pte_mkdirty = srmmu_pte_mkdirty;
-	pte_mkyoung = srmmu_pte_mkyoung;
-	update_mmu_cache = srmmu_update_mmu_cache;
-	destroy_context = srmmu_destroy_context;
+#ifndef __SMP__	
+	BTFIXUPSET_CALL(___xchg32, ___xchg32_sun4md, BTFIXUPCALL_SWAPG1G2);
+#endif
+	BTFIXUPSET_CALL(get_pte_fast, srmmu_get_pte_fast, BTFIXUPCALL_RETINT(0));
+	BTFIXUPSET_CALL(get_pgd_fast, srmmu_get_pgd_fast, BTFIXUPCALL_RETINT(0));
+	BTFIXUPSET_CALL(free_pte_slow, srmmu_free_pte_slow, BTFIXUPCALL_NOP);
+	BTFIXUPSET_CALL(free_pgd_slow, srmmu_free_pgd_slow, BTFIXUPCALL_NOP);
+	
+	BTFIXUPSET_CALL(set_pgdir, srmmu_set_pgdir, BTFIXUPCALL_NORM);
+	    
+	BTFIXUPSET_CALL(set_pte, srmmu_set_pte_cacheable, BTFIXUPCALL_SWAPO0O1);
+	BTFIXUPSET_CALL(init_new_context, srmmu_init_new_context, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(switch_to_context, srmmu_switch_to_context, BTFIXUPCALL_NORM);
+
+	BTFIXUPSET_CALL(pte_page, srmmu_pte_page, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pmd_page, srmmu_pmd_page, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pgd_page, srmmu_pgd_page, BTFIXUPCALL_NORM);
+
+	BTFIXUPSET_CALL(sparc_update_rootmmu_dir, srmmu_update_rootmmu_dir, BTFIXUPCALL_NORM);
+
+	BTFIXUPSET_SETHI(none_mask, 0xF0000000);
+	
+	BTFIXUPSET_CALL(pte_present, srmmu_pte_present, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pte_clear, srmmu_pte_clear, BTFIXUPCALL_SWAPO0G0);
+
+	BTFIXUPSET_CALL(pmd_bad, srmmu_pmd_bad, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pmd_present, srmmu_pmd_present, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pmd_clear, srmmu_pmd_clear, BTFIXUPCALL_SWAPO0G0);
+
+	BTFIXUPSET_CALL(pgd_none, srmmu_pgd_none, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pgd_bad, srmmu_pgd_bad, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pgd_present, srmmu_pgd_present, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pgd_clear, srmmu_pgd_clear, BTFIXUPCALL_SWAPO0G0);
+
+	BTFIXUPSET_CALL(mk_pte, srmmu_mk_pte, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(mk_pte_phys, srmmu_mk_pte_phys, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(mk_pte_io, srmmu_mk_pte_io, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pgd_set, srmmu_pgd_set, BTFIXUPCALL_NORM);
 	
-	mmu_info = srmmu_mmu_info;
-        mmu_v2p = srmmu_v2p;
-        mmu_p2v = srmmu_p2v;
+	BTFIXUPSET_INT(pte_modify_mask, SRMMU_CHG_MASK);
+	BTFIXUPSET_CALL(pgd_offset, srmmu_pgd_offset, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pmd_offset, srmmu_pmd_offset, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pte_offset, srmmu_pte_offset, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pte_free_kernel, srmmu_pte_free, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pmd_free_kernel, srmmu_pmd_free, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pte_alloc_kernel, srmmu_pte_alloc, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pmd_alloc_kernel, srmmu_pmd_alloc, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pte_free, srmmu_pte_free, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pte_alloc, srmmu_pte_alloc, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pmd_free, srmmu_pmd_free, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pmd_alloc, srmmu_pmd_alloc, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pgd_free, srmmu_pgd_free, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pgd_alloc, srmmu_pgd_alloc, BTFIXUPCALL_NORM);
+
+	BTFIXUPSET_HALF(pte_writei, SRMMU_WRITE);
+	BTFIXUPSET_HALF(pte_dirtyi, SRMMU_DIRTY);
+	BTFIXUPSET_HALF(pte_youngi, SRMMU_REF);
+	BTFIXUPSET_HALF(pte_wrprotecti, SRMMU_WRITE);
+	BTFIXUPSET_HALF(pte_mkcleani, SRMMU_DIRTY);
+	BTFIXUPSET_HALF(pte_mkoldi, SRMMU_REF);
+	BTFIXUPSET_CALL(pte_mkwrite, srmmu_pte_mkwrite, BTFIXUPCALL_ORINT(SRMMU_WRITE));
+	BTFIXUPSET_CALL(pte_mkdirty, srmmu_pte_mkdirty, BTFIXUPCALL_ORINT(SRMMU_DIRTY));
+	BTFIXUPSET_CALL(pte_mkyoung, srmmu_pte_mkyoung, BTFIXUPCALL_ORINT(SRMMU_REF));
+	BTFIXUPSET_CALL(update_mmu_cache, srmmu_update_mmu_cache, BTFIXUPCALL_NOP);
+	BTFIXUPSET_CALL(destroy_context, srmmu_destroy_context, BTFIXUPCALL_NORM);
+	
+	BTFIXUPSET_CALL(mmu_info, srmmu_mmu_info, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(mmu_v2p, srmmu_v2p, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(mmu_p2v, srmmu_p2v, BTFIXUPCALL_NORM);
 
 	/* Task struct and kernel stack allocating/freeing. */
-	alloc_task_struct = srmmu_alloc_task_struct;
-	free_task_struct = srmmu_free_task_struct;
+	BTFIXUPSET_CALL(alloc_task_struct, srmmu_alloc_task_struct, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(free_task_struct, srmmu_free_task_struct, BTFIXUPCALL_NORM);
 
-	quick_kernel_fault = srmmu_quick_kernel_fault;
+	BTFIXUPSET_CALL(quick_kernel_fault, srmmu_quick_kernel_fault, BTFIXUPCALL_NORM);
 
 	/* SRMMU specific. */
-	ctxd_set = srmmu_ctxd_set;
-	pmd_set = srmmu_pmd_set;
+	BTFIXUPSET_CALL(ctxd_set, srmmu_ctxd_set, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pmd_set, srmmu_pmd_set, BTFIXUPCALL_NORM);
 
 	get_srmmu_type();
 	patch_window_trap_handlers();
@@ -2938,33 +2932,38 @@
 #ifdef __SMP__
 	/* El switcheroo... */
 
-	local_flush_cache_all = flush_cache_all;
-	local_flush_cache_mm = flush_cache_mm;
-	local_flush_cache_range = flush_cache_range;
-	local_flush_cache_page = flush_cache_page;
-	local_flush_tlb_all = flush_tlb_all;
-	local_flush_tlb_mm = flush_tlb_mm;
-	local_flush_tlb_range = flush_tlb_range;
-	local_flush_tlb_page = flush_tlb_page;
-	local_flush_page_to_ram = flush_page_to_ram;
-	local_flush_sig_insns = flush_sig_insns;
-	local_flush_page_for_dma = flush_page_for_dma;
-
-	flush_cache_all = smp_flush_cache_all;
-	flush_cache_mm = smp_flush_cache_mm;
-	flush_cache_range = smp_flush_cache_range;
-	flush_cache_page = smp_flush_cache_page;
-	flush_tlb_all = smp_flush_tlb_all;
-	flush_tlb_mm = smp_flush_tlb_mm;
-	flush_tlb_range = smp_flush_tlb_range;
-	flush_tlb_page = smp_flush_tlb_page;
-	flush_page_to_ram = smp_flush_page_to_ram;
-	flush_sig_insns = smp_flush_sig_insns;
-	if (flush_page_for_dma)
-		flush_page_for_dma = smp_flush_page_for_dma;
+	BTFIXUPCOPY_CALL(local_flush_cache_all, flush_cache_all);
+	BTFIXUPCOPY_CALL(local_flush_cache_mm, flush_cache_mm);
+	BTFIXUPCOPY_CALL(local_flush_cache_range, flush_cache_range);
+	BTFIXUPCOPY_CALL(local_flush_cache_page, flush_cache_page);
+	BTFIXUPCOPY_CALL(local_flush_tlb_all, flush_tlb_all);
+	BTFIXUPCOPY_CALL(local_flush_tlb_mm, flush_tlb_mm);
+	BTFIXUPCOPY_CALL(local_flush_tlb_range, flush_tlb_range);
+	BTFIXUPCOPY_CALL(local_flush_tlb_page, flush_tlb_page);
+	BTFIXUPCOPY_CALL(local_flush_page_to_ram, flush_page_to_ram);
+	BTFIXUPCOPY_CALL(local_flush_sig_insns, flush_sig_insns);
+	BTFIXUPCOPY_CALL(local_flush_page_for_dma, flush_page_for_dma);
+
+	BTFIXUPSET_CALL(flush_cache_all, smp_flush_cache_all, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_cache_mm, smp_flush_cache_mm, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_cache_range, smp_flush_cache_range, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_cache_page, smp_flush_cache_page, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_tlb_all, smp_flush_tlb_all, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_tlb_mm, smp_flush_tlb_mm, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_tlb_range, smp_flush_tlb_range, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_tlb_page, smp_flush_tlb_page, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_page_to_ram, smp_flush_page_to_ram, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_sig_insns, smp_flush_sig_insns, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_page_for_dma, smp_flush_page_for_dma, BTFIXUPCALL_NORM);
 #endif
 	if (sparc_cpu_model == sun4d)
 		ld_mmu_iounit();
 	else
 		ld_mmu_iommu();
+#ifdef __SMP__
+	if (sparc_cpu_model == sun4d)
+		sun4d_init_smp();
+	else
+		sun4m_init_smp();
+#endif
 }
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/mm/sun4c.c linux/arch/sparc/mm/sun4c.c
--- v2.1.96/linux/arch/sparc/mm/sun4c.c	Fri Jan 16 20:38:57 1998
+++ linux/arch/sparc/mm/sun4c.c	Tue Apr 14 17:44:20 1998
@@ -1,9 +1,11 @@
-/* $Id: sun4c.c,v 1.149 1997/07/20 05:59:38 davem Exp $
+/* $Id: sun4c.c,v 1.163 1998/03/11 04:08:21 tdyas Exp $
  * sun4c.c: Doing in software what should be done in hardware.
  *
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
  * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be)
  * Copyright (C) 1996 Andrew Tridgell (Andrew.Tridgell@anu.edu.au)
+ * Copyright (C) 1997 Anton Blanchard (anton@progsoc.uts.edu.au)
+ * Copyright (C) 1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
  */
 
 #include <linux/kernel.h>
@@ -22,6 +24,7 @@
 #include <asm/oplib.h>
 #include <asm/openprom.h>
 #include <asm/mmu_context.h>
+#include <asm/sun4paddr.h>
 
 /* TODO: Make it such that interrupt handlers cannot dick with
  *       the user segment lists, most of the cli/sti pairs can
@@ -59,11 +62,15 @@
 	: "g4", "cc");							\
 } while(0);
 
-/* That's it, we prom_halt() if the cache size is something other than 65536.
+#ifdef CONFIG_SUN4
+#define SUN4C_VAC_SIZE sun4c_vacinfo.num_bytes
+#else
+/* That's it, we prom_halt() on sun4c if the cache size is something other than 65536.
  * So let's save some cycles and just use that everywhere except for that bootup
  * sanity check.
  */
-#define SUN4C_VAC_SIZE	65536
+#define SUN4C_VAC_SIZE 65536
+#endif
 
 #define SUN4C_KERNEL_BUCKETS 32
 
@@ -427,22 +434,76 @@
 	sun4c_set_context(savectx);
 }
 
-void sun4c_probe_vac(void)
+__initfunc(void sun4c_probe_vac(void))
 {
 	sun4c_disable_vac();
-	if((idprom->id_machtype == (SM_SUN4C | SM_4C_SS1)) ||
-	   (idprom->id_machtype == (SM_SUN4C | SM_4C_SS1PLUS))) {
-		/* PROM on SS1 lacks this info, to be super safe we
-		 * hard code it here since this arch is cast in stone.
-		 */
-		sun4c_vacinfo.num_bytes = 65536;
-		sun4c_vacinfo.linesize = 16;
+
+	if (ARCH_SUN4) {
+		switch(idprom->id_machtype) {
+
+		case (SM_SUN4|SM_4_110):
+			sun4c_vacinfo.type = NONE;
+			sun4c_vacinfo.num_bytes = 0;
+			sun4c_vacinfo.linesize = 0;
+			sun4c_vacinfo.do_hwflushes = 0;
+			prom_printf("No VAC. Get some bucks and buy a real computer.");
+			prom_halt();
+			break;
+
+		case (SM_SUN4|SM_4_260):
+			sun4c_vacinfo.type = WRITE_BACK;
+			sun4c_vacinfo.num_bytes = 128 * 1024;
+			sun4c_vacinfo.linesize = 16;
+			sun4c_vacinfo.do_hwflushes = 0;
+			break;
+
+		case (SM_SUN4|SM_4_330):
+			sun4c_vacinfo.type = WRITE_THROUGH;
+			sun4c_vacinfo.num_bytes = 128 * 1024;
+			sun4c_vacinfo.linesize = 16;
+			sun4c_vacinfo.do_hwflushes = 0;
+			break;
+
+		case (SM_SUN4|SM_4_470):
+			sun4c_vacinfo.type = WRITE_BACK;
+			sun4c_vacinfo.num_bytes = 128 * 1024;
+			sun4c_vacinfo.linesize = 32;
+			sun4c_vacinfo.do_hwflushes = 0;
+			break;
+
+		default:
+			prom_printf("Cannot initialize VAC - wierd sun4 model idprom->id_machtype = %d", idprom->id_machtype);
+			prom_halt();
+		}
 	} else {
-		sun4c_vacinfo.num_bytes = prom_getintdefault(prom_root_node,
-							     "vac-size", 65536);
-		sun4c_vacinfo.linesize = prom_getintdefault(prom_root_node,
-							    "vac-linesize", 16);
+		sun4c_vacinfo.type = WRITE_THROUGH;
+
+		if((idprom->id_machtype == (SM_SUN4C | SM_4C_SS1)) ||
+		   (idprom->id_machtype == (SM_SUN4C | SM_4C_SS1PLUS))) {
+			/* PROM on SS1 lacks this info, to be super safe we
+			 * hard code it here since this arch is cast in stone.
+			 */
+			sun4c_vacinfo.num_bytes = 65536;
+			sun4c_vacinfo.linesize = 16;
+		} else {
+			sun4c_vacinfo.num_bytes =
+			 prom_getintdefault(prom_root_node, "vac-size", 65536);
+			sun4c_vacinfo.linesize =
+			 prom_getintdefault(prom_root_node, "vac-linesize", 16);
+		}
+		sun4c_vacinfo.do_hwflushes =
+		 prom_getintdefault(prom_root_node, "vac-hwflush", 0);
+
+		if(sun4c_vacinfo.do_hwflushes == 0)
+			sun4c_vacinfo.do_hwflushes =
+			 prom_getintdefault(prom_root_node, "vac_hwflush", 0);
+
+		if (sun4c_vacinfo.num_bytes != 65536) {
+			prom_printf("WEIRD Sun4C VAC cache size, tell davem");
+			prom_halt();
+		}
 	}
+
 	sun4c_vacinfo.num_lines =
 		(sun4c_vacinfo.num_bytes / sun4c_vacinfo.linesize);
 	switch(sun4c_vacinfo.linesize) {
@@ -458,17 +519,6 @@
 		prom_halt();
 	};
 
-	sun4c_vacinfo.do_hwflushes = prom_getintdefault(prom_root_node,
-							"vac-hwflush", 0);
-	if(sun4c_vacinfo.do_hwflushes == 0)
-		sun4c_vacinfo.do_hwflushes = prom_getintdefault(prom_root_node,
-								"vac_hwflush", 0);
-
-	if(sun4c_vacinfo.num_bytes != 65536) {
-		prom_printf("WEIRD Sun4C VAC cache size, tell davem");
-		prom_halt();
-	}
-
 	sun4c_flush_all();
 	sun4c_enable_vac();
 }
@@ -476,6 +526,7 @@
 /* Patch instructions for the low level kernel fault handler. */
 extern unsigned long invalid_segment_patch1, invalid_segment_patch1_ff;
 extern unsigned long invalid_segment_patch2, invalid_segment_patch2_ff;
+extern unsigned long invalid_segment_patch1_1ff, invalid_segment_patch2_1ff;
 extern unsigned long num_context_patch1, num_context_patch1_16;
 extern unsigned long num_context_patch2, num_context_patch2_16;
 extern unsigned long vac_linesize_patch, vac_linesize_patch_32;
@@ -502,6 +553,12 @@
 			PATCH_INSN(invalid_segment_patch2_ff,
 				   invalid_segment_patch2);
 			break;
+		case 512:
+			PATCH_INSN(invalid_segment_patch1_1ff,
+				   invalid_segment_patch1);
+			PATCH_INSN(invalid_segment_patch2_1ff,
+				   invalid_segment_patch2);
+			break;
 		default:
 			prom_printf("Unhandled number of segmaps: %d\n",
 				    num_segmaps);
@@ -541,38 +598,80 @@
 	}
 }
 
-static void sun4c_probe_mmu(void)
+__initfunc(static void sun4c_probe_mmu(void))
 {
-	if((idprom->id_machtype == (SM_SUN4C | SM_4C_SS1)) ||
-	   (idprom->id_machtype == (SM_SUN4C | SM_4C_SS1PLUS))) {
-		/* Hardcode these just to be safe, PROM on SS1 does
-		 * not have this info available in the root node.
-		 */
-		num_segmaps = 128;
-		num_contexts = 8;
+	if (ARCH_SUN4) {
+		switch(idprom->id_machtype) {
+		case (SM_SUN4|SM_4_110):
+			prom_printf("No support for 4100 yet\n");
+			prom_halt();
+			num_segmaps = 256;
+			num_contexts = 8;
+			break;
+
+		case (SM_SUN4|SM_4_260):
+			prom_printf("No support for 4200 yet\n");
+			prom_halt();
+			num_segmaps = 512;
+			num_contexts = 16;
+			break;
+
+		case (SM_SUN4|SM_4_330):
+			num_segmaps = 256;
+			num_contexts = 16;
+			break;
+
+		case (SM_SUN4|SM_4_470):
+			prom_printf("No support for 4400 yet\n");
+			prom_halt();
+			num_segmaps = 1024;
+			num_contexts = 64;
+			break;
+		default:
+			prom_printf("Invalid SUN4 model\n");
+			prom_halt();
+		}
 	} else {
-		num_segmaps = prom_getintdefault(prom_root_node, "mmu-npmg", 128);
-		num_contexts = prom_getintdefault(prom_root_node, "mmu-nctx", 0x8);
+		if((idprom->id_machtype == (SM_SUN4C | SM_4C_SS1)) ||
+	   	(idprom->id_machtype == (SM_SUN4C | SM_4C_SS1PLUS))) {
+			/* Hardcode these just to be safe, PROM on SS1 does
+		 	* not have this info available in the root node.
+		 	*/
+			num_segmaps = 128;
+			num_contexts = 8;
+		} else {
+			num_segmaps =
+			    prom_getintdefault(prom_root_node, "mmu-npmg", 128);
+			num_contexts =
+			    prom_getintdefault(prom_root_node, "mmu-nctx", 0x8);
+		}
 	}
 	patch_kernel_fault_handler();
 }
 
 volatile unsigned long *sun4c_memerr_reg = 0;
 
-void sun4c_probe_memerr_reg(void)
+__initfunc(void sun4c_probe_memerr_reg(void))
 {
 	int node;
 	struct linux_prom_registers regs[1];
 
-	node = prom_getchild(prom_root_node);
-	node = prom_searchsiblings(prom_root_node, "memory-error");
-	if (!node)
-		return;
-	prom_getproperty(node, "reg", (char *)regs, sizeof(regs));
-	sun4c_memerr_reg = sparc_alloc_io(regs[0].phys_addr, 0,
-					  regs[0].reg_size,
-					  "memory parity error",
-					  regs[0].which_io, 0);
+	if (ARCH_SUN4) {
+		sun4c_memerr_reg = sparc_alloc_io(SUN4_MEMREG_PHYSADDR, 0,
+					  	PAGE_SIZE,
+					  	"memory parity error",
+					  	0x0, 0);
+	} else {
+		node = prom_getchild(prom_root_node);
+		node = prom_searchsiblings(prom_root_node, "memory-error");
+		if (!node)
+			return;
+		prom_getproperty(node, "reg", (char *)regs, sizeof(regs));
+		sun4c_memerr_reg = sparc_alloc_io(regs[0].phys_addr, 0,
+					  	regs[0].reg_size,
+					  	"memory parity error",
+					  	regs[0].which_io, 0);
+	}
 }
 
 static inline void sun4c_init_ss2_cache_bug(void)
@@ -581,6 +680,7 @@
 
 	if((idprom->id_machtype == (SM_SUN4C | SM_4C_SS2)) ||
 	   (idprom->id_machtype == (SM_SUN4C | SM_4C_IPX)) ||
+	   (idprom->id_machtype == (SM_SUN4 | SM_4_330)) ||
 	   (idprom->id_machtype == (SM_SUN4C | SM_4C_ELC))) {
 		/* Whee.. */
 		printk("SS2 cache bug detected, uncaching trap table page\n");
@@ -626,13 +726,14 @@
 	unsigned char pseg;
 	unsigned char locked;
 };
-static struct sun4c_mmu_entry mmu_entry_pool[256];
+
+static struct sun4c_mmu_entry mmu_entry_pool[SUN4C_MAX_SEGMAPS];
 
 __initfunc(static void sun4c_init_mmu_entry_pool(void))
 {
 	int i;
 
-	for(i=0; i < 256; i++) {
+	for(i=0; i < SUN4C_MAX_SEGMAPS; i++) {
 		mmu_entry_pool[i].pseg = i;
 		mmu_entry_pool[i].next = 0;
 		mmu_entry_pool[i].prev = 0;
@@ -703,7 +804,8 @@
 	struct sun4c_mmu_entry ringhd;
 	int num_entries;
 };
-static struct sun4c_mmu_ring sun4c_context_ring[16]; /* used user entries */
+
+static struct sun4c_mmu_ring sun4c_context_ring[SUN4C_MAX_CONTEXTS]; /* used user entries */
 static struct sun4c_mmu_ring sun4c_ufree_ring;       /* free user entries */
 struct sun4c_mmu_ring sun4c_kernel_ring;      /* used kernel entries */
 struct sun4c_mmu_ring sun4c_kfree_ring;       /* free kernel entries */
@@ -711,7 +813,7 @@
 static inline void sun4c_init_rings(unsigned long *mempool)
 {
 	int i;
-	for(i=0; i<16; i++) {
+	for(i=0; i<SUN4C_MAX_CONTEXTS; i++) {
 		sun4c_context_ring[i].ringhd.next =
 			sun4c_context_ring[i].ringhd.prev =
 			&sun4c_context_ring[i].ringhd;
@@ -1120,7 +1222,7 @@
 #define BUCKET_PTE(page)       \
         ((((page) - PAGE_OFFSET) >> PAGE_SHIFT) | pgprot_val(SUN4C_PAGE_KERNEL))
 #define BUCKET_PTE_PAGE(pte)   \
-        (PAGE_OFFSET + (((pte) & 0xffff) << PAGE_SHIFT))
+        (PAGE_OFFSET + (((pte) & SUN4C_PFN_MASK) << PAGE_SHIFT))
 
 static inline void get_locked_segment(unsigned long addr)
 {
@@ -1180,12 +1282,18 @@
 	free_locked_segment(BUCKET_ADDR(entry));
 }
 
+#ifdef CONFIG_SUN4
+#define TASK_STRUCT_ORDER	0
+#else
+#define TASK_STRUCT_ORDER	1
+#endif
+
 static struct task_struct *sun4c_alloc_task_struct(void)
 {
 	unsigned long addr, pages;
 	int entry;
 
-	pages = __get_free_pages(GFP_KERNEL, 1);
+	pages = __get_free_pages(GFP_KERNEL, TASK_STRUCT_ORDER);
 	if(!pages)
 		return (struct task_struct *) 0;
 
@@ -1193,7 +1301,7 @@
 		if(sun4c_bucket[entry] == BUCKET_EMPTY)
 			break;
 	if(entry == NR_TASKS) {
-		free_pages(pages, 1);
+		free_pages(pages, TASK_STRUCT_ORDER);
 		return (struct task_struct *) 0;
 	}
 	if(entry >= sun4c_lowbucket_avail)
@@ -1204,8 +1312,9 @@
 	if(sun4c_get_segmap(addr) == invalid_segment)
 		get_locked_segment(addr);
 	sun4c_put_pte(addr, BUCKET_PTE(pages));
+#ifndef CONFIG_SUN4	
 	sun4c_put_pte(addr + PAGE_SIZE, BUCKET_PTE(pages + PAGE_SIZE));
-
+#endif
 	return (struct task_struct *) addr;
 }
 
@@ -1217,15 +1326,18 @@
 
 	/* We are deleting a mapping, so the flush here is mandatory. */
 	sun4c_flush_page_hw(tsaddr);
+#ifndef CONFIG_SUN4	
 	sun4c_flush_page_hw(tsaddr + PAGE_SIZE);
-
+#endif
 	sun4c_put_pte(tsaddr, 0);
+#ifndef CONFIG_SUN4	
 	sun4c_put_pte(tsaddr + PAGE_SIZE, 0);
+#endif
 	sun4c_bucket[entry] = BUCKET_EMPTY;
 	if(entry < sun4c_lowbucket_avail)
 		sun4c_lowbucket_avail = entry;
 
-	free_pages(pages, 1);
+	free_pages(pages, TASK_STRUCT_ORDER);
 	garbage_collect(entry);
 }
 
@@ -1237,15 +1349,18 @@
 
 	/* We are deleting a mapping, so the flush here is mandatory. */
 	sun4c_flush_page_sw(tsaddr);
+#ifndef CONFIG_SUN4	
 	sun4c_flush_page_sw(tsaddr + PAGE_SIZE);
-
+#endif
 	sun4c_put_pte(tsaddr, 0);
+#ifndef CONFIG_SUN4	
 	sun4c_put_pte(tsaddr + PAGE_SIZE, 0);
+#endif
 	sun4c_bucket[entry] = BUCKET_EMPTY;
 	if(entry < sun4c_lowbucket_avail)
 		sun4c_lowbucket_avail = entry;
 
-	free_pages(pages, 1);
+	free_pages(pages, TASK_STRUCT_ORDER);
 	garbage_collect(entry);
 }
 
@@ -1253,9 +1368,8 @@
 {
 	int entry;
 
-	if(sizeof(union task_union) != (PAGE_SIZE << 1)) {
-		prom_printf("task union not 2 pages!\n");
-		prom_halt();
+	if(sizeof(union task_union) != (PAGE_SIZE << TASK_STRUCT_ORDER)) {
+		prom_printf("task union not %d page(s)!\n", 1 << TASK_STRUCT_ORDER);
 	}
 	for(entry = 0; entry < NR_TASKS; entry++)
 		sun4c_bucket[entry] = BUCKET_EMPTY;
@@ -1949,12 +2063,17 @@
 	*ptep = pte;
 }
 
+static void sun4c_pgd_set(pgd_t * pgdp, pmd_t * pmdp)
+{
+}
+
+
 void sun4c_mapioaddr(unsigned long physaddr, unsigned long virt_addr,
 		     int bus_type, int rdonly)
 {
 	unsigned long page_entry;
 
-	page_entry = ((physaddr >> PAGE_SHIFT) & 0xffff);
+	page_entry = ((physaddr >> PAGE_SHIFT) & SUN4C_PFN_MASK);
 	page_entry |= ((pg_iobits | _SUN4C_PAGE_PRIV) & ~(_SUN4C_PAGE_PRESENT));
 	if(rdonly)
 		page_entry &= ~_SUN4C_WRITEABLE;
@@ -2092,21 +2211,17 @@
 	}
 }
 
-#if KGPROF_PROFILING
-static char s4cinfo[10240];
-#else
-static char s4cinfo[512];
-#endif
-
-static char *sun4c_mmu_info(void)
+static int sun4c_mmu_info(char *buf)
 {
 	int used_user_entries, i;
+	int len;
 
 	used_user_entries = 0;
 	for(i=0; i < num_contexts; i++)
 		used_user_entries += sun4c_context_ring[i].num_entries;
 
-	sprintf(s4cinfo, "vacsize\t\t: %d bytes\n"
+	len = sprintf(buf, 
+		"vacsize\t\t: %d bytes\n"
 		"vachwflush\t: %s\n"
 		"vaclinesize\t: %d bytes\n"
 		"mmuctxs\t\t: %d\n"
@@ -2135,29 +2250,31 @@
 
 #if KGPROF_PROFILING
 	{
-		char *p = s4cinfo + strlen(s4cinfo);
 		int i,j;
-		sprintf(p,"kgprof profiling:\n"); p += strlen(p);
+		len += sprintf(buf + len,"kgprof profiling:\n");
 		for (i=0;i<KGPROF_SIZE && kgprof_counters[i].addr[0];i++) {
-			sprintf(p,"%5d  ",kgprof_counters[i].count); p += strlen(p);
+			len += sprintf(buf + len,"%5d  ",kgprof_counters[i].count);
 			for (j=0;j<KGPROF_DEPTH;j++) {
-				sprintf(p,"%08x ",kgprof_counters[i].addr[j]);
-				p += strlen(p);
+				len += sprintf(buf + len,"%08x ",kgprof_counters[i].addr[j]);
 			}
-			sprintf(p,"\n"); p += strlen(p);
+			len += sprintf(buf + len,"\n");
 		}
 	}
 #endif
 
-	return s4cinfo;
+	return len;
 }
 
 /* Nothing below here should touch the mmu hardware nor the mmu_entry
  * data structures.
  */
 
+#if 0 /* Not used due to BTFIXUPs */
 static unsigned int sun4c_pmd_align(unsigned int addr) { return SUN4C_PMD_ALIGN(addr); }
+#endif
+#if 0 /* Not used due to BTFIXUPs */
 static unsigned int sun4c_pgdir_align(unsigned int addr) { return SUN4C_PGDIR_ALIGN(addr); }
+#endif
 
 /* First the functions which the mid-level code uses to directly
  * manipulate the software page tables.  Some defines since we are
@@ -2170,12 +2287,17 @@
 #define PGD_DIRTY    0x040
 #define PGD_TABLE    (PGD_PRESENT | PGD_RW | PGD_USER | PGD_ACCESSED | PGD_DIRTY)
 
+#if 0 /* Not used due to BTFIXUPs */
 static unsigned long sun4c_vmalloc_start(void)
 {
 	return SUN4C_VMALLOC_START;
 }
+#endif
 
+#if 0 /* Not used due to BTFIXUPs */
 static int sun4c_pte_none(pte_t pte)		{ return !pte_val(pte); }
+#endif
+
 static int sun4c_pte_present(pte_t pte)
 {
 	return ((pte_val(pte) & (_SUN4C_PAGE_PRESENT | _SUN4C_PAGE_PRIV)) != 0);
@@ -2204,35 +2326,47 @@
  * The following only work if pte_present() is true.
  * Undefined behaviour if not..
  */
+#if 0 /* Not used due to BTFIXUPs */
 static int sun4c_pte_write(pte_t pte)
 {
 	return pte_val(pte) & _SUN4C_PAGE_WRITE;
 }
+#endif
 
+#if 0 /* Not used due to BTFIXUPs */
 static int sun4c_pte_dirty(pte_t pte)
 {
 	return pte_val(pte) & _SUN4C_PAGE_MODIFIED;
 }
+#endif
 
+#if 0 /* Not used due to BTFIXUPs */
 static int sun4c_pte_young(pte_t pte)
 {
 	return pte_val(pte) & _SUN4C_PAGE_ACCESSED;
 }
+#endif
 
+#if 0 /* Not used due to BTFIXUPs */
 static pte_t sun4c_pte_wrprotect(pte_t pte)
 {
 	return __pte(pte_val(pte) & ~(_SUN4C_PAGE_WRITE | _SUN4C_PAGE_SILENT_WRITE));
 }
+#endif
 
+#if 0 /* Not used due to BTFIXUPs */
 static pte_t sun4c_pte_mkclean(pte_t pte)
 {
 	return __pte(pte_val(pte) & ~(_SUN4C_PAGE_MODIFIED | _SUN4C_PAGE_SILENT_WRITE));
 }
+#endif
 
+#if 0 /* Not used due to BTFIXUPs */
 static pte_t sun4c_pte_mkold(pte_t pte)
 {
 	return __pte(pte_val(pte) & ~(_SUN4C_PAGE_ACCESSED | _SUN4C_PAGE_SILENT_READ));
 }
+#endif
 
 static pte_t sun4c_pte_mkwrite(pte_t pte)
 {
@@ -2277,22 +2411,29 @@
 	return __pte(((page - PAGE_OFFSET) >> PAGE_SHIFT) | pgprot_val(pgprot));
 }
 
+#if 0 /* Not used due to BTFIXUPs */
 static pte_t sun4c_pte_modify(pte_t pte, pgprot_t newprot)
 {
 	return __pte((pte_val(pte) & _SUN4C_PAGE_CHG_MASK) |
 		     pgprot_val(newprot));
 }
+#endif
 
 static unsigned long sun4c_pte_page(pte_t pte)
 {
-	return (PAGE_OFFSET + ((pte_val(pte) & 0xffff) << (PAGE_SHIFT)));
+	return (PAGE_OFFSET + ((pte_val(pte) & SUN4C_PFN_MASK) << (PAGE_SHIFT)));
 }
 
-static unsigned long sun4c_pmd_page(pmd_t pmd)
+static inline unsigned long sun4c_pmd_page(pmd_t pmd)
 {
 	return (pmd_val(pmd) & PAGE_MASK);
 }
 
+static unsigned long sun4c_pgd_page(pgd_t pgd)
+{
+	return 0;
+}
+
 /* to find an entry in a page-table-directory */
 pgd_t *sun4c_pgd_offset(struct mm_struct * mm, unsigned long address)
 {
@@ -2351,6 +2492,16 @@
 	return (pte_t *) sun4c_pmd_page(*pmd) + address;
 }
 
+static void sun4c_free_pte_slow(pte_t *pte)
+{
+	free_page((unsigned long)pte);
+}
+
+static void sun4c_free_pgd_slow(pgd_t *pgd)
+{
+	free_page((unsigned long)pgd);
+}
+
 /*
  * allocating and freeing a pmd is trivial: the 1-entry pmd is
  * inside the pgd, so has no extra memory associated with it.
@@ -2364,16 +2515,73 @@
 	return (pmd_t *) pgd;
 }
 
+extern __inline__ pgd_t *sun4c_get_pgd_fast(void)
+{
+	unsigned long *ret;
+
+	if((ret = pgd_quicklist) != NULL) {
+		pgd_quicklist = (unsigned long *)(*ret);
+		ret[0] = ret[1];
+		pgtable_cache_size--;
+	} else {
+		pgd_t *init;
+		
+		ret = (unsigned long *)__get_free_page(GFP_KERNEL);
+		memset (ret, 0, (KERNBASE / SUN4C_PGDIR_SIZE) * sizeof(pgd_t));
+		init = pgd_offset(&init_mm, 0);
+		memcpy (((pgd_t *)ret) + USER_PTRS_PER_PGD, init + USER_PTRS_PER_PGD,
+			(PTRS_PER_PGD - USER_PTRS_PER_PGD) * sizeof(pgd_t));
+	}
+	return (pgd_t *)ret;
+}
+
+static void sun4c_set_pgdir(unsigned long address, pgd_t entry)
+{
+	/* Nothing to do */
+}
+
+extern __inline__ void sun4c_free_pgd_fast(pgd_t *pgd)
+{
+	*(unsigned long *)pgd = (unsigned long) pgd_quicklist;
+	pgd_quicklist = (unsigned long *) pgd;
+	pgtable_cache_size++;
+}
+
+extern __inline__ pte_t *sun4c_get_pte_fast(void)
+{
+	unsigned long *ret;
+
+	if((ret = (unsigned long *)pte_quicklist) != NULL) {
+		pte_quicklist = (unsigned long *)(*ret);
+		ret[0] = ret[1];
+		pgtable_cache_size--;
+	}
+	return (pte_t *)ret;
+}
+
+extern __inline__ void sun4c_free_pte_fast(pte_t *pte)
+{
+	*(unsigned long *)pte = (unsigned long) pte_quicklist;
+	pte_quicklist = (unsigned long *) pte;
+	pgtable_cache_size++;
+}
+
 static void sun4c_pte_free(pte_t *pte)
 {
-	free_page((unsigned long) pte);
+	sun4c_free_pte_fast(pte);
 }
 
 static pte_t *sun4c_pte_alloc(pmd_t * pmd, unsigned long address)
 {
 	address = (address >> PAGE_SHIFT) & (SUN4C_PTRS_PER_PTE - 1);
 	if (sun4c_pmd_none(*pmd)) {
-		pte_t *page = (pte_t *) get_free_page(GFP_KERNEL);
+		pte_t *page = (pte_t *) sun4c_get_pte_fast();
+		
+		if (page) {
+			*pmd = __pmd(PGD_TABLE | (unsigned long) page);
+			return page + address;
+		}
+		page = (pte_t *) get_free_page(GFP_KERNEL);
 		if (sun4c_pmd_none(*pmd)) {
 			if (page) {
 				*pmd = __pmd(PGD_TABLE | (unsigned long) page);
@@ -2392,13 +2600,17 @@
 	return (pte_t *) sun4c_pmd_page(*pmd) + address;
 }
 
+static pte_t *sun4c_pte_get(void)
+{
+	return sun4c_get_pte_fast();
+}
+
 /*
  * allocating and freeing a pmd is trivial: the 1-entry pmd is
  * inside the pgd, so has no extra memory associated with it.
  */
 static void sun4c_pmd_free(pmd_t * pmd)
 {
-	*pmd = __pmd(0);
 }
 
 static pmd_t *sun4c_pmd_alloc(pgd_t * pgd, unsigned long address)
@@ -2408,12 +2620,12 @@
 
 static void sun4c_pgd_free(pgd_t *pgd)
 {
-	free_page((unsigned long) pgd);
+	sun4c_free_pgd_fast(pgd);
 }
 
 static pgd_t *sun4c_pgd_alloc(void)
 {
-	return (pgd_t *) get_free_page(GFP_KERNEL);
+	return sun4c_get_pgd_fast();
 }
 
 /* There are really two cases of aliases to watch out for, and these
@@ -2435,12 +2647,13 @@
  */
 static void sun4c_vac_alias_fixup(struct vm_area_struct *vma, unsigned long address, pte_t pte)
 {
-	struct dentry *dentry;
+	struct dentry *dentry = NULL;
 	struct inode *inode = NULL;
 	pgd_t *pgdp;
 	pte_t *ptep;
 
-	dentry = vma->vm_dentry;
+	if (vma->vm_file)
+		dentry = vma->vm_file->f_dentry;
 	if(dentry)
 		inode = dentry->d_inode;
 	if(inode) {
@@ -2556,134 +2769,147 @@
 /* Load up routines and constants for sun4c mmu */
 __initfunc(void ld_mmu_sun4c(void))
 {
+	extern void ___xchg32_sun4c(void);
+	
 	printk("Loading sun4c MMU routines\n");
 
 	/* First the constants */
-	pmd_shift = SUN4C_PMD_SHIFT;
-	pmd_size = SUN4C_PMD_SIZE;
-	pmd_mask = SUN4C_PMD_MASK;
-	pgdir_shift = SUN4C_PGDIR_SHIFT;
-	pgdir_size = SUN4C_PGDIR_SIZE;
-	pgdir_mask = SUN4C_PGDIR_MASK;
-
-	ptrs_per_pte = SUN4C_PTRS_PER_PTE;
-	ptrs_per_pmd = SUN4C_PTRS_PER_PMD;
-	ptrs_per_pgd = SUN4C_PTRS_PER_PGD;
-
-	page_none = SUN4C_PAGE_NONE;
-	page_shared = SUN4C_PAGE_SHARED;
-	page_copy = SUN4C_PAGE_COPY;
-	page_readonly = SUN4C_PAGE_READONLY;
-	page_kernel = SUN4C_PAGE_KERNEL;
+	BTFIXUPSET_SIMM13(pmd_shift, SUN4C_PMD_SHIFT);
+	BTFIXUPSET_SETHI(pmd_size, SUN4C_PMD_SIZE);
+	BTFIXUPSET_SETHI(pmd_mask, SUN4C_PMD_MASK);
+	BTFIXUPSET_SIMM13(pgdir_shift, SUN4C_PGDIR_SHIFT);
+	BTFIXUPSET_SETHI(pgdir_size, SUN4C_PGDIR_SIZE);
+	BTFIXUPSET_SETHI(pgdir_mask, SUN4C_PGDIR_MASK);
+
+	BTFIXUPSET_SIMM13(ptrs_per_pte, SUN4C_PTRS_PER_PTE);
+	BTFIXUPSET_SIMM13(ptrs_per_pmd, SUN4C_PTRS_PER_PMD);
+	BTFIXUPSET_SIMM13(ptrs_per_pgd, SUN4C_PTRS_PER_PGD);
+	BTFIXUPSET_SIMM13(user_ptrs_per_pgd, KERNBASE / SUN4C_PGDIR_SIZE);
+
+	BTFIXUPSET_INT(page_none, pgprot_val(SUN4C_PAGE_NONE));
+	BTFIXUPSET_INT(page_shared, pgprot_val(SUN4C_PAGE_SHARED));
+	BTFIXUPSET_INT(page_copy, pgprot_val(SUN4C_PAGE_COPY));
+	BTFIXUPSET_INT(page_readonly, pgprot_val(SUN4C_PAGE_READONLY));
+	BTFIXUPSET_INT(page_kernel, pgprot_val(SUN4C_PAGE_KERNEL));
 	pg_iobits = _SUN4C_PAGE_PRESENT | _SUN4C_READABLE | _SUN4C_WRITEABLE |
 		    _SUN4C_PAGE_IO | _SUN4C_PAGE_NOCACHE;
 	
 	/* Functions */
-	flush_cache_all = sun4c_flush_cache_all;
+#ifndef __SMP__
+	BTFIXUPSET_CALL(___xchg32, ___xchg32_sun4c, BTFIXUPCALL_NORM);
+#endif
+	BTFIXUPSET_CALL(get_pte_fast, sun4c_pte_get, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(get_pgd_fast, sun4c_pgd_alloc, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(free_pte_slow, sun4c_free_pte_slow, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(free_pgd_slow, sun4c_free_pgd_slow, BTFIXUPCALL_NORM);
+	
+	BTFIXUPSET_CALL(set_pgdir, sun4c_set_pgdir, BTFIXUPCALL_NOP);
+
+	BTFIXUPSET_CALL(flush_cache_all, sun4c_flush_cache_all, BTFIXUPCALL_NORM);
 
 	if(sun4c_vacinfo.do_hwflushes) {
-		flush_cache_mm = sun4c_flush_cache_mm_hw;
-		flush_cache_range = sun4c_flush_cache_range_hw;
-		flush_cache_page = sun4c_flush_cache_page_hw;
-		flush_page_to_ram = sun4c_flush_page_to_ram_hw;
-		flush_tlb_mm = sun4c_flush_tlb_mm_hw;
-		flush_tlb_range = sun4c_flush_tlb_range_hw;
-		flush_tlb_page = sun4c_flush_tlb_page_hw;
-		free_task_struct = sun4c_free_task_struct_hw;
-		switch_to_context = sun4c_switch_to_context_hw;
-		destroy_context = sun4c_destroy_context_hw;
-		init_new_context = sun4c_init_new_context_hw;
+		BTFIXUPSET_CALL(flush_cache_mm, sun4c_flush_cache_mm_hw, BTFIXUPCALL_NORM);
+		BTFIXUPSET_CALL(flush_cache_range, sun4c_flush_cache_range_hw, BTFIXUPCALL_NORM);
+		BTFIXUPSET_CALL(flush_cache_page, sun4c_flush_cache_page_hw, BTFIXUPCALL_NORM);
+		BTFIXUPSET_CALL(flush_page_to_ram, sun4c_flush_page_to_ram_hw, BTFIXUPCALL_NORM);
+		BTFIXUPSET_CALL(flush_tlb_mm, sun4c_flush_tlb_mm_hw, BTFIXUPCALL_NORM);
+		BTFIXUPSET_CALL(flush_tlb_range, sun4c_flush_tlb_range_hw, BTFIXUPCALL_NORM);
+		BTFIXUPSET_CALL(flush_tlb_page, sun4c_flush_tlb_page_hw, BTFIXUPCALL_NORM);
+		BTFIXUPSET_CALL(free_task_struct, sun4c_free_task_struct_hw, BTFIXUPCALL_NORM);
+		BTFIXUPSET_CALL(switch_to_context, sun4c_switch_to_context_hw, BTFIXUPCALL_NORM);
+		BTFIXUPSET_CALL(destroy_context, sun4c_destroy_context_hw, BTFIXUPCALL_NORM);
+		BTFIXUPSET_CALL(init_new_context, sun4c_init_new_context_hw, BTFIXUPCALL_NORM);
 	} else {
-		flush_cache_mm = sun4c_flush_cache_mm_sw;
-		flush_cache_range = sun4c_flush_cache_range_sw;
-		flush_cache_page = sun4c_flush_cache_page_sw;
-		flush_page_to_ram = sun4c_flush_page_to_ram_sw;
-		flush_tlb_mm = sun4c_flush_tlb_mm_sw;
-		flush_tlb_range = sun4c_flush_tlb_range_sw;
-		flush_tlb_page = sun4c_flush_tlb_page_sw;
-		free_task_struct = sun4c_free_task_struct_sw;
-		switch_to_context = sun4c_switch_to_context_sw;
-		destroy_context = sun4c_destroy_context_sw;
-		init_new_context = sun4c_init_new_context_sw;
-	}
-
-	flush_tlb_all = sun4c_flush_tlb_all;
-
-	flush_sig_insns = sun4c_flush_sig_insns;
-
-	set_pte = sun4c_set_pte;
-	pmd_align = sun4c_pmd_align;
-	pgdir_align = sun4c_pgdir_align;
-	vmalloc_start = sun4c_vmalloc_start;
-
-	pte_page = sun4c_pte_page;
-	pmd_page = sun4c_pmd_page;
-
-	sparc_update_rootmmu_dir = sun4c_update_rootmmu_dir;
-
-	pte_none = sun4c_pte_none;
-	pte_present = sun4c_pte_present;
-	pte_clear = sun4c_pte_clear;
-
-	pmd_none = sun4c_pmd_none;
-	pmd_bad = sun4c_pmd_bad;
-	pmd_present = sun4c_pmd_present;
-	pmd_clear = sun4c_pmd_clear;
-
-	pgd_none = sun4c_pgd_none;
-	pgd_bad = sun4c_pgd_bad;
-	pgd_present = sun4c_pgd_present;
-	pgd_clear = sun4c_pgd_clear;
-
-	mk_pte = sun4c_mk_pte;
-	mk_pte_phys = sun4c_mk_pte_phys;
-	mk_pte_io = sun4c_mk_pte_io;
-	pte_modify = sun4c_pte_modify;
-	pgd_offset = sun4c_pgd_offset;
-	pmd_offset = sun4c_pmd_offset;
-	pte_offset = sun4c_pte_offset;
-	pte_free_kernel = sun4c_pte_free_kernel;
-	pmd_free_kernel = sun4c_pmd_free_kernel;
-	pte_alloc_kernel = sun4c_pte_alloc_kernel;
-	pmd_alloc_kernel = sun4c_pmd_alloc_kernel;
-	pte_free = sun4c_pte_free;
-	pte_alloc = sun4c_pte_alloc;
-	pmd_free = sun4c_pmd_free;
-	pmd_alloc = sun4c_pmd_alloc;
-	pgd_free = sun4c_pgd_free;
-	pgd_alloc = sun4c_pgd_alloc;
-
-	pte_write = sun4c_pte_write;
-	pte_dirty = sun4c_pte_dirty;
-	pte_young = sun4c_pte_young;
-	pte_wrprotect = sun4c_pte_wrprotect;
-	pte_mkclean = sun4c_pte_mkclean;
-	pte_mkold = sun4c_pte_mkold;
-	pte_mkwrite = sun4c_pte_mkwrite;
-	pte_mkdirty = sun4c_pte_mkdirty;
-	pte_mkyoung = sun4c_pte_mkyoung;
-	update_mmu_cache = sun4c_update_mmu_cache;
-
-	mmu_lockarea = sun4c_lockarea;
-	mmu_unlockarea = sun4c_unlockarea;
-
-	mmu_get_scsi_one = sun4c_get_scsi_one;
-	mmu_get_scsi_sgl = sun4c_get_scsi_sgl;
-	mmu_release_scsi_one = sun4c_release_scsi_one;
-	mmu_release_scsi_sgl = sun4c_release_scsi_sgl;
+		BTFIXUPSET_CALL(flush_cache_mm, sun4c_flush_cache_mm_sw, BTFIXUPCALL_NORM);
+		BTFIXUPSET_CALL(flush_cache_range, sun4c_flush_cache_range_sw, BTFIXUPCALL_NORM);
+		BTFIXUPSET_CALL(flush_cache_page, sun4c_flush_cache_page_sw, BTFIXUPCALL_NORM);
+		BTFIXUPSET_CALL(flush_page_to_ram, sun4c_flush_page_to_ram_sw, BTFIXUPCALL_NORM);
+		BTFIXUPSET_CALL(flush_tlb_mm, sun4c_flush_tlb_mm_sw, BTFIXUPCALL_NORM);
+		BTFIXUPSET_CALL(flush_tlb_range, sun4c_flush_tlb_range_sw, BTFIXUPCALL_NORM);
+		BTFIXUPSET_CALL(flush_tlb_page, sun4c_flush_tlb_page_sw, BTFIXUPCALL_NORM);
+		BTFIXUPSET_CALL(free_task_struct, sun4c_free_task_struct_sw, BTFIXUPCALL_NORM);
+		BTFIXUPSET_CALL(switch_to_context, sun4c_switch_to_context_sw, BTFIXUPCALL_NORM);
+		BTFIXUPSET_CALL(destroy_context, sun4c_destroy_context_sw, BTFIXUPCALL_NORM);
+		BTFIXUPSET_CALL(init_new_context, sun4c_init_new_context_sw, BTFIXUPCALL_NORM);
+	}
+
+	BTFIXUPSET_CALL(flush_tlb_all, sun4c_flush_tlb_all, BTFIXUPCALL_NORM);
+
+	BTFIXUPSET_CALL(flush_sig_insns, sun4c_flush_sig_insns, BTFIXUPCALL_NOP);
+
+	BTFIXUPSET_CALL(set_pte, sun4c_set_pte, BTFIXUPCALL_STO1O0);
+
+	BTFIXUPSET_CALL(pte_page, sun4c_pte_page, BTFIXUPCALL_NORM);
+#if PAGE_SHIFT <= 12	
+	BTFIXUPSET_CALL(pmd_page, sun4c_pmd_page, BTFIXUPCALL_ANDNINT(PAGE_SIZE - 1));
+#else
+	BTFIXUPSET_CALL(pmd_page, sun4c_pmd_page, BTFIXUPCALL_NORM);
+#endif
+
+	BTFIXUPSET_CALL(sparc_update_rootmmu_dir, sun4c_update_rootmmu_dir, BTFIXUPCALL_NOP);
+
+	BTFIXUPSET_CALL(pte_present, sun4c_pte_present, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pte_clear, sun4c_pte_clear, BTFIXUPCALL_STG0O0);
+
+	BTFIXUPSET_CALL(pmd_bad, sun4c_pmd_bad, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pmd_present, sun4c_pmd_present, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pmd_clear, sun4c_pmd_clear, BTFIXUPCALL_STG0O0);
+
+	BTFIXUPSET_CALL(pgd_none, sun4c_pgd_none, BTFIXUPCALL_RETINT(0));
+	BTFIXUPSET_CALL(pgd_bad, sun4c_pgd_bad, BTFIXUPCALL_RETINT(0));
+	BTFIXUPSET_CALL(pgd_present, sun4c_pgd_present, BTFIXUPCALL_RETINT(1));
+	BTFIXUPSET_CALL(pgd_clear, sun4c_pgd_clear, BTFIXUPCALL_NOP);
+
+	BTFIXUPSET_CALL(mk_pte, sun4c_mk_pte, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(mk_pte_phys, sun4c_mk_pte_phys, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(mk_pte_io, sun4c_mk_pte_io, BTFIXUPCALL_NORM);
+	
+	BTFIXUPSET_INT(pte_modify_mask, _SUN4C_PAGE_CHG_MASK);
+	BTFIXUPSET_CALL(pgd_offset, sun4c_pgd_offset, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pmd_offset, sun4c_pmd_offset, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pte_offset, sun4c_pte_offset, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pte_free_kernel, sun4c_pte_free_kernel, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pmd_free_kernel, sun4c_pmd_free_kernel, BTFIXUPCALL_NOP);
+	BTFIXUPSET_CALL(pte_alloc_kernel, sun4c_pte_alloc_kernel, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pmd_alloc_kernel, sun4c_pmd_alloc_kernel, BTFIXUPCALL_RETO0);
+	BTFIXUPSET_CALL(pte_free, sun4c_pte_free, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pte_alloc, sun4c_pte_alloc, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pmd_free, sun4c_pmd_free, BTFIXUPCALL_NOP);
+	BTFIXUPSET_CALL(pmd_alloc, sun4c_pmd_alloc, BTFIXUPCALL_RETO0);
+	BTFIXUPSET_CALL(pgd_free, sun4c_pgd_free, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pgd_alloc, sun4c_pgd_alloc, BTFIXUPCALL_NORM);
+
+	BTFIXUPSET_HALF(pte_writei, _SUN4C_PAGE_WRITE);
+	BTFIXUPSET_HALF(pte_dirtyi, _SUN4C_PAGE_MODIFIED);
+	BTFIXUPSET_HALF(pte_youngi, _SUN4C_PAGE_ACCESSED);
+	BTFIXUPSET_HALF(pte_wrprotecti, _SUN4C_PAGE_WRITE|_SUN4C_PAGE_SILENT_WRITE);
+	BTFIXUPSET_HALF(pte_mkcleani, _SUN4C_PAGE_MODIFIED|_SUN4C_PAGE_SILENT_WRITE);
+	BTFIXUPSET_HALF(pte_mkoldi, _SUN4C_PAGE_ACCESSED|_SUN4C_PAGE_SILENT_READ);
+	BTFIXUPSET_CALL(pte_mkwrite, sun4c_pte_mkwrite, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pte_mkdirty, sun4c_pte_mkdirty, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pte_mkyoung, sun4c_pte_mkyoung, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(update_mmu_cache, sun4c_update_mmu_cache, BTFIXUPCALL_NORM);
+
+	BTFIXUPSET_CALL(mmu_lockarea, sun4c_lockarea, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(mmu_unlockarea, sun4c_unlockarea, BTFIXUPCALL_NORM);
+
+	BTFIXUPSET_CALL(mmu_get_scsi_one, sun4c_get_scsi_one, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(mmu_get_scsi_sgl, sun4c_get_scsi_sgl, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(mmu_release_scsi_one, sun4c_release_scsi_one, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(mmu_release_scsi_sgl, sun4c_release_scsi_sgl, BTFIXUPCALL_NORM);
 
-	mmu_map_dma_area = sun4c_map_dma_area;
+	BTFIXUPSET_CALL(mmu_map_dma_area, sun4c_map_dma_area, BTFIXUPCALL_NORM);
 
-        mmu_v2p = sun4c_v2p;
-        mmu_p2v = sun4c_p2v;
+        BTFIXUPSET_CALL(mmu_v2p, sun4c_v2p, BTFIXUPCALL_NORM);
+        BTFIXUPSET_CALL(mmu_p2v, sun4c_p2v, BTFIXUPCALL_NORM);
 	
 	/* Task struct and kernel stack allocating/freeing. */
-	alloc_task_struct = sun4c_alloc_task_struct;
+	BTFIXUPSET_CALL(alloc_task_struct, sun4c_alloc_task_struct, BTFIXUPCALL_NORM);
 
-	quick_kernel_fault = sun4c_quick_kernel_fault;
-	mmu_info = sun4c_mmu_info;
+	BTFIXUPSET_CALL(quick_kernel_fault, sun4c_quick_kernel_fault, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(mmu_info, sun4c_mmu_info, BTFIXUPCALL_NORM);
 
 	/* These should _never_ get called with two level tables. */
-	pgd_set = 0;
-	pgd_page = 0;
+	BTFIXUPSET_CALL(pgd_set, sun4c_pgd_set, BTFIXUPCALL_NOP);
+	BTFIXUPSET_CALL(pgd_page, sun4c_pgd_page, BTFIXUPCALL_RETO0);
 }
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/mm/turbosparc.S linux/arch/sparc/mm/turbosparc.S
--- v2.1.96/linux/arch/sparc/mm/turbosparc.S	Mon Jan 12 15:15:43 1998
+++ linux/arch/sparc/mm/turbosparc.S	Tue Apr 14 17:44:20 1998
@@ -1,4 +1,4 @@
-/* $Id: turbosparc.S,v 1.2 1997/11/26 13:27:59 jj Exp $
+/* $Id: turbosparc.S,v 1.3 1998/02/05 14:19:04 jj Exp $
  * turbosparc.S: High speed TurboSparc mmu/cache operations.
  *
  * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
@@ -27,6 +27,7 @@
 
 	.globl	turbosparc_flush_cache_all
 	.globl	turbosparc_flush_sig_insns
+	.globl	turbosparc_flush_page_for_dma
 
 turbosparc_flush_cache_all:
 	WINDOW_FLUSH(%g4, %g5)
@@ -42,5 +43,6 @@
 	 sta	%g0, [%g0] ASI_M_IC_FLCLEAR
 
 turbosparc_flush_sig_insns:
+turbosparc_flush_page_for_dma:
 	retl
 	 nop
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/mm/viking.S linux/arch/sparc/mm/viking.S
--- v2.1.96/linux/arch/sparc/mm/viking.S	Mon Jan 12 15:15:43 1998
+++ linux/arch/sparc/mm/viking.S	Tue Apr 14 17:44:20 1998
@@ -1,8 +1,8 @@
-/* $Id: viking.S,v 1.6 1997/11/27 15:42:32 jj Exp $
+/* $Id: viking.S,v 1.11 1998/02/20 18:07:50 jj Exp $
  * viking.S: High speed Viking cache/mmu operations
  *
  * Copyright (C) 1997  Eddie C. Dost  (ecd@skynet.be)
- * Copyright (C) 1997  Jakub Jelinek  (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1997,1998  Jakub Jelinek  (jj@sunsite.mff.cuni.cz)
  */
 
 #include <asm/ptrace.h>
@@ -13,6 +13,7 @@
 #include <asm/pgtsrmmu.h>
 #include <asm/viking.h>
 #include <asm/cprefix.h>
+#include <asm/btfixup.h>
 
 #define WINDOW_FLUSH(tmp1, tmp2)					\
 	mov	0, tmp1;						\
@@ -37,40 +38,33 @@
 	.globl	viking_flush_tlb_all, viking_flush_tlb_mm
 	.globl	viking_flush_tlb_range, viking_flush_tlb_page
 
-	.globl	viking_c_mxcc_flush_page
-	.globl	viking_c_flush_page, viking_c_flush_chunk
+	.globl	viking_c_flush_chunk, viking_s_flush_chunk
+
+viking_s_flush_chunk:
+	sethi	%hi(KERNBASE), %g2
+	ba	2f
+	 sub	%o0, %g2, %g3
 
-viking_c_flush_page:
 viking_c_flush_chunk:
 	sethi	%hi(KERNBASE), %g2
 	cmp	%o0, %g2
 	bgeu	2f
 	 sub	%o0, %g2, %g3
-	sethi	%hi(C_LABEL(page_contig_offset)), %g2
-	ld	[%g2 + %lo(C_LABEL(page_contig_offset))], %g2
+	sethi	BTFIXUP_SETHI(page_contig_offset), %g2
 	ba	2f
 	 sub	%o0, %g2, %g3
 
 viking_flush_page:
 viking_flush_chunk:
 	sethi	%hi(C_LABEL(srmmu_v2p_hash)), %g2
-	or	%g2, %lo(C_LABEL(srmmu_v2p_hash)), %g2
 	srl	%o0, 24, %o1
+	or	%g2, %lo(C_LABEL(srmmu_v2p_hash)), %g2
 	sll	%o1, 2, %o1
-
 	ld	[%g2 + %o1], %g3
-	cmp	%g3, 0
-	bne	1f
-	 and	%o0, PAGE_MASK, %o0
-
-	retl
-	 nop
-
-1:
-	ld	[%g3], %o1
-	sub	%o0, %o1, %g2
-	ld	[%g3 + 4], %o0
-	add	%g2, %o0, %g3
+	and	%o0, PAGE_MASK, %o0
+	cmp	%g3, -1
+	be	9f
+	 add	%o0, %g3, %g3
 2:	srl	%g3, 12, %g1		! ppage >> 12
 
 	clr	%o1			! set counter, 0 - 127
@@ -124,41 +118,22 @@
 	ble	5b
 	 clr	%o2
 
-	retl
+9:	retl
 	 nop
 
-viking_c_mxcc_flush_page:
-	sethi	%hi(KERNBASE), %g2
-	cmp	%o0, %g2
-	bgeu	2f
-	 sub	%o0, %g2, %g3
-	sethi	%hi(C_LABEL(page_contig_offset)), %g2
-	ld	[%g2 + %lo(C_LABEL(page_contig_offset))], %g2
-	ba	2f
-	 sub	%o0, %g2, %g3
-
 viking_mxcc_flush_page:
 	sethi	%hi(C_LABEL(srmmu_v2p_hash)), %g2
-	or	%g2, %lo(C_LABEL(srmmu_v2p_hash)), %g2
 	srl	%o0, 24, %o1
+	or	%g2, %lo(C_LABEL(srmmu_v2p_hash)), %g2
 	sll	%o1, 2, %o1
-
 	ld	[%g2 + %o1], %g3
-	cmp	%g3, 0
-	bne	1f
-	 and	%o0, PAGE_MASK, %o0
-
-	retl
-	 nop
-
-1:
-	ld	[%g3], %o1
-	sub	%o0, %o1, %g2
-	ld	[%g3 + 4], %o0
-	add	%g2, %o0, %g3
+	and	%o0, PAGE_MASK, %o0
+	cmp	%g3, -1
+	be	9f
+	 add	%o0, %g3, %g3
 2:	sub	%g3, -PAGE_SIZE, %g3		! ppage + PAGE_SIZE
-	mov	0x10, %g2			! set cacheable bit
 	sethi	%hi(MXCC_SRCSTREAM), %o3	! assume %hi(MXCC_SRCSTREAM) == %hi(MXCC_DESTSTREAM)
+	mov	0x10, %g2			! set cacheable bit
 	or	%o3, %lo(MXCC_SRCSTREAM), %o2
 	or	%o3, %lo(MXCC_DESSTREAM), %o3
 	sub	%g3, MXCC_STREAM_SIZE, %g3
@@ -169,7 +144,7 @@
 	bne	6b
 	 sub	%g3, MXCC_STREAM_SIZE, %g3
 
-	retl
+9:	retl
 	 nop
 
 viking_mxcc_flush_chunk:
@@ -212,13 +187,12 @@
 	cmp	%o3, -1
 	be	2f
 #endif
-	srl	%o1, SRMMU_PGDIR_SHIFT, %o1
+	sethi	%hi(~((1 << SRMMU_PGDIR_SHIFT) - 1)), %o4
 	sta	%o3, [%g1] ASI_M_MMUREGS
-	sll	%o1, SRMMU_PGDIR_SHIFT, %o1
-	sethi	%hi(1 << SRMMU_PGDIR_SHIFT), %o4
+	and	%o1, %o4, %o1
 	add	%o1, 0x200, %o1
 	sta	%g0, [%o1] ASI_M_FLUSH_PROBE
-1:	add	%o1, %o4, %o1
+1:	sub	%o1, %o4, %o1
 	cmp	%o1, %o2
 	blu,a	1b
 	 sta	%g0, [%o1] ASI_M_FLUSH_PROBE
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/prom/Makefile linux/arch/sparc/prom/Makefile
--- v2.1.96/linux/arch/sparc/prom/Makefile	Sat Nov  9 00:12:23 1996
+++ linux/arch/sparc/prom/Makefile	Tue Apr 14 17:44:20 1998
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.5 1995/11/25 00:59:48 davem Exp $
+# $Id: Makefile,v 1.6 1998/01/30 10:58:59 jj Exp $
 # Makefile for the Sun Boot PROM interface library under
 # Linux.
 #
@@ -9,7 +9,11 @@
 # Note 2! The CFLAGS definitions are now in the main makefile...
 
 OBJS  = bootstr.o devmap.o devops.o init.o memory.o misc.o mp.o \
-        palloc.o ranges.o segment.o tree.o console.o printf.o
+	palloc.o ranges.o segment.o console.o printf.o tree.o
+
+ifeq ($(CONFIG_SUN4),y)
+OBJS += sun4prom.o
+endif
 
 all: promlib.a
 
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/prom/bootstr.c linux/arch/sparc/prom/bootstr.c
--- v2.1.96/linux/arch/sparc/prom/bootstr.c	Mon Jul  7 08:18:54 1997
+++ linux/arch/sparc/prom/bootstr.c	Tue Apr 14 17:44:20 1998
@@ -1,4 +1,4 @@
-/* $Id: bootstr.c,v 1.14 1997/06/19 16:28:49 jj Exp $
+/* $Id: bootstr.c,v 1.17 1998/02/09 13:26:21 jj Exp $
  * bootstr.c:  Boot string/argument acquisition from the PROM.
  *
  * Copyright(C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -7,12 +7,15 @@
 #include <linux/config.h>
 #include <linux/string.h>
 #include <asm/oplib.h>
+#include <asm/sun4prom.h>
 #include <linux/init.h>
 
 #define BARG_LEN  256
-static char barg_buf[BARG_LEN] __initdata = { 0 };
+static char barg_buf[BARG_LEN] = { 0 };
 static char fetched __initdata = 0;
 
+extern linux_sun4_romvec *sun4_romvec;
+
 __initfunc(char *
 prom_getbootargs(void))
 {
@@ -26,6 +29,7 @@
 
 	switch(prom_vers) {
 	case PROM_V0:
+	case PROM_SUN4:
 		cp = barg_buf;
 		/* Start from 1 and go over fd(0,0,0)kernel */
 		for(iter = 1; iter < 8; iter++) {
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/prom/console.c linux/arch/sparc/prom/console.c
--- v2.1.96/linux/arch/sparc/prom/console.c	Thu May 15 16:48:02 1997
+++ linux/arch/sparc/prom/console.c	Tue Apr 14 17:44:20 1998
@@ -1,4 +1,4 @@
-/* $Id: console.c,v 1.14 1997/05/14 20:44:58 davem Exp $
+/* $Id: console.c,v 1.17 1998/03/09 14:04:21 jj Exp $
  * console.c: Routines that deal with sending and receiving IO
  *            to/from the current console device using the PROM.
  *
@@ -10,12 +10,12 @@
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <asm/openprom.h>
+#include <asm/sun4prom.h>
 #include <asm/oplib.h>
 #include <asm/system.h>
 #include <linux/string.h>
 
-/* XXX Let's get rid of this thing if we can... */
-extern struct task_struct *current_set[NR_CPUS];
+extern void restore_current(void);
 
 /* Non blocking get character from console input device, returns -1
  * if no input was taken.  This can be used for polling.
@@ -30,6 +30,7 @@
 	save_flags(flags); cli();
 	switch(prom_vers) {
 	case PROM_V0:
+	case PROM_SUN4:
 		i = (*(romvec->pv_nbgetchar))();
 		break;
 	case PROM_V2:
@@ -45,9 +46,7 @@
 		i = -1;
 		break;
 	};
-	__asm__ __volatile__("ld [%0], %%g6\n\t" : :
-			     "r" (&current_set[hard_smp_processor_id()]) :
-			     "memory");
+	restore_current();
 	restore_flags(flags);
 	return i; /* Ugh, we could spin forever on unsupported proms ;( */
 }
@@ -65,6 +64,7 @@
 	save_flags(flags); cli();
 	switch(prom_vers) {
 	case PROM_V0:
+	case PROM_SUN4:
 		i = (*(romvec->pv_nbputchar))(c);
 		break;
 	case PROM_V2:
@@ -89,9 +89,7 @@
 		i = -1;
 		break;
 	};
-	__asm__ __volatile__("ld [%0], %%g6\n\t" : :
-			     "r" (&current_set[hard_smp_processor_id()]) :
-			     "memory");
+	restore_current();
 	restore_flags(flags);
 	return i; /* Ugh, we could spin forever on unsupported proms ;( */
 }
@@ -125,6 +123,7 @@
 	switch(prom_vers) {
 	case PROM_V0:
 	case PROM_V2:
+	case PROM_SUN4:
 	default:
 		switch(*romvec->pv_stdin) {
 		case PROMDEV_KBD:	return PROMDEV_IKBD;
@@ -136,9 +135,7 @@
 	case PROM_V3:
 		save_flags(flags); cli();
 		st_p = (*romvec->pv_v2devops.v2_inst2pkg)(*romvec->pv_v2bootargs.fd_stdin);
-		__asm__ __volatile__("ld [%0], %%g6\n\t" : :
-				     "r" (&current_set[hard_smp_processor_id()]) :
-				     "memory");
+		restore_current();
 		restore_flags(flags);
 		if(prom_node_has_property(st_p, "keyboard"))
 			return PROMDEV_IKBD;
@@ -173,6 +170,7 @@
 
 	switch(prom_vers) {
 	case PROM_V0:
+	case PROM_SUN4:
 		switch(*romvec->pv_stdin) {
 		case PROMDEV_SCREEN:	return PROMDEV_OSCREEN;
 		case PROMDEV_TTYA:	return PROMDEV_OTTYA;
@@ -183,9 +181,7 @@
 	case PROM_V3:
 		save_flags(flags); cli();
 		st_p = (*romvec->pv_v2devops.v2_inst2pkg)(*romvec->pv_v2bootargs.fd_stdout);
-		__asm__ __volatile__("ld [%0], %%g6\n\t" : :
-				     "r" (&current_set[hard_smp_processor_id()]) :
-				     "memory");
+		restore_current();
 		restore_flags(flags);
 		propl = prom_getproperty(st_p, "device_type", propb, sizeof(propb));
 		if (propl >= 0 && propl == sizeof("display") &&
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/prom/devmap.c linux/arch/sparc/prom/devmap.c
--- v2.1.96/linux/arch/sparc/prom/devmap.c	Thu May 15 16:48:02 1997
+++ linux/arch/sparc/prom/devmap.c	Tue Apr 14 17:44:20 1998
@@ -1,4 +1,4 @@
-/* $Id: devmap.c,v 1.5 1997/05/14 20:44:59 davem Exp $
+/* $Id: devmap.c,v 1.6 1998/03/09 14:04:23 jj Exp $
  * promdevmap.c:  Map device/IO areas to virtual addresses.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -11,8 +11,7 @@
 #include <asm/openprom.h>
 #include <asm/oplib.h>
 
-/* XXX Let's get rid of this thing if we can... */
-extern struct task_struct *current_set[NR_CPUS];
+extern void restore_current(void);
 
 /* Just like the routines in palloc.c, these should not be used
  * by the kernel at all.  Bootloader facility mainly.  And again,
@@ -35,9 +34,7 @@
 	else
 	ret = (*(romvec->pv_v2devops.v2_dumb_mmap))(vhint, ios, paddr,
 						    num_bytes);
-	__asm__ __volatile__("ld [%0], %%g6\n\t" : :
-			     "r" (&current_set[hard_smp_processor_id()]) :
-			     "memory");
+	restore_current();
 	restore_flags(flags);
 	return ret;
 }
@@ -51,9 +48,7 @@
 	if(num_bytes == 0x0) return;
 	save_flags(flags); cli();
 	(*(romvec->pv_v2devops.v2_dumb_munmap))(vaddr, num_bytes);
-	__asm__ __volatile__("ld [%0], %%g6\n\t" : :
-			     "r" (&current_set[hard_smp_processor_id()]) :
-			     "memory");
+	restore_current();
 	restore_flags(flags);
 	return;
 }
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/prom/devops.c linux/arch/sparc/prom/devops.c
--- v2.1.96/linux/arch/sparc/prom/devops.c	Thu May 15 16:48:02 1997
+++ linux/arch/sparc/prom/devops.c	Tue Apr 14 17:44:20 1998
@@ -1,4 +1,4 @@
-/* $Id: devops.c,v 1.10 1997/05/14 20:44:59 davem Exp $
+/* $Id: devops.c,v 1.11 1998/03/09 14:04:24 jj Exp $
  * devops.c:  Device operations using the PROM.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -10,8 +10,7 @@
 #include <asm/openprom.h>
 #include <asm/oplib.h>
 
-/* XXX Let's get rid of this thing if we can... */
-extern struct task_struct *current_set[NR_CPUS];
+extern void restore_current(void);
 
 /* Open the device described by the string 'dstr'.  Returns the handle
  * to that device used for subsequent operations on that device.
@@ -37,9 +36,7 @@
 		handle = -1;
 		break;
 	};
-	__asm__ __volatile__("ld [%0], %%g6\n\t" : :
-			     "r" (&current_set[hard_smp_processor_id()]) :
-			     "memory");
+	restore_current();
 	restore_flags(flags);
 
 	return handle;
@@ -63,9 +60,7 @@
 	default:
 		break;
 	};
-	__asm__ __volatile__("ld [%0], %%g6\n\t" : :
-			     "r" (&current_set[hard_smp_processor_id()]) :
-			     "memory");
+	restore_current();
 	restore_flags(flags);
 	return 0;
 }
@@ -90,9 +85,7 @@
 	default:
 		break;
 	};
-	__asm__ __volatile__("ld [%0], %%g6\n\t" : :
-			     "r" (&current_set[hard_smp_processor_id()]) :
-			     "memory");
+	restore_current();
 	restore_flags(flags);
 
 	return;
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/prom/init.c linux/arch/sparc/prom/init.c
--- v2.1.96/linux/arch/sparc/prom/init.c	Thu Mar 27 14:40:00 1997
+++ linux/arch/sparc/prom/init.c	Tue Apr 14 17:44:20 1998
@@ -1,8 +1,9 @@
-/* $Id: init.c,v 1.11 1997/03/18 17:58:24 jj Exp $
+/* $Id: init.c,v 1.12 1998/01/30 10:59:02 jj Exp $
  * init.c:  Initialize internal variables used by the PROM
  *          library functions.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
  */
 
 #include <linux/config.h>
@@ -11,10 +12,12 @@
 
 #include <asm/openprom.h>
 #include <asm/oplib.h>
+#include <asm/sun4prom.h>
 
 struct linux_romvec *romvec;
 enum prom_major_version prom_vers;
 unsigned int prom_rev, prom_prev;
+linux_sun4_romvec *sun4_romvec;
 
 /* The root node of the prom device tree. */
 int prom_root_node;
@@ -34,11 +37,14 @@
 
 __initfunc(void prom_init(struct linux_romvec *rp))
 {
+#ifdef CONFIG_SUN4
+	extern struct linux_romvec *sun4_prom_init(void);
+	rp = sun4_prom_init();
+#endif
 #if CONFIG_AP1000
 	extern struct linux_romvec *ap_prom_init(void);
 	rp = ap_prom_init();
 #endif
-
 	romvec = rp;
 
 	switch(romvec->pv_romvers) {
@@ -51,6 +57,9 @@
 	case 3:
 		prom_vers = PROM_V3;
 		break;
+	case 40:
+		prom_vers = PROM_SUN4;
+		break;
 	case 42: /* why not :-) */
 		prom_vers = PROM_AP1000;
 		break;
@@ -83,8 +92,11 @@
 
 	prom_ranges_init();
 
+#ifndef CONFIG_SUN4
+	/* SUN4 prints this in sun4_prom_init */
 	printk("PROMLIB: Sun Boot Prom Version %d Revision %d\n",
 	       romvec->pv_romvers, prom_rev);
+#endif
 
 	/* Initialization successful. */
 	return;
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/prom/memory.c linux/arch/sparc/prom/memory.c
--- v2.1.96/linux/arch/sparc/prom/memory.c	Thu May 29 21:53:04 1997
+++ linux/arch/sparc/prom/memory.c	Tue Apr 14 17:44:20 1998
@@ -1,8 +1,9 @@
-/* $Id: memory.c,v 1.12 1997/05/27 06:45:57 davem Exp $
+/* $Id: memory.c,v 1.13 1998/01/30 10:59:03 jj Exp $
  * memory.c: Prom routine for acquiring various bits of information
  *           about RAM on the machine, both virtual and physical.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1997 Michael A. Griffith (grif@acm.org)
  */
 
 #include <linux/config.h>
@@ -10,6 +11,7 @@
 #include <linux/init.h>
 
 #include <asm/openprom.h>
+#include <asm/sun4prom.h>
 #include <asm/oplib.h>
 
 /* This routine, for consistency, returns the ram parameters in the
@@ -177,6 +179,21 @@
 		prom_sortmemlist(prom_phys_avail);
 		break;
 
+	case PROM_SUN4:
+#ifdef CONFIG_SUN4	
+		/* how simple :) */
+		prom_phys_total[0].start_adr = 0x0;
+		prom_phys_total[0].num_bytes = *(sun4_romvec->memorysize);
+		prom_phys_total[0].theres_more = 0x0;
+		prom_prom_taken[0].start_adr = 0x0; 
+		prom_prom_taken[0].num_bytes = 0x0;
+		prom_prom_taken[0].theres_more = 0x0;
+		prom_phys_avail[0].start_adr = 0x0;
+		prom_phys_avail[0].num_bytes = *(sun4_romvec->memoryavail);
+		prom_phys_avail[0].theres_more = 0x0;
+#endif
+		break;
+
         case PROM_AP1000:
 #if CONFIG_AP1000
 		/* really simple memory map */
@@ -189,9 +206,6 @@
 		prom_phys_avail[0].start_adr = 0x00000000;
 		prom_phys_avail[0].num_bytes = prom_phys_total[0].num_bytes;
 		prom_phys_avail[0].theres_more = 0x0;
-		prom_sortmemlist(prom_phys_total);
-		prom_sortmemlist(prom_prom_taken);
-		prom_sortmemlist(prom_phys_avail);
 #endif
 	default:
 		break;
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/prom/misc.c linux/arch/sparc/prom/misc.c
--- v2.1.96/linux/arch/sparc/prom/misc.c	Thu May 15 16:48:02 1997
+++ linux/arch/sparc/prom/misc.c	Tue Apr 14 17:44:20 1998
@@ -1,4 +1,4 @@
-/* $Id: misc.c,v 1.15 1997/05/14 20:45:00 davem Exp $
+/* $Id: misc.c,v 1.16 1998/03/09 14:04:25 jj Exp $
  * misc.c:  Miscellaneous prom functions that don't belong
  *          anywhere else.
  *
@@ -13,8 +13,7 @@
 #include <asm/oplib.h>
 #include <asm/auxio.h>
 
-/* XXX Let's get rid of this thing if we can... */
-extern struct task_struct *current_set[NR_CPUS];
+extern void restore_current(void);
 
 /* Reset and reboot the machine with the command 'bcommand'. */
 void
@@ -24,9 +23,7 @@
 	save_flags(flags); cli();
 	(*(romvec->pv_reboot))(bcommand);
 	/* Never get here. */
-	__asm__ __volatile__("ld [%0], %%g6\n\t" : :
-			     "r" (&current_set[hard_smp_processor_id()]) :
-			     "memory");
+	restore_current();
 	restore_flags(flags);
 }
 
@@ -42,9 +39,7 @@
 		(*(romvec->pv_fortheval.v0_eval))(strlen(fstring), fstring);
 	else
 		(*(romvec->pv_fortheval.v2_eval))(fstring);
-	__asm__ __volatile__("ld [%0], %%g6\n\t" : :
-			     "r" (&current_set[hard_smp_processor_id()]) :
-			     "memory");
+	restore_current();
 	restore_flags(flags);
 }
 
@@ -74,9 +69,7 @@
 	install_obp_ticker();
 	save_flags(flags); cli();
 	(*(romvec->pv_abort))();
-	__asm__ __volatile__("ld [%0], %%g6\n\t" : :
-			     "r" (&current_set[hard_smp_processor_id()]) :
-			     "memory");
+	restore_current();
 	restore_flags(flags);
 	install_linux_ticker();
 #ifdef CONFIG_SUN_AUXIO
@@ -99,9 +92,7 @@
 	save_flags(flags); cli();
 	(*(romvec->pv_halt))();
 	/* Never get here. */
-	__asm__ __volatile__("ld [%0], %%g6\n\t" : :
-			     "r" (&current_set[hard_smp_processor_id()]) :
-			     "memory");
+	restore_current();
 	restore_flags(flags);
 	goto again; /* PROM is out to get me -DaveM */
 }
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/prom/mp.c linux/arch/sparc/prom/mp.c
--- v2.1.96/linux/arch/sparc/prom/mp.c	Thu May 15 16:48:02 1997
+++ linux/arch/sparc/prom/mp.c	Tue Apr 14 17:44:20 1998
@@ -1,4 +1,4 @@
-/* $Id: mp.c,v 1.9 1997/05/14 20:45:01 davem Exp $
+/* $Id: mp.c,v 1.10 1998/03/09 14:04:26 jj Exp $
  * mp.c:  OpenBoot Prom Multiprocessor support routines.  Don't call
  *        these on a UP or else you will halt and catch fire. ;)
  *
@@ -12,8 +12,7 @@
 #include <asm/openprom.h>
 #include <asm/oplib.h>
 
-/* XXX Let's get rid of this thing if we can... */
-extern struct task_struct *current_set[NR_CPUS];
+extern void restore_current(void);
 
 /* Start cpu with prom-tree node 'cpunode' using context described
  * by 'ctable_reg' in context 'ctx' at program counter 'pc'.
@@ -38,9 +37,7 @@
 		ret = (*(romvec->v3_cpustart))(cpunode, (int) ctable_reg, ctx, pc);
 		break;
 	};
-	__asm__ __volatile__("ld [%0], %%g6\n\t" : :
-			     "r" (&current_set[hard_smp_processor_id()]) :
-			     "memory");
+	restore_current();
 	restore_flags(flags);
 
 	return ret;
@@ -67,9 +64,7 @@
 		ret = (*(romvec->v3_cpustop))(cpunode);
 		break;
 	};
-	__asm__ __volatile__("ld [%0], %%g6\n\t" : :
-			     "r" (&current_set[hard_smp_processor_id()]) :
-			     "memory");
+	restore_current();
 	restore_flags(flags);
 
 	return ret;
@@ -96,9 +91,7 @@
 		ret = (*(romvec->v3_cpuidle))(cpunode);
 		break;
 	};
-	__asm__ __volatile__("ld [%0], %%g6\n\t" : :
-			     "r" (&current_set[hard_smp_processor_id()]) :
-			     "memory");
+	restore_current();
 	restore_flags(flags);
 
 	return ret;
@@ -125,9 +118,7 @@
 		ret = (*(romvec->v3_cpuresume))(cpunode);
 		break;
 	};
-	__asm__ __volatile__("ld [%0], %%g6\n\t" : :
-			     "r" (&current_set[hard_smp_processor_id()]) :
-			     "memory");
+	restore_current();
 	restore_flags(flags);
 
 	return ret;
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/prom/ranges.c linux/arch/sparc/prom/ranges.c
--- v2.1.96/linux/arch/sparc/prom/ranges.c	Mon Jan 12 15:15:43 1998
+++ linux/arch/sparc/prom/ranges.c	Tue Apr 14 17:44:20 1998
@@ -1,4 +1,4 @@
-/* $Id: ranges.c,v 1.10 1997/12/19 12:37:18 jj Exp $
+/* $Id: ranges.c,v 1.11 1998/01/30 10:59:05 jj Exp $
  * ranges.c: Handle ranges in newer proms for obio/sbus.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -68,7 +68,7 @@
 void prom_apply_sbus_ranges(struct linux_sbus *sbus, struct linux_prom_registers *regs,
 			    int nregs, struct linux_sbus_device *sdev)
 {
-	if(sbus->num_sbus_ranges) {
+	if(sbus && sbus->num_sbus_ranges) {
 		if(sdev && (sdev->ranges_applied == 0)) {
 			sdev->ranges_applied = 1;
 			prom_adjust_regs(regs, nregs, sbus->sbus_ranges,
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/prom/segment.c linux/arch/sparc/prom/segment.c
--- v2.1.96/linux/arch/sparc/prom/segment.c	Thu May 15 16:48:02 1997
+++ linux/arch/sparc/prom/segment.c	Tue Apr 14 17:44:20 1998
@@ -1,4 +1,4 @@
-/* $Id: segment.c,v 1.5 1997/05/14 20:45:02 davem Exp $
+/* $Id: segment.c,v 1.6 1998/03/09 14:04:27 jj Exp $
  * segment.c:  Prom routine to map segments in other contexts before
  *             a standalone is completely mapped.  This is for sun4 and
  *             sun4c architectures only.
@@ -12,8 +12,7 @@
 #include <asm/openprom.h>
 #include <asm/oplib.h>
 
-/* XXX Let's get rid of this thing if we can... */
-extern struct task_struct *current_set[NR_CPUS];
+extern void restore_current(void);
 
 /* Set physical segment 'segment' at virtual address 'vaddr' in
  * context 'ctx'.
@@ -24,9 +23,7 @@
 	unsigned long flags;
 	save_flags(flags); cli();
 	(*(romvec->pv_setctxt))(ctx, (char *) vaddr, segment);
-	__asm__ __volatile__("ld [%0], %%g6\n\t" : :
-			     "r" (&current_set[hard_smp_processor_id()]) :
-			     "memory");
+	restore_current();
 	restore_flags(flags);
 	return;
 }
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/prom/sun4prom.c linux/arch/sparc/prom/sun4prom.c
--- v2.1.96/linux/arch/sparc/prom/sun4prom.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc/prom/sun4prom.c	Tue Apr 14 17:44:20 1998
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 1996 The Australian National University.
+ * Copyright (C) 1996 Fujitsu Laboratories Limited
+ * Copyright (C) 1997 Michael A. Griffith (grif@acm.org)
+ * Copyright (C) 1997 Sun Weenie (ko@ko.reno.nv.us)
+ * Copyright (C) 1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * 
+ * This software may be distributed under the terms of the Gnu
+ * Public License version 2 or later
+ *
+ * fake a really simple Sun prom for the SUN4
+ */
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <asm/oplib.h>
+#include <asm/idprom.h> 
+#include <asm/machines.h> 
+#include <asm/sun4prom.h>
+#include <asm/asi.h>
+#include <asm/contregs.h>
+#include <linux/init.h>
+
+static struct linux_romvec sun4romvec;
+static struct idprom sun4_idprom;
+
+struct property {
+	char *name;
+	char *value;
+	int length;
+};
+
+struct node {
+	int level;
+	struct property *properties;
+};
+
+struct property null_properties = { NULL, NULL, -1 };
+
+struct property root_properties[] = {
+	{"device_type", "cpu", 4},
+	{"idprom", (char *)&sun4_idprom, sizeof(struct idprom)},
+	{NULL, NULL, -1}
+};
+
+struct node nodes[] = {
+	{ 0, &null_properties }, 
+	{ 0, root_properties },
+	{ -1,&null_properties }
+};
+
+
+static int no_nextnode(int node)
+{
+	if (nodes[node].level == nodes[node+1].level)
+		return node+1;
+	return -1;
+}
+
+static int no_child(int node)
+{
+	if (nodes[node].level == nodes[node+1].level-1)
+		return node+1;
+	return -1;
+}
+
+static struct property *find_property(int node,char *name)
+{
+	struct property *prop = &nodes[node].properties[0];
+	while (prop && prop->name) {
+		if (strcmp(prop->name,name) == 0) return prop;
+		prop++;
+	}
+	return NULL;
+}
+
+static int no_proplen(int node,char *name)
+{
+	struct property *prop = find_property(node,name);
+	if (prop) return prop->length;
+	return -1;
+}
+
+static int no_getprop(int node,char *name,char *value)
+{
+	struct property *prop = find_property(node,name);
+	if (prop) {
+		memcpy(value,prop->value,prop->length);
+		return 1;
+	}
+	return -1;
+}
+
+static int no_setprop(int node,char *name,char *value,int len)
+{
+	return -1;
+}
+
+static char *no_nextprop(int node,char *name)
+{
+	struct property *prop = find_property(node,name);
+	if (prop) return prop[1].name;
+	return NULL;
+}
+
+static struct linux_nodeops sun4_nodeops = {
+	no_nextnode,
+	no_child,
+	no_proplen,
+	no_getprop,
+	no_setprop,
+	no_nextprop
+};
+	
+static int synch_hook;
+
+__initfunc(struct linux_romvec *sun4_prom_init(void))
+{
+	int i;
+	unsigned char x;
+	char *p;
+                                
+	p = (char *)&sun4_idprom;
+	for (i = 0; i < sizeof(sun4_idprom); i++) {
+		__asm__ __volatile__ ("lduba [%1] %2, %0" : "=r" (x) :
+				      "r" (AC_IDPROM + i), "i" (ASI_CONTROL));
+		*p++ = x;
+	}
+
+	memset(&sun4romvec,0,sizeof(sun4romvec));
+
+	sun4_romvec = (linux_sun4_romvec *) SUN4_PROM_VECTOR;
+
+	sun4romvec.pv_romvers = 40;
+	sun4romvec.pv_nodeops = &sun4_nodeops;
+	sun4romvec.pv_reboot = sun4_romvec->reboot;
+	sun4romvec.pv_abort = sun4_romvec->abortentry;
+	sun4romvec.pv_halt = sun4_romvec->exittomon;
+	sun4romvec.pv_synchook = (void (**)(void))&synch_hook;
+	sun4romvec.pv_setctxt = sun4_romvec->setcxsegmap;
+	sun4romvec.pv_v0bootargs = sun4_romvec->bootParam;
+	sun4romvec.pv_nbgetchar = sun4_romvec->mayget;
+	sun4romvec.pv_nbputchar = sun4_romvec->mayput;
+	sun4romvec.pv_stdin = sun4_romvec->insource;
+	sun4romvec.pv_stdout = sun4_romvec->outsink;
+	
+	/*
+	 * We turn on the LEDs to let folks without monitors or
+	 * terminals know we booted.   Nothing too fancy now.  They
+	 * are all on, except for LED 5, which blinks.   When we
+	 * have more time, we can teach the penguin to say "By your
+	 * command" or "Activating turbo boost, Michael". :-)
+	 */
+	sun4_romvec->setLEDs(0x0);
+	
+	printk("PROMLIB: Old Sun4 boot PROM monitor %s, romvec version %d\n",
+		sun4_romvec->monid,
+		sun4_romvec->romvecversion);
+
+	return &sun4romvec;
+}
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/prom/tree.c linux/arch/sparc/prom/tree.c
--- v2.1.96/linux/arch/sparc/prom/tree.c	Mon Jan 12 15:15:43 1998
+++ linux/arch/sparc/prom/tree.c	Tue Apr 14 17:44:20 1998
@@ -1,4 +1,4 @@
-/* $Id: tree.c,v 1.22 1997/09/25 02:19:22 davem Exp $
+/* $Id: tree.c,v 1.24 1998/03/09 14:04:29 jj Exp $
  * tree.c: Basic device tree traversal/scanning for the Linux
  *         prom library.
  *
@@ -15,13 +15,7 @@
 #include <asm/openprom.h>
 #include <asm/oplib.h>
 
-/* XXX Let's get rid of this thing if we can... */
-extern struct task_struct *current_set[NR_CPUS];
-
-/* Macro to restore "current" to the g6 register. */
-#define restore_current() __asm__ __volatile__("ld [%0], %%g6\n\t" : : \
-			  "r" (&current_set[hard_smp_processor_id()]) : \
-			  "memory")
+extern void restore_current(void);
 
 static char promlib_buf[128];
 
@@ -95,12 +89,11 @@
 	int ret;
 	unsigned long flags;
 
-	save_flags(flags); cli();
-
 	if((!node) || (!prop))
-		ret = -1;
-	else
-		ret = prom_nodeops->no_proplen(node, prop);
+		return -1;
+		
+	save_flags(flags); cli();
+	ret = prom_nodeops->no_proplen(node, prop);
 	restore_current();
 	restore_flags(flags);
 	return ret;
@@ -115,15 +108,12 @@
 	int plen, ret;
 	unsigned long flags;
 
-	save_flags(flags); cli();
-
 	plen = prom_getproplen(node, prop);
 	if((plen > bufsize) || (plen == 0) || (plen == -1))
-		ret = -1;
-	else {
-		/* Ok, things seem all right. */
-		ret = prom_nodeops->no_getprop(node, prop, buffer);
-	}
+		return -1;
+	/* Ok, things seem all right. */
+	save_flags(flags); cli();
+	ret = prom_nodeops->no_getprop(node, prop, buffer);
 	restore_current();
 	restore_flags(flags);
 	return ret;
diff -u --recursive --new-file v2.1.96/linux/arch/sparc/vmlinux.lds linux/arch/sparc/vmlinux.lds
--- v2.1.96/linux/arch/sparc/vmlinux.lds	Sun Jan 26 02:07:09 1997
+++ linux/arch/sparc/vmlinux.lds	Tue Apr 14 17:44:20 1998
@@ -32,6 +32,7 @@
   . = ALIGN(4096);
   __init_begin = .;
   .text.init : { *(.text.init) }
+  __init_text_end = .;
   .data.init : { *(.data.init) }
   . = ALIGN(4096);
   __init_end = .;
diff -u --recursive --new-file v2.1.96/linux/arch/sparc64/Makefile linux/arch/sparc64/Makefile
--- v2.1.96/linux/arch/sparc64/Makefile	Mon Jan 12 15:15:43 1998
+++ linux/arch/sparc64/Makefile	Tue Apr 14 17:44:20 1998
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.24 1997/10/02 16:31:16 jj Exp $
+# $Id: Makefile,v 1.25 1998/04/06 16:10:31 jj Exp $
 # sparc64/Makefile
 #
 # Makefile for the architecture dependent flags and dependencies on the
@@ -38,9 +38,7 @@
   SUBDIRS += arch/sparc64/solaris
 endif
 
-ifneq ($(CONFIG_MATHEMU),n)
-  SUBDIRS += arch/sparc64/math-emu
-endif
+SUBDIRS += arch/sparc64/math-emu
 
 CORE_FILES := arch/sparc64/kernel/kernel.o arch/sparc64/mm/mm.o $(CORE_FILES)
 
@@ -48,9 +46,7 @@
   CORE_FILES += arch/sparc64/solaris/solaris.o
 endif
 
-ifeq ($(CONFIG_MATHEMU),y)
-  CORE_FILES += arch/sparc64/math-emu/math-emu.o
-endif
+CORE_FILES += arch/sparc64/math-emu/math-emu.o
 
 LIBS := $(TOPDIR)/lib/lib.a $(LIBS) $(TOPDIR)/arch/sparc64/prom/promlib.a \
 	$(TOPDIR)/arch/sparc64/lib/lib.a
diff -u --recursive --new-file v2.1.96/linux/arch/sparc64/config.in linux/arch/sparc64/config.in
--- v2.1.96/linux/arch/sparc64/config.in	Wed Apr  1 20:11:48 1998
+++ linux/arch/sparc64/config.in	Tue Apr 14 17:44:20 1998
@@ -1,4 +1,4 @@
-# $Id: config.in,v 1.36 1998/01/10 19:04:30 ecd Exp $
+# $Id: config.in,v 1.44 1998/04/06 16:10:35 jj Exp $
 # For a description of the syntax of this configuration file,
 # see the Configure script.
 #
@@ -67,9 +67,6 @@
 if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
   tristate 'Solaris binary emulation' CONFIG_SOLARIS_EMUL
 fi
-if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
-  tristate 'Kernel Quad FPU (long double) and subnormal float/double emulation' CONFIG_MATHEMU
-fi
 endmenu
 
 if [ "$CONFIG_PCI" = "y" ]; then
@@ -84,6 +81,7 @@
       bool '    Support IEEE1284 status readback' CONFIG_PRINTER_READBACK
     fi
   fi
+  tristate 'SUNW,envctrl support' CONFIG_ENVCTRL
 fi
 
 mainmenu_option next_comment
@@ -109,14 +107,16 @@
 
 if [ "$CONFIG_PCI" = "y" ]; then
 	tristate 'Ultra/PCI IDE disk/cdrom/tape/floppy support' CONFIG_BLK_DEV_IDE
-	if [ "$CONFIG_BLK_DEV_IDE" = "y" ]; then
+	if [ "$CONFIG_BLK_DEV_IDE" != "n" ]; then
 	  dep_tristate '   Include IDE/ATA-2 DISK support' CONFIG_BLK_DEV_IDEDISK $CONFIG_BLK_DEV_IDE
 	  dep_tristate '   Include IDE/ATAPI CDROM support' CONFIG_BLK_DEV_IDECD $CONFIG_BLK_DEV_IDE
 	  dep_tristate '   Include IDE/ATAPI TAPE support' CONFIG_BLK_DEV_IDETAPE $CONFIG_BLK_DEV_IDE
 	  dep_tristate '   Include IDE/ATAPI FLOPPY support' CONFIG_BLK_DEV_IDEFLOPPY $CONFIG_BLK_DEV_IDE
 	  dep_tristate '   SCSI emulation support' CONFIG_BLK_DEV_IDESCSI $CONFIG_BLK_DEV_IDE
-	  define_bool CONFIG_IDE_CHIPSETS y
-	  define_bool CONFIG_BLK_DEV_NS87415_AX y
+	  define_bool CONFIG_BLK_DEV_IDEPCI y
+	  define_bool CONFIG_BLK_DEV_IDEDMA y
+	  define_bool CONFIG_BLK_DEV_NS87415 y
+	  define_bool CONFIG_BLK_DEV_CMD646 y
 	fi
 fi
 
diff -u --recursive --new-file v2.1.96/linux/arch/sparc64/defconfig linux/arch/sparc64/defconfig
--- v2.1.96/linux/arch/sparc64/defconfig	Wed Apr  1 20:11:48 1998
+++ linux/arch/sparc64/defconfig	Tue Apr 14 17:44:20 1998
@@ -25,12 +25,12 @@
 CONFIG_SUN_MOUSE=y
 CONFIG_SERIAL=y
 CONFIG_SUN_SERIAL=y
+CONFIG_SERIAL_CONSOLE=y
 CONFIG_SUN_KEYBOARD=y
 CONFIG_SUN_CONSOLE=y
 CONFIG_SUN_AUXIO=y
 CONFIG_SUN_IO=y
 CONFIG_PCI=y
-CONFIG_PCI_OLD_PROC=y
 
 #
 # SBUS Frame Buffer support
@@ -52,10 +52,11 @@
 CONFIG_SUN_OPENPROMIO=m
 CONFIG_SUN_MOSTEK_RTC=y
 CONFIG_SAB82532=y
-# CONFIG_OBP_FLASH is not set
+CONFIG_OBP_FLASH=m
 # CONFIG_SUN_BPP is not set
 # CONFIG_SUN_VIDEOPIX is not set
 CONFIG_SUN_OPENPROMFS=m
+CONFIG_PCI_OLD_PROC=y
 CONFIG_NET=y
 CONFIG_SYSVIPC=y
 # CONFIG_BSD_PROCESS_ACCT is not set
@@ -67,11 +68,12 @@
 CONFIG_BINFMT_MISC=m
 CONFIG_BINFMT_JAVA=m
 CONFIG_SOLARIS_EMUL=m
-CONFIG_MATHEMU=m
 CONFIG_PARPORT=y
 CONFIG_PARPORT_AX=y
+# CONFIG_PARPORT_OTHER is not set
 CONFIG_PRINTER=y
 CONFIG_PRINTER_READBACK=y
+CONFIG_ENVCTRL=y
 
 #
 # Floppy, IDE, and other block devices
@@ -92,8 +94,10 @@
 CONFIG_BLK_DEV_IDETAPE=m
 CONFIG_BLK_DEV_IDEFLOPPY=m
 # CONFIG_BLK_DEV_IDESCSI is not set
-CONFIG_IDE_CHIPSETS=y
-CONFIG_BLK_DEV_NS87415_AX=y
+CONFIG_BLK_DEV_IDEPCI=y
+CONFIG_BLK_DEV_IDEDMA=y
+CONFIG_BLK_DEV_NS87415=y
+CONFIG_BLK_DEV_CMD646=y
 
 #
 # Networking options
@@ -101,18 +105,18 @@
 CONFIG_PACKET=y
 # CONFIG_NETLINK is not set
 # CONFIG_FIREWALL is not set
-# CONFIG_NET_ALIAS is not set
+CONFIG_NET_ALIAS=y
+# CONFIG_FILTER is not set
 CONFIG_UNIX=y
 CONFIG_INET=y
 # CONFIG_IP_MULTICAST is not set
 # CONFIG_IP_ADVANCED_ROUTER is not set
 # CONFIG_IP_PNP is not set
 # CONFIG_IP_ACCT is not set
-# CONFIG_IP_MASQUERADE is not set
 # CONFIG_IP_ROUTER is not set
 # CONFIG_NET_IPIP is not set
 # CONFIG_NET_IPGRE is not set
-# CONFIG_IP_ALIAS is not set
+CONFIG_IP_ALIAS=y
 # CONFIG_SYN_COOKIES is not set
 
 #
@@ -123,30 +127,34 @@
 CONFIG_SKB_LARGE=y
 CONFIG_IPV6=m
 # CONFIG_IPV6_EUI64 is not set
-# CONFIG_IPV6_NO_PB is not set
 
 #
 #  
 #
 CONFIG_IPX=m
+
+#
+# IPX options
+#
 # CONFIG_IPX_INTERN is not set
 CONFIG_ATALK=m
-# CONFIG_AX25 is not set
 # CONFIG_X25 is not set
 # CONFIG_LAPB is not set
 # CONFIG_BRIDGE is not set
 # CONFIG_LLC is not set
 # CONFIG_WAN_ROUTER is not set
+# CONFIG_NET_FASTROUTE is not set
+# CONFIG_NET_HW_FLOWCONTROL is not set
 # CONFIG_CPU_IS_SLOW is not set
 CONFIG_NET_SCHED=y
 CONFIG_NET_SCH_CBQ=y
 CONFIG_NET_SCH_CSZ=y
-CONFIG_NET_SCH_HFQ=y
 CONFIG_NET_SCH_RED=y
 CONFIG_NET_SCH_SFQ=y
 CONFIG_NET_SCH_TBF=y
 CONFIG_NET_SCH_PFIFO=y
 CONFIG_NET_SCH_PRIO=y
+# CONFIG_NET_PROFILE is not set
 
 #
 # SCSI support
@@ -187,6 +195,21 @@
 # CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT is not set
 
 #
+# Fibre Channel support
+#
+CONFIG_FC4=m
+
+#
+# FC4 drivers
+#
+CONFIG_FC4_SOC=m
+
+#
+# FC4 targets
+#
+CONFIG_SCSI_PLUTO=m
+
+#
 # Network device support
 #
 CONFIG_NETDEVICES=y
@@ -227,21 +250,31 @@
 CONFIG_SMB_FS=m
 CONFIG_SMB_WIN95=y
 CONFIG_NCP_FS=m
+# CONFIG_NCPFS_PACKET_SIGNING is not set
+# CONFIG_NCPFS_IOCTL_LOCKING is not set
+# CONFIG_NCPFS_STRONG is not set
+# CONFIG_NCPFS_NFS_NS is not set
+# CONFIG_NCPFS_OS2_NS is not set
+# CONFIG_NCPFS_MOUNT_SUBDIR is not set
 CONFIG_HPFS_FS=m
+# CONFIG_NTFS_FS is not set
 CONFIG_SYSV_FS=m
 CONFIG_AFFS_FS=m
+# CONFIG_HFS_FS is not set
 CONFIG_ROMFS_FS=m
 CONFIG_AUTOFS_FS=m
 CONFIG_AMIGA_PARTITION=y
 CONFIG_UFS_FS=m
 CONFIG_BSD_DISKLABEL=y
 CONFIG_SMD_DISKLABEL=y
+CONFIG_SOLARIS_X86_PARTITION=y
+# CONFIG_ADFS_FS is not set
 # CONFIG_MAC_PARTITION is not set
+CONFIG_NLS=y
 
 #
 # Native Language Support
 #
-CONFIG_NLS=y
 # CONFIG_NLS_CODEPAGE_437 is not set
 # CONFIG_NLS_CODEPAGE_737 is not set
 # CONFIG_NLS_CODEPAGE_775 is not set
diff -u --recursive --new-file v2.1.96/linux/arch/sparc64/kernel/Makefile linux/arch/sparc64/kernel/Makefile
--- v2.1.96/linux/arch/sparc64/kernel/Makefile	Mon Jan 12 15:15:43 1998
+++ linux/arch/sparc64/kernel/Makefile	Tue Apr 14 17:44:20 1998
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.35 1997/09/20 21:48:58 davem Exp $
+# $Id: Makefile,v 1.36 1998/02/01 11:15:55 ecd Exp $
 # Makefile for the linux kernel.
 #
 # Note! Dependencies are done automagically by 'make dep', which also
@@ -61,13 +61,24 @@
 binfmt_elf32.o: $(TOPDIR)/fs/binfmt_elf.c
 
 check_asm: dummy
+	@echo "/* Automatically generated. Do not edit. */" > asm_offsets.h
+	@echo "#ifndef __ASM_OFFSETS_H__" >> asm_offsets.h
+	@echo "#define __ASM_OFFSETS_H__" >> asm_offsets.h
+	@echo "" >> asm_offsets.h
+	@echo "#ifndef __SMP__" >> asm_offsets.h
+	@echo "" >> asm_offsets.h
 	@echo "#include <linux/sched.h>" > tmp.c
 	$(CC) -E tmp.c -o tmp.i
-	@echo "/* Automatically generated. Do not edit. */" > check_asm.c; echo "#include <linux/sched.h>" >> check_asm.c; echo 'struct task_struct _task; struct mm_struct _mm; struct thread_struct _thread; int main(void) { printf ("/* Automatically generated. Do not edit. */\n#ifndef __ASM_OFFSETS_H__\n#define __ASM_OFFSETS_H__\n\n");' >> check_asm.c
+	@echo "/* Automatically generated. Do not edit. */" > check_asm.c
+	@echo "#include <linux/sched.h>" >> check_asm.c
+	@echo 'struct task_struct _task;' >> check_asm.c
+	@echo 'struct mm_struct _mm;' >> check_asm.c
+	@echo 'struct thread_struct _thread;' >> check_asm.c
+	@echo 'int main(void) {' >> check_asm.c
 	$(SH) ./check_asm.sh task tmp.i check_asm.c
 	$(SH) ./check_asm.sh mm tmp.i check_asm.c
 	$(SH) ./check_asm.sh thread tmp.i check_asm.c
-	@echo 'printf ("\n#endif /* __ASM_OFFSETS_H__ */\n"); return 0; }' >> check_asm.c
+	@echo 'return 0; }' >> check_asm.c
 	@rm -f tmp.[ci]
 	#$(CC) -o check_asm check_asm.c
 	# <hack> Until we can do this natively, a hack has to take place
@@ -75,9 +86,46 @@
 	$(HOSTCC) -Wa,-Av9a -o check_asm check_asm.s
 	@rm -f check_asm.s
 	# </hack>
-	./check_asm > asm_offsets.h
-	@if test -r $(HPATH)/asm/asm_offsets.h; then if cmp -s asm_offsets.h $(HPATH)/asm/asm_offsets.h; then echo $(HPATH)/asm/asm_offsets.h is unchanged; rm -f asm_offsets.h; else mv -f asm_offsets.h $(HPATH)/asm/asm_offsets.h; fi; else mv -f asm_offsets.h $(HPATH)/asm/asm_offsets.h; fi
+	./check_asm >> asm_offsets.h
 	@rm -f check_asm check_asm.c
+	@echo "" >> asm_offsets.h
+	@echo "#else /* __SMP__ */" >> asm_offsets.h
+	@echo "" >> asm_offsets.h
+	@echo "#include <linux/sched.h>" > tmp.c
+	$(CC) -D__SMP__ -E tmp.c -o tmp.i
+	@echo "/* Automatically generated. Do not edit. */" > check_asm.c
+	@echo "#include <linux/sched.h>" >> check_asm.c
+	@echo 'struct task_struct _task;' >> check_asm.c
+	@echo 'struct mm_struct _mm;' >> check_asm.c
+	@echo 'struct thread_struct _thread;' >> check_asm.c
+	@echo 'int main(void) {' >> check_asm.c
+	$(SH) ./check_asm.sh task tmp.i check_asm.c
+	$(SH) ./check_asm.sh mm tmp.i check_asm.c
+	$(SH) ./check_asm.sh thread tmp.i check_asm.c
+	@echo 'return 0; }' >> check_asm.c
+	@rm -f tmp.[ci]
+	#$(CC) -D__SMP__ -o check_asm check_asm.c
+	# <hack> Until we can do this natively, a hack has to take place
+	$(CC) -D__SMP__ -mmedlow -ffixed-g4 -S -o check_asm.s check_asm.c
+	$(HOSTCC) -Wa,-Av9a -o check_asm check_asm.s
+	@rm -f check_asm.s
+	# </hack>
+	./check_asm >> asm_offsets.h
+	@rm -f check_asm check_asm.c
+	@echo "" >> asm_offsets.h
+	@echo "#endif /* __SMP__ */" >> asm_offsets.h
+	@echo "" >> asm_offsets.h
+	@echo "#endif /* __ASM_OFFSETS_H__ */" >> asm_offsets.h
+	@if test -r $(HPATH)/asm/asm_offsets.h; then \
+	  if cmp -s asm_offsets.h $(HPATH)/asm/asm_offsets.h; then \
+	    echo $(HPATH)/asm/asm_offsets.h is unchanged; \
+	    rm -f asm_offsets.h; \
+	  else \
+	    mv -f asm_offsets.h $(HPATH)/asm/asm_offsets.h; \
+	  fi; \
+	else \
+	  mv -f asm_offsets.h $(HPATH)/asm/asm_offsets.h; \
+	fi
 
 
 include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v2.1.96/linux/arch/sparc64/kernel/binfmt_aout32.c linux/arch/sparc64/kernel/binfmt_aout32.c
--- v2.1.96/linux/arch/sparc64/kernel/binfmt_aout32.c	Mon Jan 12 15:15:44 1998
+++ linux/arch/sparc64/kernel/binfmt_aout32.c	Tue Apr 14 17:44:20 1998
@@ -20,6 +20,7 @@
 #include <linux/string.h>
 #include <linux/stat.h>
 #include <linux/fcntl.h>
+#include <linux/file.h>
 #include <linux/ptrace.h>
 #include <linux/user.h>
 #include <linux/malloc.h>
@@ -257,7 +258,7 @@
 	unsigned long p = bprm->p;
 	unsigned long fd_offset;
 	unsigned long rlim;
-int retval;
+	int retval;
 
 	ex = *((struct exec *) bprm->buf);		/* exec-header */
 	if ((N_MAGIC(ex) != ZMAGIC && N_MAGIC(ex) != OMAGIC &&
@@ -326,10 +327,10 @@
 			printk(KERN_NOTICE "executable not page aligned\n");
 
 		fd = open_dentry(bprm->dentry, O_RDONLY);
-
 		if (fd < 0)
 			return fd;
-		file = current->files->fd[fd];
+		file = fcheck(fd);
+
 		if (!file->f_op || !file->f_op->mmap) {
 			sys_close(fd);
 			do_mmap(NULL, 0, ex.a_text+ex.a_data,
@@ -397,6 +398,7 @@
 	return retval;
 }
 
+/* N.B. Move to .h file and use code in fs/binfmt_aout.c? */
 static inline int
 do_load_aout32_library(int fd)
 {
@@ -409,7 +411,7 @@
 	unsigned int start_addr;
 	unsigned long error;
 
-	file = current->files->fd[fd];
+	file = fcheck(fd);
 
 	if (!file || !file->f_op)
 		return -EACCES;
diff -u --recursive --new-file v2.1.96/linux/arch/sparc64/kernel/central.c linux/arch/sparc64/kernel/central.c
--- v2.1.96/linux/arch/sparc64/kernel/central.c	Thu Sep  4 12:54:48 1997
+++ linux/arch/sparc64/kernel/central.c	Tue Apr 14 17:44:20 1998
@@ -1,4 +1,4 @@
-/* $Id: central.c,v 1.4 1997/08/19 14:17:49 jj Exp $
+/* $Id: central.c,v 1.5 1998/02/12 15:57:59 jj Exp $
  * central.c: Central FHC driver for Sunfire/Starfire/Wildfire.
  *
  * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
@@ -35,22 +35,18 @@
 		printk("no central found.\n");
 		return memory_start;
 	}
-	prom_printf("CENTRAL: found central PROM node.\n");
 	printk("found central PROM node.\n");
 
 	/* Ok we got one, grab some memory for software state. */
 	memory_start = long_align(memory_start);
 	central_bus = (struct linux_central *) (memory_start);
 
-	prom_printf("CENTRAL: central_bus[%p] ", central_bus);
 	memory_start += sizeof(struct linux_central);
 	memory_start = long_align(memory_start);
 	fhc = (struct linux_fhc *)(memory_start);
 	memory_start += sizeof(struct linux_fhc);
 	memory_start = long_align(memory_start);
 
-	prom_printf("fhc[%p] ", fhc);
-
 	/* First init central. */
 	central_bus->child = fhc;
 	central_bus->prom_node = cnode;
@@ -58,7 +54,6 @@
 	prom_getstring(cnode, "name", namebuf, sizeof(namebuf));
 	strcpy(central_bus->prom_name, namebuf);
 
-	prom_printf("init_central_ranges ");
 	prom_central_ranges_init(cnode, central_bus);
 
 	/* And then central's FHC. */
@@ -73,27 +68,15 @@
 	prom_getstring(fnode, "name", namebuf, sizeof(namebuf));
 	strcpy(fhc->prom_name, namebuf);
 
-	prom_printf("cnode[%x] fnode[%x] init_fhc_ranges\n", cnode, fnode);
 	prom_fhc_ranges_init(fnode, fhc);
 
-	/* Finally, map in FHC register set.  (From the prtconf dumps
-	 * I have seen on Ex000 boxes only the central ranges need to
-	 * be applied to the fhc internal register set) -DaveM
-	 */
-	err = prom_getproperty(fnode, "reg", (char *)&fpregs[0], sizeof(fpregs));
-	if(err == -1) {
+	/* Finally, map in FHC register set. */
+	if (prom_getproperty(fnode, "reg", (char *)&fpregs[0], sizeof(fpregs)) == -1) {
 		prom_printf("CENTRAL: fatal error, cannot get fhc regs.\n");
 		prom_halt();
 	}
 	prom_apply_central_ranges(central_bus, &fpregs[0], 6);
-	prom_printf("CENTRAL: FHC_REGS[(%08x,%08x) (%08x,%08x) "
-		    "(%08x,%08x) (%08x,%08x) (%08x,%08x) (%08x,%08x)]\n",
-		    fpregs[0].which_io, fpregs[0].phys_addr,
-		    fpregs[1].which_io, fpregs[1].phys_addr,
-		    fpregs[2].which_io, fpregs[2].phys_addr,
-		    fpregs[3].which_io, fpregs[3].phys_addr,
-		    fpregs[4].which_io, fpregs[4].phys_addr,
-		    fpregs[5].which_io, fpregs[5].phys_addr);
+	
 	fhc->fhc_regs.pregs = (struct fhc_internal_regs *)
 		__va((((unsigned long)fpregs[0].which_io)<<32) |
 		     (((unsigned long)fpregs[0].phys_addr)));
@@ -112,14 +95,8 @@
 	fhc->fhc_regs.tregs = (struct fhc_tod_regs *)
 		__va((((unsigned long)fpregs[5].which_io)<<32) |
 		     (((unsigned long)fpregs[5].phys_addr)));
-	prom_printf("CENTRAL: FHC_REGS[%p %p %p %p %p %p]\n",
-		    fhc->fhc_regs.pregs, fhc->fhc_regs.ireg,
-		    fhc->fhc_regs.ffregs, fhc->fhc_regs.sregs,
-		    fhc->fhc_regs.uregs, fhc->fhc_regs.tregs);
 
-	prom_printf("CENTRAL: reading FHC_ID register... ");
 	err = fhc->fhc_regs.pregs->fhc_id;
-	prom_printf("VALUE[%x]\n", err);
 	printk("FHC Version[%x] PartID[%x] Manufacturer[%x]\n",
 	       ((err & FHC_ID_VERS) >> 28),
 	       ((err & FHC_ID_PARTID) >> 12),
diff -u --recursive --new-file v2.1.96/linux/arch/sparc64/kernel/cpu.c linux/arch/sparc64/kernel/cpu.c
--- v2.1.96/linux/arch/sparc64/kernel/cpu.c	Sat Aug 16 09:51:08 1997
+++ linux/arch/sparc64/kernel/cpu.c	Tue Apr 14 17:44:20 1998
@@ -32,7 +32,8 @@
   { 0x17, 0x10, 0, "UltraSparc I integrated FPU"},
   { 0x22, 0x10, 0, "UltraSparc II integrated FPU"},
   { 0x17, 0x11, 0, "UltraSparc II integrated FPU"},
-  { 0x17, 0x12, 0, "UltraSparc III integrated FPU"},
+  { 0x17, 0x12, 0, "UltraSparc IIi integrated FPU"},
+  { 0x17, 0x13, 0, "UltraSparc III integrated FPU"},
 };
 
 #define NSPARCFPU  (sizeof(linux_sparc_fpu)/sizeof(struct cpu_fp_info))
@@ -41,7 +42,8 @@
   { 0x17, 0x10, "TI UltraSparc I   (SpitFire)"},
   { 0x22, 0x10, "TI UltraSparc II  (BlackBird)"},
   { 0x17, 0x11, "TI UltraSparc II  (BlackBird)"},
-  { 0x17, 0x12, "TI UltraSparc III (Cheetah)"},  /* A guess... */
+  { 0x17, 0x12, "TI UltraSparc IIi"},
+  { 0x17, 0x13, "TI UltraSparc III (Cheetah)"},  /* A guess... */
 };
 
 #define NSPARCCHIPS  (sizeof(linux_sparc_chips)/sizeof(struct cpu_iu_info))
diff -u --recursive --new-file v2.1.96/linux/arch/sparc64/kernel/devices.c linux/arch/sparc64/kernel/devices.c
--- v2.1.96/linux/arch/sparc64/kernel/devices.c	Sat Aug 16 09:51:08 1997
+++ linux/arch/sparc64/kernel/devices.c	Tue Apr 14 17:44:20 1998
@@ -13,7 +13,8 @@
 #include <asm/system.h>
 #include <asm/smp.h>
 
-struct prom_cpuinfo linux_cpus[NR_CPUS];
+struct prom_cpuinfo linux_cpus[NR_CPUS] __initdata = { { 0 } };
+unsigned prom_cpu_nodes[NR_CPUS];
 int linux_num_cpus = 0;
 
 extern void cpu_probe(void);
@@ -64,6 +65,8 @@
 	prom_node_cpu = cpu_nds[0];
 
 	linux_num_cpus = cpu_ctr;
+	
+	prom_cpu_nodes[0] = prom_node_cpu;
 
 	cpu_probe();
 	return central_probe(mem_start);
diff -u --recursive --new-file v2.1.96/linux/arch/sparc64/kernel/dtlb_miss.S linux/arch/sparc64/kernel/dtlb_miss.S
--- v2.1.96/linux/arch/sparc64/kernel/dtlb_miss.S	Mon Jan 12 15:15:44 1998
+++ linux/arch/sparc64/kernel/dtlb_miss.S	Tue Apr 14 17:44:20 1998
@@ -1,9 +1,9 @@
-/* $Id: dtlb_miss.S,v 1.14 1997/10/14 01:48:28 davem Exp $
+/* $Id: dtlb_miss.S,v 1.15 1998/01/14 17:14:44 jj Exp $
  * dtlb_miss.S:	Data TLB miss code, this is included directly
  *              into the trap table.
  *
  * Copyright (C) 1996,1997 David S. Miller (davem@caip.rutgers.edu)
- * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
  */
 
 /* The basic algorithm is:
@@ -36,22 +36,22 @@
 
 #define KERN_HIGHBITS	((_PAGE_VALID | _PAGE_SZ4MB) ^ 0xfffff80000000000)
 #define KERN_LOWBITS	(_PAGE_CP | _PAGE_CV | _PAGE_P | _PAGE_W)
-#define KERN_LOWBITS_IO	((_PAGE_E | _PAGE_P | _PAGE_W) ^ KERN_LOWBITS)
+#define KERN_LOWBITS_IO	(_PAGE_E | _PAGE_P | _PAGE_W)
 
 				/* ICACHE line 1 */
   /*0x00*/	ldxa		[%g0] ASI_DMMU, %g1		! Get TAG_TARGET
-  /*0x04*/	srlx		%g1, 8, %g3			! Position PGD offset
-  /*0x08*/	srlx		%g1, 48, %g5			! Shift down CONTEXT bits
-  /*0x0c*/	and		%g3, %g2, %g3			! Mask PGD offset
-  /*0x10*/	sllx		%g1, 2, %g4			! Position PMD offset
-  /*0x14*/	brz,pn		%g5, 3f				! Context 0 == kernel
-  /*0x18*/	 and		%g4, %g2, %g4			! Mask PMD offset
+  /*0x04*/	srlx		%g1, 10, %g3			! Position PGD offset
+  /*0x08*/	andcc		%g1, %g2, %g0			! Test CONTEXT bits
+  /*0x0c*/	and		%g3, 0xffc, %g3			! Mask PGD offset
+  /*0x18*/	and		%g1, 0xffe, %g4			! Mask PMD offset
+  /*0x14*/	be,pn		%xcc, 3f			! Context 0 == kernel
+  /*0x10*/	 add		%g4, %g4, %g4			! Position PMD offset
   /*0x1c*/	ldxa		[%g0] ASI_DMMU_TSB_8KB_PTR, %g1	! For PTE offset
 
 				/* ICACHE line 2 */
-  /*0x20*/	ldxa		[%g7 + %g3] ASI_PHYS_USE_EC, %g5	! Load PGD
+  /*0x20*/	lduwa		[%g7 + %g3] ASI_PHYS_USE_EC, %g5	! Load PGD
   /*0x24*/	srlx		%g1, 1, %g1				! PTE offset
-2:/*0x28*/	ldxa		[%g5 + %g4] ASI_PHYS_USE_EC, %g3	! Load PMD
+2:/*0x28*/	lduwa		[%g5 + %g4] ASI_PHYS_USE_EC, %g3	! Load PMD
   /*0x2c*/	ldxa		[%g3 + %g1] ASI_PHYS_USE_EC, %g5	! Load PTE
   /*0x30*/	brgez,pn	%g5, sparc64_dtlb_refbit_catch		! Valid set?
   /*0x34*/	 nop							! delay
@@ -61,22 +61,22 @@
 3:				/* ICACHE line 3 */
   /*0x40*/	sllx		%g1, 22, %g5			! This is now physical page + PAGE_OFFSET
   /*0x44*/	brgez,pn	%g5, 4f				! If >= 0, then walk down page tables
-  /*0x48*/	 sethi		%uhi(KERN_HIGHBITS), %g1	! Construct PTE ^ PAGE_OFFSET
-  /*0x4c*/	andcc		%g3, 0x400, %g0			! Slick trick...
-  /*0x50*/	sllx		%g1, 32, %g1			! Move high bits up
-  /*0x54*/	or		%g1, (KERN_LOWBITS), %g1	! Assume not IO
-  /*0x58*/	bne,a,pn	%icc, 5f			! Is it an IO page?
-  /*0x5c*/	 xor		%g1, (KERN_LOWBITS_IO), %g1	! Aha, it is IO...
+  /*0x48*/	 or		%g2, (KERN_LOWBITS), %g1	! Construct PTE ^ PAGE_OFFSET
+  /*0x4c*/	andcc		%g3, 0x100, %g0			! Slick trick...
+  /*0x50*/	bne,a,pn	%icc, 5f			! Is it an IO page?
+  /*0x54*/	 or		%g2, (KERN_LOWBITS_IO), %g1	! Aha, it is IO...
+5:/*0x58*/	xor		%g1, %g5, %g1			! Slick trick II...
+  /*0x5c*/	stxa		%g1, [%g0] ASI_DTLB_DATA_IN	! TLB load
 
 				/* ICACHE line 4 */
-5:/*0x60*/	xor		%g1, %g5, %g1			! Slick trick II...
-  /*0x64*/	stxa		%g1, [%g0] ASI_DTLB_DATA_IN	! TLB load
-  /*0x68*/	retry						! Trap return
-4:/*0x6c*/	ldxa		[%g0] ASI_DMMU_TSB_8KB_PTR, %g1	! For PTE offset
-  /*0x70*/	ldxa		[%g6 + %g3] ASI_PHYS_USE_EC, %g5	! Load kern PGD
-  /*0x74*/	ba,pt		%xcc, 2b			! Go back up top
-  /*0x78*/	 srlx		%g1, 1, %g1
-  /*0x7c*/	nop
+  /*0x60*/	retry						! Trap return
+  /*0x64*/	nop
+  /*0x68*/	nop
+  /*0x6c*/	nop
+4:/*0x70*/	ldxa		[%g0] ASI_DMMU_TSB_8KB_PTR, %g1		! For PTE offset
+  /*0x74*/	lduwa		[%g6 + %g3] ASI_PHYS_USE_EC, %g5	! Load kern PGD
+  /*0x78*/	ba,pt		%xcc, 2b				! Go back up top
+  /*0x7c*/	 srlx		%g1, 1, %g1
 
 #undef KERN_HIGHBITS
 #undef KERN_LOWBITS
diff -u --recursive --new-file v2.1.96/linux/arch/sparc64/kernel/dtlb_prot.S linux/arch/sparc64/kernel/dtlb_prot.S
--- v2.1.96/linux/arch/sparc64/kernel/dtlb_prot.S	Sat Aug 16 09:51:08 1997
+++ linux/arch/sparc64/kernel/dtlb_prot.S	Tue Apr 14 17:44:20 1998
@@ -1,9 +1,9 @@
-/* $Id: dtlb_prot.S,v 1.14 1997/08/03 09:07:00 davem Exp $
+/* $Id: dtlb_prot.S,v 1.15 1998/01/14 17:14:46 jj Exp $
  * dtlb_prot.S:	Data TLB protection code, this is included directly
  *              into the trap table.
  *
  * Copyright (C) 1996,1997 David S. Miller (davem@caip.rutgers.edu)
- * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
  */
 
 	/* We know kernel never takes protection trap,
@@ -15,12 +15,12 @@
 
 				/* ICACHE line 1 */
   /*0x00*/	ldxa		[%g0] ASI_DMMU, %g1		! Get TAG_TARGET
-  /*0x04*/	srlx		%g1, 8, %g3			! Position PGD offset
-  /*0x08*/	sllx		%g1, 2, %g4			! Position PMD offset
-  /*0x0c*/	and		%g3, %g2, %g3			! Mask PGD offset
-  /*0x10*/	and		%g4, %g2, %g4			! Mask PMD offset
-  /*0x14*/	ldxa		[%g7 + %g3] ASI_PHYS_USE_EC, %g5	! Load PGD
-  /*0x18*/	ldxa		[%g5 + %g4] ASI_PHYS_USE_EC, %g4	! Load PMD
+  /*0x04*/	srlx		%g1, 10, %g3			! Position PGD offset
+  /*0x08*/	and		%g1, 0xffe, %g4			! Mask PMD offset
+  /*0x0c*/	and		%g3, 0xffc, %g3			! Mask PGD offset
+  /*0x10*/	add		%g4, %g4, %g4			! Position PMD offset
+  /*0x14*/	lduwa		[%g7 + %g3] ASI_PHYS_USE_EC, %g5	! Load PGD
+  /*0x18*/	lduwa		[%g5 + %g4] ASI_PHYS_USE_EC, %g4	! Load PMD
   /*0x1c*/	ldxa		[%g0] ASI_DMMU_TSB_8KB_PTR, %g1		! For PTE offset
 
 				/* ICACHE line 2 */
@@ -34,10 +34,10 @@
   /*0x3c*/	ldxa		[%g5] ASI_DMMU, %g4			! From MMU
 
 				/* ICACHE line 3 */
-  /*0x40*/	add		%g2, 7, %g5				! Compute mask
-  /*0x44*/	andn		%g4, %g5, %g4				! Mask page
-  /*0x48*/	mov		TLB_SFSR, %g5				! read SFSR
-  /*0x4c*/	ldxa		[%g5] ASI_DMMU, %g5			! from DMMU for
+  /*0x40*/	mov		TLB_SFSR, %g5				! read SFSR
+  /*0x44*/	srlx		%g4, 13, %g4				!   Prepare...
+  /*0x48*/	ldxa		[%g5] ASI_DMMU, %g5			! from DMMU for
+  /*0x4c*/	sllx		%g4, 13, %g4				!   ...and mask page
   /*0x50*/	and		%g5, 0x10, %g5				! context bit
   /*0x54*/	or		%g4, %g5, %g4				! for prot trap
 1:/*0x58*/	stxa		%g0, [%g4] ASI_DMMU_DEMAP		! TLB flush page
diff -u --recursive --new-file v2.1.96/linux/arch/sparc64/kernel/ebus.c linux/arch/sparc64/kernel/ebus.c
--- v2.1.96/linux/arch/sparc64/kernel/ebus.c	Mon Jan 12 15:15:44 1998
+++ linux/arch/sparc64/kernel/ebus.c	Tue Apr 14 17:44:20 1998
@@ -1,4 +1,4 @@
-/* $Id: ebus.c,v 1.17 1998/01/10 18:26:13 ecd Exp $
+/* $Id: ebus.c,v 1.23 1998/03/29 16:27:24 ecd Exp $
  * ebus.c: PCI to EBus bridge device.
  *
  * Copyright (C) 1997  Eddie C. Dost  (ecd@skynet.be)
@@ -29,14 +29,12 @@
 struct linux_ebus *ebus_chain = 0;
 
 extern void prom_ebus_ranges_init(struct linux_ebus *);
+extern void prom_ebus_intmap_init(struct linux_ebus *);
 extern unsigned long pci_console_init(unsigned long memory_start);
 
 #ifdef CONFIG_SUN_OPENPROMIO
 extern int openprom_init(void);
 #endif
-#ifdef CONFIG_SUN_MOSTEK_RTC
-extern int rtc_init(void);
-#endif
 #ifdef CONFIG_SPARCAUDIO
 extern int sparcaudio_init(void);
 #endif
@@ -46,6 +44,9 @@
 #ifdef CONFIG_OBP_FLASH
 extern int flash_init(void);
 #endif
+#ifdef CONFIG_ENVCTRL
+extern int envctrl_init(void);
+#endif
 
 extern unsigned int psycho_irq_build(struct linux_pbm_info *pbm,
 				     unsigned int full_ino);
@@ -62,7 +63,35 @@
 	return mem;
 }
 
-__initfunc(void fill_ebus_child(int node, struct linux_ebus_child *dev))
+__initfunc(void ebus_intmap_match(struct linux_ebus *ebus,
+				  struct linux_prom_registers *reg,
+				  int *interrupt))
+{
+	unsigned int hi, lo, irq;
+	int i;
+
+	if (!ebus->num_ebus_intmap)
+		return;
+
+	hi = reg->which_io & ebus->ebus_intmask.phys_hi;
+	lo = reg->phys_addr & ebus->ebus_intmask.phys_lo;
+	irq = *interrupt & ebus->ebus_intmask.interrupt;
+	for (i = 0; i < ebus->num_ebus_intmap; i++) {
+		if ((ebus->ebus_intmap[i].phys_hi == hi) &&
+		    (ebus->ebus_intmap[i].phys_lo == lo) &&
+		    (ebus->ebus_intmap[i].interrupt == irq)) {
+			*interrupt = ebus->ebus_intmap[i].cinterrupt;
+			return;
+		}
+	}
+
+	prom_printf("ebus: IRQ [%08x.%08x.%08x] not found in interrupt-map\n",
+		    reg->which_io, reg->phys_addr, *interrupt);
+	prom_halt();
+}
+
+__initfunc(void fill_ebus_child(int node, struct linux_prom_registers *preg,
+				struct linux_ebus_child *dev))
 {
 	int regs[PROMREG_MAX];
 	int irqs[PROMREG_MAX];
@@ -90,8 +119,10 @@
 		dev->num_irqs = 0;
 	} else {
 		dev->num_irqs = len / sizeof(irqs[0]);
-		for (i = 0; i < dev->num_irqs; i++)
+		for (i = 0; i < dev->num_irqs; i++) {
+			ebus_intmap_match(dev->bus, preg, &irqs[i]);
 			dev->irqs[i] = psycho_irq_build(dev->bus->parent, irqs[i]);
+		}
 	}
 
 #ifdef DEBUG_FILL_EBUS_DEV
@@ -108,7 +139,8 @@
 #endif
 }
 
-__initfunc(unsigned long fill_ebus_device(int node, struct linux_ebus_device *dev,
+__initfunc(unsigned long fill_ebus_device(int node,
+					  struct linux_ebus_device *dev,
 					  unsigned long memory_start))
 {
 	struct linux_prom_registers regs[PROMREG_MAX];
@@ -142,8 +174,10 @@
 		dev->num_irqs = 0;
 	} else {
 		dev->num_irqs = len / sizeof(irqs[0]);
-		for (i = 0; i < dev->num_irqs; i++)
+		for (i = 0; i < dev->num_irqs; i++) {
+			ebus_intmap_match(dev->bus, &regs[0], &irqs[i]);
 			dev->irqs[i] = psycho_irq_build(dev->bus->parent, irqs[i]);
+		}
 	}
 
 #ifdef DEBUG_FILL_EBUS_DEV
@@ -166,7 +200,7 @@
 		child->next = 0;
 		child->parent = dev;
 		child->bus = dev->bus;
-		fill_ebus_child(node, child);
+		fill_ebus_child(node, &regs[0], child);
 
 		while ((node = prom_getsibling(node))) {
 			child->next = (struct linux_ebus_child *)
@@ -176,13 +210,16 @@
 			child->next = 0;
 			child->parent = dev;
 			child->bus = dev->bus;
-			fill_ebus_child(node, child);
+			fill_ebus_child(node, &regs[0], child);
 		}
 	}
 
 	return memory_start;
 }
 
+extern void sun4u_start_timers(void);
+extern void clock_probe(void);
+
 __initfunc(unsigned long ebus_init(unsigned long memory_start,
 				   unsigned long memory_end))
 {
@@ -199,14 +236,10 @@
 	int reg, rng, nreg;
 	int num_ebus = 0;
 
-	if (!pcibios_present())
+	if (!pci_present())
 		return memory_start;
 
-	for (pdev = pci_devices; pdev; pdev = pdev->next) {
-		if ((pdev->vendor == PCI_VENDOR_ID_SUN) &&
-		    (pdev->device == PCI_DEVICE_ID_SUN_EBUS))
-			break;
-	}
+	pdev = pci_find_device(PCI_VENDOR_ID_SUN, PCI_DEVICE_ID_SUN_EBUS, 0);
 	if (!pdev) {
 		printk("ebus: No EBus's found.\n");
 #ifdef PROM_DEBUG
@@ -236,11 +269,9 @@
 		ebus->parent = pbm = cookie->pbm;
 
 		/* Enable BUS Master. */
-		pcibios_read_config_word(pdev->bus->number, pdev->devfn,
-					 PCI_COMMAND, &pci_command);
+		pci_read_config_word(pdev, PCI_COMMAND, &pci_command);
 		pci_command |= PCI_COMMAND_MASTER;
-		pcibios_write_config_word(pdev->bus->number, pdev->devfn,
-					  PCI_COMMAND, pci_command);
+		pci_write_config_word(pdev, PCI_COMMAND, pci_command);
 
 		len = prom_getproperty(ebusnd, "reg", (void *)regs,
 				       sizeof(regs));
@@ -285,6 +316,7 @@
 #endif
 
 		prom_ebus_ranges_init(ebus);
+		prom_ebus_intmap_init(ebus);
 
 		nd = prom_getchild(ebusnd);
 		if (!nd)
@@ -312,11 +344,8 @@
 		}
 
 	next_ebus:
-		for (pdev = pdev->next; pdev; pdev = pdev->next) {
-			if ((pdev->vendor == PCI_VENDOR_ID_SUN) &&
-			    (pdev->device == PCI_DEVICE_ID_SUN_EBUS))
-				break;
-		}
+		pdev = pci_find_device(PCI_VENDOR_ID_SUN,
+				       PCI_DEVICE_ID_SUN_EBUS, pdev);
 		if (!pdev)
 			break;
 
@@ -335,9 +364,6 @@
 #ifdef CONFIG_SUN_OPENPROMIO
 	openprom_init();
 #endif
-#ifdef CONFIG_SUN_MOSTEK_RTC
-	rtc_init();
-#endif
 #ifdef CONFIG_SPARCAUDIO
 	sparcaudio_init();
 #endif
@@ -345,20 +371,15 @@
 	bpp_init();
 #endif
 #ifdef CONFIG_SUN_AUXIO
-	if (sparc_cpu_model == sun4u)
-		auxio_probe();
+	auxio_probe();
+#endif
+#ifdef CONFIG_ENVCTRL
+	envctrl_init();
 #endif
 #ifdef CONFIG_OBP_FLASH
 	flash_init();
 #endif
-#ifdef __sparc_v9__
-	if (sparc_cpu_model == sun4u) {
-		extern void sun4u_start_timers(void);
-		extern void clock_probe(void);
-
-		sun4u_start_timers();
-		clock_probe();
-	}
-#endif
+	sun4u_start_timers();
+	clock_probe();
 	return memory_start;
 }
diff -u --recursive --new-file v2.1.96/linux/arch/sparc64/kernel/head.S linux/arch/sparc64/kernel/head.S
--- v2.1.96/linux/arch/sparc64/kernel/head.S	Sat Aug 16 09:51:08 1997
+++ linux/arch/sparc64/kernel/head.S	Tue Apr 14 17:44:20 1998
@@ -1,9 +1,9 @@
-/* $Id: head.S,v 1.46 1997/08/08 08:33:30 jj Exp $
+/* $Id: head.S,v 1.49 1998/03/03 12:31:17 jj Exp $
  * head.S: Initial boot code for the Sparc64 port of Linux.
  *
  * Copyright (C) 1996,1997 David S. Miller (davem@caip.rutgers.edu)
  * Copyright (C) 1996 David Sitsky (David.Sitsky@anu.edu.au)
- * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
  * Copyright (C) 1997 Miguel de Icaza (miguel@nuclecu.unam.mx)
  */
 
@@ -26,7 +26,7 @@
 /* This section from from _start to sparc64_boot_end should fit into
  * 0x0000.0000.0040.4000 to 0x0000.0000.0040.8000 and will be sharing space
  * with bootup_user_stack, which is from 0x0000.0000.0040.4000 to
- * 0x0000.0000.0040.6000 and bootup_kernel_stack, which is from
+ * 0x0000.0000.0040.6000 and empty_bad_page, which is from
  * 0x0000.0000.0040.6000 to 0x0000.0000.0040.8000. 
  */
 
@@ -326,6 +326,8 @@
 	 nop
 	/* Not reached... */
 
+/* IMPORTANT NOTE: Whenever making changes here, check
+ * trampoline.S as well. -jj */
 	.globl	setup_tba
 setup_tba:
 	save	%sp, -160, %sp
@@ -346,9 +348,11 @@
 	/* Set up MMU globals */
 	wrpr	%o1, (PSTATE_MG|PSTATE_IE), %pstate
 
-	/* PGD/PMD offset mask, used by TLB miss handlers. */
-	sethi	%hi(0x1ff8), %g2
-	or	%g2, %lo(0x1ff8), %g2
+	/* Set KERN_HIGHBITS used by dTLB miss handler. */
+#define KERN_HIGHBITS ((_PAGE_VALID | _PAGE_SZ4MB) ^ 0xfffff80000000000)
+	sethi	%uhi(KERN_HIGHBITS), %g2
+	sllx	%g2, 32, %g2
+#undef KERN_HIGHBITS
 
 	/* Kernel PGDIR used by TLB miss handlers. */
 	mov	%i0, %g6
@@ -391,7 +395,8 @@
 	.skip	0x2000 + _start - sparc64_boot_end
 bootup_user_stack_end:
 
-bootup_kernel_stack:
+	.globl	empty_bad_page
+empty_bad_page:
 	.skip	0x2000
 
 ! 0x0000000000408000
diff -u --recursive --new-file v2.1.96/linux/arch/sparc64/kernel/init_task.c linux/arch/sparc64/kernel/init_task.c
--- v2.1.96/linux/arch/sparc64/kernel/init_task.c	Tue Mar 17 22:18:14 1998
+++ linux/arch/sparc64/kernel/init_task.c	Tue Apr 14 17:44:20 1998
@@ -6,7 +6,7 @@
 
 static struct vm_area_struct init_mmap = INIT_MMAP;
 static struct fs_struct init_fs = INIT_FS;
-static struct files * init_fd_array[NR_OPEN] = { NULL, };
+static struct file * init_fd_array[NR_OPEN] = { NULL, };
 static struct files_struct init_files = INIT_FILES;
 static struct signal_struct init_signals = INIT_SIGNALS;
 struct mm_struct init_mm = INIT_MM;
diff -u --recursive --new-file v2.1.96/linux/arch/sparc64/kernel/ioctl32.c linux/arch/sparc64/kernel/ioctl32.c
--- v2.1.96/linux/arch/sparc64/kernel/ioctl32.c	Mon Jan 12 15:15:44 1998
+++ linux/arch/sparc64/kernel/ioctl32.c	Tue Apr 14 17:44:20 1998
@@ -1,4 +1,4 @@
-/* $Id: ioctl32.c,v 1.26 1997/12/15 15:11:02 jj Exp $
+/* $Id: ioctl32.c,v 1.35 1998/04/10 02:01:46 davem Exp $
  * ioctl32.c: Conversion between 32bit and 64bit native ioctls.
  *
  * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
@@ -23,9 +23,11 @@
 #include <linux/netlink.h>
 #include <linux/vt.h>
 #include <linux/fs.h>
+#include <linux/file.h>
 #include <linux/fd.h>
 #include <linux/if_ppp.h>
 #include <linux/mtio.h>
+#include <linux/cdrom.h>
 
 #include <scsi/scsi.h>
 /* Ugly hack. */
@@ -64,6 +66,30 @@
 	return err;
 }
  
+struct timeval32 {
+	int tv_sec;
+	int tv_usec;
+};
+
+static int do_siocgstamp(unsigned int fd, unsigned int cmd, u32 arg)
+{
+	struct timeval32 *up = (struct timeval32 *)A(arg);
+	struct timeval ktv;
+	mm_segment_t old_fs = get_fs();
+	int err;
+
+	set_fs(KERNEL_DS);
+	err = sys_ioctl(fd, cmd, (unsigned long)&ktv);
+	set_fs(old_fs);
+	if(!err) {
+		if(!access_ok(VERIFY_WRITE, up, sizeof(*up))	||
+		   __put_user(ktv.tv_sec, &up->tv_sec)		||
+		   __put_user(ktv.tv_usec, &up->tv_usec))
+			err = -EFAULT;
+	}
+	return err;
+}
+
 struct ifmap32 {
 	u32 mem_start;
 	u32 mem_end;
@@ -948,6 +974,90 @@
 	return 0;
 }
 
+struct cdrom_read32 {
+	int			cdread_lba;
+	__kernel_caddr_t32	cdread_bufaddr;
+	int			cdread_buflen;
+};
+
+struct cdrom_read_audio32 {
+	union cdrom_addr	addr;
+	u_char			addr_format;
+	int			nframes;
+	__kernel_caddr_t32	buf;
+};
+
+static int cdrom_ioctl_trans(unsigned int fd, unsigned int cmd, u32 arg)
+{
+	mm_segment_t old_fs = get_fs();
+	struct cdrom_read cdread;
+	struct cdrom_read_audio cdreadaudio;
+	__kernel_caddr_t32 addr;
+	char *data = 0;
+	void *karg;
+	int err = 0;
+
+	switch(cmd) {
+	case CDROMREADMODE2:
+	case CDROMREADMODE1:
+	case CDROMREADRAW:
+	case CDROMREADCOOKED:
+		karg = &cdread;
+		if (__get_user(cdread.cdread_lba, &((struct cdrom_read32 *)A(arg))->cdread_lba) ||
+		    __get_user(addr, &((struct cdrom_read32 *)A(arg))->cdread_bufaddr) ||
+		    __get_user(cdread.cdread_buflen, &((struct cdrom_read32 *)A(arg))->cdread_buflen))
+			return -EFAULT;
+		data = kmalloc(cdread.cdread_buflen, GFP_KERNEL);
+		if (!data)
+			return -ENOMEM;
+		cdread.cdread_bufaddr = data;
+		break;
+	case CDROMREADAUDIO:
+		karg = &cdreadaudio;
+		if (copy_from_user(&cdreadaudio.addr, &((struct cdrom_read_audio32 *)A(arg))->addr, sizeof(cdreadaudio.addr)) ||
+		    __get_user(cdreadaudio.addr_format, &((struct cdrom_read_audio32 *)A(arg))->addr_format) ||
+		    __get_user(cdreadaudio.nframes, &((struct cdrom_read_audio32 *)A(arg))->nframes) || 
+		    __get_user(addr, &((struct cdrom_read_audio32 *)A(arg))->buf))
+			return -EFAULT;
+		data = kmalloc(cdreadaudio.nframes * 2352, GFP_KERNEL);
+		if (!data)
+			return -ENOMEM;
+		cdreadaudio.buf = data;
+		break;
+	default:
+		printk("cdrom_ioctl: Unknown cmd fd(%d) cmd(%08x) arg(%08x)\n",
+		       (int)fd, (unsigned int)cmd, (unsigned int)arg);
+		return -EINVAL;
+	}
+	set_fs (KERNEL_DS);
+	err = sys_ioctl (fd, cmd, (unsigned long)karg);
+	set_fs (old_fs);
+	if (err) {
+		if (data) kfree(data);
+		return err;
+	}
+	switch (cmd) {
+	case CDROMREADMODE2:
+	case CDROMREADMODE1:
+	case CDROMREADRAW:
+	case CDROMREADCOOKED:
+		if (copy_to_user((char *)A(addr), data, cdread.cdread_buflen)) {
+			kfree(data);
+			return -EFAULT;
+		}
+		break;
+	case CDROMREADAUDIO:
+		if (copy_to_user((char *)A(addr), data, cdreadaudio.nframes * 2352)) {
+			kfree(data);
+			return -EFAULT;
+		}
+		break;
+	default:
+		break;
+	}
+	if (data) kfree(data);
+	return 0;
+}
 
 asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd, u32 arg)
 {
@@ -955,10 +1065,7 @@
 	int error = -EBADF;
 
 	lock_kernel();
-	if(fd >= NR_OPEN)
-		goto out;
-
-	filp = current->files->fd[fd];
+	filp = fcheck(fd);
 	if(!filp)
 		goto out;
 
@@ -966,7 +1073,6 @@
 		error = sys_ioctl (fd, cmd, (unsigned long)arg);
 		goto out;
 	}
-	error = -EFAULT;
 	switch (cmd) {
 	case SIOCGIFCONF:
 		error = dev_ifconf(fd, arg);
@@ -1014,6 +1120,11 @@
 		error = -EINVAL;
 		goto out;
 
+	case SIOCGSTAMP:
+		/* Sorry, timeval in the kernel is different now. */
+		error = do_siocgstamp(fd, cmd, arg);
+		goto out;
+
 	case HDIO_GETGEO:
 		error = hdio_getgeo(fd, arg);
 		goto out;
@@ -1066,6 +1177,15 @@
 		error = mt_ioctl_trans(fd, cmd, arg);
 		goto out;
 
+	case CDROMREADMODE2:
+	case CDROMREADMODE1:
+	case CDROMREADRAW:
+	case CDROMREADCOOKED:
+	case CDROMREADAUDIO:
+	case CDROMREADALL:
+		error = cdrom_ioctl_trans(fd, cmd, arg);
+		goto out;
+
 	/* List here exlicitly which ioctl's are known to have
 	 * compatable types passed or none at all...
 	 */
@@ -1170,6 +1290,7 @@
 	
 	/* 0x09 */
 	case REGISTER_DEV:
+	case REGISTER_DEV_NEW:
 	case START_MD:
 	case STOP_MD:
 	
@@ -1219,6 +1340,7 @@
 	case SCSI_IOCTL_TAGGED_ENABLE:
 	case SCSI_IOCTL_TAGGED_DISABLE:
 	case SCSI_IOCTL_GET_BUS_NUMBER:
+	case SCSI_IOCTL_SEND_COMMAND:
 	
 	/* Big V */
 	case VT_SETMODE:
@@ -1267,7 +1389,6 @@
 	case FIOGETOWN:
 	case SIOCGPGRP:
 	case SIOCATMARK:
-	case SIOCGSTAMP:
 	case SIOCSIFLINK:
 	case SIOCSIFENCAP:
 	case SIOCGIFENCAP:
@@ -1305,6 +1426,36 @@
 	case PPPIOCSNPMODE:
 	case PPPIOCGDEBUG:
 	case PPPIOCSDEBUG:
+
+	/* CDROM stuff */
+	case CDROMPAUSE:
+	case CDROMRESUME:
+	case CDROMPLAYMSF:
+	case CDROMPLAYTRKIND:
+	case CDROMREADTOCHDR:
+	case CDROMREADTOCENTRY:
+	case CDROMSTOP:
+	case CDROMSTART:
+	case CDROMEJECT:
+	case CDROMVOLCTRL:
+	case CDROMSUBCHNL:
+	case CDROMEJECT_SW:
+	case CDROMMULTISESSION:
+	case CDROM_GET_MCN:
+	case CDROMRESET:
+	case CDROMVOLREAD:
+	case CDROMSEEK:
+	case CDROMPLAYBLK:
+	case CDROMCLOSETRAY:
+	case CDROM_SET_OPTIONS:
+	case CDROM_CLEAR_OPTIONS:
+	case CDROM_SELECT_SPEED:
+	case CDROM_SELECT_DISC:
+	case CDROM_MEDIA_CHANGED:
+	case CDROM_DRIVE_STATUS:
+	case CDROM_DISC_STATUS:
+	case CDROM_CHANGER_NSLOTS:
+
 		error = sys_ioctl (fd, cmd, (unsigned long)arg);
 		goto out;
 
@@ -1312,7 +1463,6 @@
 		printk("sys32_ioctl: Unknown cmd fd(%d) cmd(%08x) arg(%08x)\n",
 		       (int)fd, (unsigned int)cmd, (unsigned int)arg);
 		error = -EINVAL;
-		goto out;
 		break;
 	}
 out:
diff -u --recursive --new-file v2.1.96/linux/arch/sparc64/kernel/irq.c linux/arch/sparc64/kernel/irq.c
--- v2.1.96/linux/arch/sparc64/kernel/irq.c	Mon Jan 12 15:15:44 1998
+++ linux/arch/sparc64/kernel/irq.c	Tue Apr 14 17:44:20 1998
@@ -1,7 +1,8 @@
-/* $Id: irq.c,v 1.47 1998/01/10 18:26:17 ecd Exp $
+/* $Id: irq.c,v 1.52 1998/03/19 00:22:54 ecd Exp $
  * irq.c: UltraSparc IRQ handling/init/registry.
  *
- * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1997  David S. Miller  (davem@caip.rutgers.edu)
+ * Copyright (C) 1998  Eddie C. Dost    (ecd@skynet.be)
  */
 
 #include <linux/config.h>
@@ -98,12 +99,22 @@
 {
 	int i, len = 0;
 	struct irqaction *action;
+#ifdef __SMP__
+	int j;
+#endif
 
 	for(i = 0; i < (NR_IRQS + 1); i++) {
 		if(!(action = *(i + irq_action)))
 			continue;
-		len += sprintf(buf + len, "%2d: %8d %c %s",
-			       i, kstat.interrupts[i],
+		len += sprintf(buf + len, "%3d: ", i);
+#ifndef __SMP__
+		len += sprintf(buf + len, "%10u ", kstat_irqs(i));
+#else
+		for (j = 0; j < smp_num_cpus; j++)
+			len += sprintf(buf + len, "%10u ",
+				kstat.irqs[cpu_logical_map(j)][i]);
+#endif
+		len += sprintf(buf + len, "%c %s",
 			       (action->flags & SA_INTERRUPT) ? '+' : ' ',
 			       action->name);
 		for(action = action->next; action; action = action->next) {
@@ -113,19 +124,6 @@
 		}
 		len += sprintf(buf + len, "\n");
 	}
-#if 0
-#ifdef CONFIG_PCI
-	{
-		struct linux_psycho *p;
-		for (p = psycho_root; p; p = p->next)
-			len += sprintf(buf + len,
-				       "ISTAT[%d]: PCI[%016lx] OBIO[%016lx]\n",
-				       p->index,
-				       p->psycho_regs->pci_istate,
-				       p->psycho_regs->obio_istate);
-	}
-#endif
-#endif
 	return len;
 }
 
@@ -197,8 +195,7 @@
 	unsigned long offset;
 	struct sysio_regs *sregs;
 
-	if((irq == 14) ||
-	   (irq >= NUM_SYSIO_OFFSETS) ||
+	if((irq >= NUM_SYSIO_OFFSETS) ||
 	   ((offset = sysio_irq_offsets[irq]) == ((unsigned long)-1)))
 		return NULL;
 	sregs = SBus_chain->iommu->sysio_regs;
@@ -224,8 +221,8 @@
 unsigned char psycho_ino_to_pil[] = {
 	7, 5, 5, 2,			/* PCI A slot 0  Int A, B, C, D */
 	7, 5, 5, 2,			/* PCI A slot 1  Int A, B, C, D */
-	0, 0, 0, 0,
-	0, 0, 0, 0,
+	7, 5, 5, 2,			/* PCI A slot 2  Int A, B, C, D */
+	7, 5, 5, 2,			/* PCI A slot 3  Int A, B, C, D */
 	6, 4, 3, 1,			/* PCI B slot 0  Int A, B, C, D */
 	6, 4, 3, 1,			/* PCI B slot 1  Int A, B, C, D */
 	6, 4, 3, 1,			/* PCI B slot 2  Int A, B, C, D */
@@ -255,13 +252,13 @@
  */
 #define psycho_offset(x) ((unsigned long)(&(((struct psycho_regs *)0)->x)))
 
-#define psycho_imap_offset(ino)							\
-	((ino & 0x20) ? (psycho_offset(imap_scsi) + (((ino) & 0x1f) << 3)) :	\
+#define psycho_imap_offset(ino)						      \
+	((ino & 0x20) ? (psycho_offset(imap_scsi) + (((ino) & 0x1f) << 3)) :  \
 			(psycho_offset(imap_a_slot0) + (((ino) & 0x3c) << 1)))
 
-#define psycho_iclr_offset(ino)							\
-	((ino & 0x20) ? (psycho_offset(iclr_scsi) + (((ino) & 0x1f) << 3)) :	\
-			(psycho_offset(iclr_a_slot0[0]) + (((ino) & 0x1f) << 3)))
+#define psycho_iclr_offset(ino)						      \
+	((ino & 0x20) ? (psycho_offset(iclr_scsi) + (((ino) & 0x1f) << 3)) :  \
+			(psycho_offset(iclr_a_slot0[0]) + (((ino) & 0x1f)<<3)))
 
 #endif
 
@@ -529,7 +526,7 @@
 	unsigned long flags;
 	unsigned int *imap, *iclr;
 	void *bus_id = NULL;
-	int ivindex, ivindex_fixup, cpu_irq = -1, pending;
+	int ivindex = -1, ivindex_fixup, cpu_irq = -1, pending = 0;
 	
 	if(!handler)
 	    return -EINVAL;
@@ -537,43 +534,47 @@
 	imap = iclr = NULL;
 
 	ivindex_fixup = 0;
+
+	if (irq == 0) {
+		cpu_irq = irq;
+		irqflags &= ~(SA_IMAP_MASKED);
+	} else {
+		irqflags |= SA_IMAP_MASKED;
 #ifdef CONFIG_PCI
-	if(PCI_IRQ_P(irq)) {
-		pci_irq_frobnicate(&cpu_irq, &ivindex_fixup, &imap, &iclr, irq);
-	} else
+		if(PCI_IRQ_P(irq)) {
+			pci_irq_frobnicate(&cpu_irq, &ivindex_fixup, &imap, &iclr, irq);
+		} else
 #endif
-	if(irqflags & SA_DCOOKIE) {
-		if(!dev_id) {
-			printk("request_irq: SA_DCOOKIE but dev_id is NULL!\n");
-			panic("Bogus irq registry.");
-		}
-		dcookie		= dev_id;
-		dev_id		= dcookie->real_dev_id;
-		cpu_irq		= dcookie->pil;
-		imap		= dcookie->imap;
-		iclr		= dcookie->iclr;
-		bus_id		= dcookie->bus_cookie;
-		get_irq_translations(&cpu_irq, &ivindex_fixup, &imap,
-				     &iclr, bus_id, irqflags, irq);
-	} else {
-		/* XXX NOTE: This code is maintained for compatability until I can
-		 * XXX       verify that all drivers sparc64 will use are updated
-		 * XXX       to use the new IRQ registry dcookie interface.  -DaveM
-		 */
-		if(irq == 14)
-			cpu_irq = irq;
-		else
+		if(irqflags & SA_DCOOKIE) {
+			if(!dev_id) {
+				printk("request_irq: SA_DCOOKIE but dev_id is NULL!\n");
+				panic("Bogus irq registry.");
+			}
+			dcookie		= dev_id;
+			dev_id		= dcookie->real_dev_id;
+			cpu_irq		= dcookie->pil;
+			imap		= dcookie->imap;
+			iclr		= dcookie->iclr;
+			bus_id		= dcookie->bus_cookie;
+			get_irq_translations(&cpu_irq, &ivindex_fixup, &imap,
+					     &iclr, bus_id, irqflags, irq);
+		} else {
+			/* XXX NOTE: This code is maintained for compatability until I can
+			 * XXX       verify that all drivers sparc64 will use are updated
+			 * XXX       to use the new IRQ registry dcookie interface.  -DaveM
+			 */
 			cpu_irq = sysio_ino_to_pil[irq];
-		imap = sysio_irq_to_imap(irq);
-		if(!imap) {
-			printk("request_irq: BAD, null imap for old style "
-			       "irq registry IRQ[%x].\n", irq);
-			panic("Bad IRQ registery...");
+			imap = sysio_irq_to_imap(irq);
+			if(!imap) {
+				printk("request_irq: BAD, null imap for old style "
+				       "irq registry IRQ[%x].\n", irq);
+				panic("Bad IRQ registery...");
+			}
+			iclr = sysio_imap_to_iclr(imap);
 		}
-		iclr = sysio_imap_to_iclr(imap);
+		ivindex = (*imap & (SYSIO_IMAP_IGN | SYSIO_IMAP_INO));
+		ivindex += ivindex_fixup;
 	}
-	ivindex = (*imap & (SYSIO_IMAP_IGN | SYSIO_IMAP_INO));
-	ivindex += ivindex_fixup;
 
 	action = *(cpu_irq + irq_action);
 	if(action) {
@@ -612,26 +613,28 @@
 		return -ENOMEM;
 	}
 
-	bucket = add_ino_hash(ivindex, imap, iclr, irqflags);
-	if(!bucket) {
-		kfree(action);
-		restore_flags(flags);
-		return -ENOMEM;
-	}
-
-	pending = ((ivector_to_mask[ivindex] & 0x80000000) != 0);
-	ivector_to_mask[ivindex] = (1 << cpu_irq);
-	if(pending)
-		ivector_to_mask[ivindex] |= 0x80000000;
+	if (irqflags & SA_IMAP_MASKED) {
+		bucket = add_ino_hash(ivindex, imap, iclr, irqflags);
+		if(!bucket) {
+			kfree(action);
+			restore_flags(flags);
+			return -ENOMEM;
+		}
 
-	if(dcookie) {
-		dcookie->ret_ino = ivindex;
-		dcookie->ret_pil = cpu_irq;
+		pending = ((ivector_to_mask[ivindex] & 0x80000000) != 0);
+		ivector_to_mask[ivindex] = (1 << cpu_irq);
+		if(pending)
+			ivector_to_mask[ivindex] |= 0x80000000;
+
+		if(dcookie) {
+			dcookie->ret_ino = ivindex;
+			dcookie->ret_pil = cpu_irq;
+		}
 	}
 
 	action->mask = (unsigned long) bucket;
 	action->handler = handler;
-	action->flags = irqflags | SA_IMAP_MASKED;
+	action->flags = irqflags;
 	action->name = name;
 	action->next = NULL;
 	action->dev_id = dev_id;
@@ -664,7 +667,7 @@
 	unsigned int cpu_irq;
 	int ivindex = -1;
 
-	if(irq == 14) {
+	if(irq == 0) {
 		cpu_irq = irq;
 	} else {
 #ifdef CONFIG_PCI
@@ -951,34 +954,43 @@
 void handler_irq(int irq, struct pt_regs *regs)
 {
 	struct ino_bucket *bucket = NULL;
-	struct irqaction *action;
+	struct irqaction *action, *act;
 	int cpu = smp_processor_id();
 
+#ifndef __SMP__
+	/*
+	 * Check for TICK_INT on level 14 softint.
+	 */
+	if ((irq == 14) && get_softint() & (1UL << 0))
+		irq = 0;
+#endif
 	clear_softint(1 << irq);
 
 	irq_enter(cpu, irq);
 	action = *(irq + irq_action);
-	kstat.interrupts[irq]++;
+	kstat.irqs[cpu][irq]++;
 	if(!action) {
 		unexpected_irq(irq, 0, regs);
 	} else {
+		act = action;
 		do {
-			unsigned long *swmask = NULL;
-
-			if(action->flags & SA_IMAP_MASKED) {
-				bucket = (struct ino_bucket *)action->mask;
-
-				swmask = &ivector_to_mask[bucket->ino];
-				if(!(*swmask & 0x80000000))
+			if(act->flags & SA_IMAP_MASKED) {
+				bucket = (struct ino_bucket *)act->mask;
+				if(!(ivector_to_mask[bucket->ino] & 0x80000000))
 					continue;
 			}
-
-			action->handler(irq, action->dev_id, regs);
-			if(swmask) {
-				*swmask &= ~(0x80000000);
+			act->handler(irq, act->dev_id, regs);
+		} while((act = act->next) != NULL);
+		act = action;
+		do {
+			if(act->flags & SA_IMAP_MASKED) {
+				bucket = (struct ino_bucket *)act->mask;
+				if(!(ivector_to_mask[bucket->ino] & 0x80000000))
+					continue;
+				ivector_to_mask[bucket->ino] &= ~(0x80000000);
 				*(bucket->iclr) = SYSIO_ICLR_IDLE;
 			}
-		} while((action = action->next) != NULL);
+		} while((act = act->next) != NULL);
 	}
 	irq_exit(cpu, irq);
 }
@@ -993,6 +1005,7 @@
 	int cpu = smp_processor_id();
 
 	irq_enter(cpu, irq);
+	kstat.irqs[cpu][irq]++;
 	bucket = (struct ino_bucket *)action->mask;
 	floppy_interrupt(irq, dev_cookie, regs);
 	ivector_to_mask[bucket->ino] &= ~(0x80000000);
@@ -1036,13 +1049,19 @@
 	unsigned long flags;
 	unsigned int *imap, *iclr;
 	void *bus_id = NULL;
-	int ivindex, ivindex_fixup, cpu_irq = -1;
+	int ivindex = -1, ivindex_fixup, cpu_irq = -1;
 
 	if(!handler)
 		return -EINVAL;
 
 	imap = iclr = NULL;
 	ivindex_fixup = 0;
+
+	if ((irq == 0) || (irq == 14)) {
+		printk("request_fast_irq: Trying to register shared IRQ 0 or 14.\n");
+		return -EBUSY;
+	}
+
 #ifdef CONFIG_PCI
 	if(PCI_IRQ_P(irq)) {
 		pci_irq_frobnicate(&cpu_irq, &ivindex_fixup, &imap, &iclr, irq);
@@ -1066,10 +1085,7 @@
 		 * XXX       verify that all drivers sparc64 will use are updated
 		 * XXX       to use the new IRQ registry dcookie interface.  -DaveM
 		 */
-		if(irq == 14)
-			cpu_irq = irq;
-		else
-			cpu_irq = sysio_ino_to_pil[irq];
+		cpu_irq = sysio_ino_to_pil[irq];
 		imap = sysio_irq_to_imap(irq);
 		if(!imap) {
 			printk("request_irq: BAD, null imap for old style "
@@ -1153,85 +1169,100 @@
 	return 0;
 }
 
-struct sun5_timer *linux_timers = NULL;
-
-/* This is gets the master level10 timer going. */
-void init_timers(void (*cfunc)(int, void *, struct pt_regs *))
+/* This is gets the master TICK_INT timer going. */
+void init_timers(void (*cfunc)(int, void *, struct pt_regs *),
+		 unsigned long *clock)
 {
-	struct linux_prom64_registers pregs[3];
-	struct devid_cookie dcookie;
-	unsigned int *imap, *iclr;
-	u32 pirqs[2];
+	unsigned long flags;
+	unsigned long timer_tick_offset;
 	int node, err;
 
-	node = prom_finddevice("/counter-timer");
-	if(node == 0 || node == -1) {
-		prom_printf("init_timers: Cannot find counter-timer PROM node.\n");
-		prom_halt();
-	}
-	err = prom_getproperty(node, "reg", (char *)&pregs[0], sizeof(pregs));
-	if(err == -1) {
-		prom_printf("init_timers: Cannot obtain 'reg' for counter-timer.\n");
-		prom_halt();
-	}
-	err = prom_getproperty(node, "interrupts", (char *)&pirqs[0], sizeof(pirqs));
-	if(err == -1) {
-		prom_printf("init_timers: Cannot obtain 'interrupts' "
-			    "for counter-timer.\n");
-		prom_halt();
-	}
-	linux_timers = (struct sun5_timer *) __va(pregs[0].phys_addr);
-	iclr = (((unsigned int *)__va(pregs[1].phys_addr))+1);
-	imap = (((unsigned int *)__va(pregs[2].phys_addr))+1);
-
-	/* Shut it up first. */
-	linux_timers->limit0 = 0;
+	node = linux_cpus[0].prom_node;
+	*clock = prom_getint(node, "clock-frequency");
+	timer_tick_offset = *clock / HZ;
 
 	/* Register IRQ handler. */
-	dcookie.real_dev_id = NULL;
-	dcookie.imap = imap;
-	dcookie.iclr = iclr;
-	dcookie.pil = 10;
-	dcookie.bus_cookie = NULL;
-
-	err = request_irq(pirqs[0], cfunc,
-			  (SA_DCOOKIE | SA_INTERRUPT | SA_STATIC_ALLOC),
-			  "timer", &dcookie);
+	err = request_irq(0, cfunc, (SA_INTERRUPT | SA_STATIC_ALLOC),
+			  "timer", NULL);
 
 	if(err) {
-		prom_printf("Serious problem, cannot register timer interrupt\n");
+		prom_printf("Serious problem, cannot register TICK_INT\n");
 		prom_halt();
-	} else {
-		unsigned long flags;
+	}
 
-		save_and_cli(flags);
+	save_and_cli(flags);
 
-		/* Set things up so user can access tick register for profiling
-		 * purposes.
-		 */
-		__asm__ __volatile__("
-	sethi	%%hi(0x80000000), %%g1
-	sllx	%%g1, 32, %%g1
-	rd	%%tick, %%g2
-	add	%%g2, 6, %%g2
-	andn	%%g2, %%g1, %%g2
-	wrpr	%%g2, 0, %%tick
-"		: /* no outputs */
-		: /* no inputs */
-		: "g1", "g2");
-
-		linux_timers->limit0 =
-			(SUN5_LIMIT_ENABLE | SUN5_LIMIT_ZRESTART | SUN5_LIMIT_TOZERO |
-			 (SUN5_HZ_TO_LIMIT(HZ) & SUN5_LIMIT_CMASK));
+	/* Set things up so user can access tick register for profiling
+	 * purposes.
+	 */
+	__asm__ __volatile__("
+		sethi	%%hi(0x80000000), %%g1
+		sllx	%%g1, 32, %%g1
+		rd	%%tick, %%g2
+		add	%%g2, 6, %%g2
+		andn	%%g2, %%g1, %%g2
+		wrpr	%%g2, 0, %%tick
+"	: /* no outputs */
+	: /* no inputs */
+	: "g1", "g2");
 
-		restore_flags(flags);
-	}
+	__asm__ __volatile__("
+		rd	%%tick, %%g1
+		add	%%g1, %0, %%g1
+		wr	%%g1, 0x0, %%tick_cmpr"
+	: /* no outputs */
+	: "r" (timer_tick_offset)
+	: "g1");
 
+	restore_flags(flags);
 	sti();
 }
 
-struct sun5_timer *prom_timers;
+#ifdef __SMP__
+/* Called from smp_commence, when we know how many cpus are in the system
+ * and can have device IRQ's directed at them.
+ */
+void distribute_irqs(void)
+{
+	unsigned long flags;
+	int cpu, level;
+
+	printk("SMP: redistributing interrupts...\n");
+	save_and_cli(flags);
+	cpu = 0;
+	for(level = 0; level < NR_IRQS; level++) {
+		struct irqaction *p = irq_action[level];
+
+		while(p) {
+			if(p->flags & SA_IMAP_MASKED) {
+				struct ino_bucket *bucket = (struct ino_bucket *)p->mask;
+				unsigned int *imap = bucket->imap;
+				unsigned int val;
+				unsigned long tid = __cpu_logical_map[cpu] << 9;
+
+				val = *imap;
+				*imap = SYSIO_IMAP_VALID | (tid & SYSIO_IMAP_TID);
 
+				printk("SMP: Redirecting IGN[%x] INO[%x] "
+				       "to cpu %d [%s]\n",
+				       (val & SYSIO_IMAP_IGN) >> 6,
+				       (val & SYSIO_IMAP_INO), cpu,
+				       p->name);
+
+				cpu++;
+				if (cpu >= NR_CPUS || __cpu_logical_map[cpu] == -1)
+					cpu = 0;
+			}
+			p = p->next;
+		}
+	}
+	restore_flags(flags);
+	irqs_have_been_distributed = 1;
+}
+#endif
+
+
+struct sun5_timer *prom_timers;
 static u64 prom_limit0, prom_limit1;
 
 static void map_prom_timers(void)
@@ -1245,9 +1276,8 @@
 	/* Assume if node is not present, PROM uses different tick mechanism
 	 * which we should not care about.
 	 */
-	if(tnode == 0) {
+	if(tnode == 0 || tnode == -1) {
 		prom_timers = (struct sun5_timer *) 0;
-		prom_printf("AIEEE, no timers\n");
 		return;
 	}
 
@@ -1299,52 +1329,6 @@
 	prom_timers->limit0 = prom_limit0;
 	prom_timers->count0 = 0;
 }
-
-#ifdef __SMP__
-/* Called from smp_commence, when we know how many cpus are in the system
- * and can have device IRQ's directed at them.
- */
-void distribute_irqs(void)
-{
-	unsigned long flags;
-	int cpu, level;
-
-	printk("SMP: redistributing interrupts...\n");
-	save_and_cli(flags);
-	cpu = 0;
-	for(level = 0; level < NR_IRQS; level++) {
-		struct irqaction *p = irq_action[level];
-
-		while(p) {
-			if(p->flags & SA_IMAP_MASKED) {
-				struct ino_bucket *bucket = (struct ino_bucket *)p->mask;
-				unsigned int *imap = bucket->imap;
-				unsigned int val;
-				unsigned long tid = linux_cpus[cpu].mid << 9;
-
-				val = *imap;
-				*imap = SYSIO_IMAP_VALID | (tid & SYSIO_IMAP_TID);
-
-				printk("SMP: Redirecting IGN[%x] INO[%x] "
-				       "to cpu %d [%s]\n",
-				       (val & SYSIO_IMAP_IGN) >> 6,
-				       (val & SYSIO_IMAP_INO), cpu,
-				       p->name);
-
-				cpu += 1;
-				while(!(cpu_present_map & (1UL << cpu))) {
-					cpu += 1;
-					if(cpu >= smp_num_cpus)
-						cpu = 0;
-				}
-			}
-			p = p->next;
-		}
-	}
-	restore_flags(flags);
-	irqs_have_been_distributed = 1;
-}
-#endif
 
 __initfunc(void init_IRQ(void))
 {
diff -u --recursive --new-file v2.1.96/linux/arch/sparc64/kernel/itlb_miss.S linux/arch/sparc64/kernel/itlb_miss.S
--- v2.1.96/linux/arch/sparc64/kernel/itlb_miss.S	Mon Jan 12 15:15:44 1998
+++ linux/arch/sparc64/kernel/itlb_miss.S	Tue Apr 14 17:44:20 1998
@@ -1,42 +1,42 @@
-/* $Id: itlb_miss.S,v 1.11 1997/10/14 01:48:25 davem Exp $
+/* $Id: itlb_miss.S,v 1.12 1998/01/14 17:14:47 jj Exp $
  * itlb_miss.S:	Instruction TLB miss code, this is included directly
  *              into the trap table.
  *
  * Copyright (C) 1996,1997 David S. Miller (davem@caip.rutgers.edu)
- * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
  */
 
 /* Gratuitous comment. */
 
 				/* ICACHE line 1 */
   /*0x00*/	ldxa		[%g0] ASI_IMMU, %g1		! Get TAG_TARGET
-  /*0x04*/	srlx		%g1, 8, %g3			! Position PGD offset
-  /*0x08*/	srlx		%g1, 48, %g5			! Shift down CONTEXT bits
-  /*0x0c*/	and		%g3, %g2, %g3			! Mask PGD offset
-  /*0x10*/	sllx		%g1, 2, %g4			! Position PMD offset
+  /*0x04*/	srlx		%g1, 10, %g3			! Position PGD offset
+  /*0x08*/	andcc		%g1, %g2, %g0			! Test CONTEXT bits
+  /*0x0c*/	and		%g3, 0xffc, %g3			! Mask PGD offset
+  /*0x10*/	and		%g1, 0xffe, %g4			! Mask PMD offset
   /*0x14*/	ldxa		[%g0] ASI_IMMU_TSB_8KB_PTR, %g1	! For PTE offset
-  /*0x18*/	brz,pn		%g5, 3f				! Context 0 == kernel
-  /*0x1c*/	 and		%g4, %g2, %g4			! Mask PMD offset
+  /*0x18*/	be,pn		%xcc, 3f			! Context 0 == kernel
+  /*0x1c*/	 add		%g4, %g4, %g4			! Position PMD offset
 
 				/* ICACHE line 2 */
-  /*0x20*/	ldxa		[%g7 + %g3] ASI_PHYS_USE_EC, %g5	! Load user PGD
+  /*0x20*/	lduwa		[%g7 + %g3] ASI_PHYS_USE_EC, %g5	! Load user PGD
   /*0x24*/	srlx		%g1, 1, %g1				! PTE offset
-  /*0x28*/	ldxa		[%g5 + %g4] ASI_PHYS_USE_EC, %g3	! Load PMD
-2:/*0x2c*/	ldxa		[%g3 + %g1] ASI_PHYS_USE_EC, %g5	! Load PTE
+  /*0x28*/	lduwa		[%g5 + %g4] ASI_PHYS_USE_EC, %g3	! Load PMD
+  /*0x2c*/	ldxa		[%g3 + %g1] ASI_PHYS_USE_EC, %g5	! Load PTE
   /*0x30*/	brgez,pn	%g5, sparc64_itlb_refbit_catch		! Valid set?
   /*0x34*/	 nop							! delay
   /*0x38*/	stxa		%g5, [%g0] ASI_ITLB_DATA_IN		! TLB load
   /*0x3c*/	retry							! Trap return
 
 3:				/* ICACHE line 3 */
-  /*0x40*/	ldxa		[%g6 + %g3] ASI_PHYS_USE_EC, %g5	! Load kern PGD
+  /*0x40*/	lduwa		[%g6 + %g3] ASI_PHYS_USE_EC, %g5	! Load kern PGD
   /*0x44*/	srlx		%g1, 1, %g1				! PTE offset
-  /*0x48*/	ba,pt		%xcc, 2b				! Continue above
-  /*0x4c*/	 ldxa		[%g5 + %g4] ASI_PHYS_USE_EC, %g3	! Load PMD
-  /*0x50*/	nop
-  /*0x54*/	nop
-  /*0x58*/	nop
-  /*0x5c*/	nop
+  /*0x48*/	lduwa		[%g5 + %g4] ASI_PHYS_USE_EC, %g3	! Load PMD
+  /*0x4c*/	ldxa		[%g3 + %g1] ASI_PHYS_USE_EC, %g5	! Load PTE
+  /*0x50*/	brgez,pn	%g5, sparc64_itlb_refbit_catch		! Valid set?
+  /*0x54*/	 nop							! delay
+  /*0x58*/	stxa		%g5, [%g0] ASI_ITLB_DATA_IN		! TLB load
+  /*0x5c*/	retry							! Trap return
 
 				/* ICACHE line 4 */
   /*0x60*/	nop
diff -u --recursive --new-file v2.1.96/linux/arch/sparc64/kernel/process.c linux/arch/sparc64/kernel/process.c
--- v2.1.96/linux/arch/sparc64/kernel/process.c	Mon Jan 12 15:15:44 1998
+++ linux/arch/sparc64/kernel/process.c	Tue Apr 14 17:44:20 1998
@@ -1,4 +1,4 @@
-/*  $Id: process.c,v 1.50 1998/01/09 16:39:33 jj Exp $
+/*  $Id: process.c,v 1.52 1998/03/29 12:57:53 ecd Exp $
  *  arch/sparc64/kernel/process.c
  *
  *  Copyright (C) 1995, 1996 David S. Miller (davem@caip.rutgers.edu)
@@ -41,9 +41,6 @@
 
 /* #define VERBOSE_SHOWREGS */
 
-#define PGTCACHE_HIGH_WATER		50
-#define PGTCACHE_LOW_WATER		25
-
 #ifndef __SMP__
 
 /*
@@ -58,16 +55,7 @@
 	current->priority = -100;
 	current->counter = -100;
 	for (;;) {
-		if(pgtable_cache_size > PGTCACHE_LOW_WATER) {
-			do {
-				if(pgd_quicklist)
-					free_page((unsigned long) get_pgd_fast());
-				if(pmd_quicklist)
-					free_page((unsigned long) get_pmd_fast());
-				if(pte_quicklist)
-					free_page((unsigned long) get_pte_fast());
-			} while(pgtable_cache_size > PGTCACHE_HIGH_WATER);
-		}
+		check_pgt_cache();
 		run_task_queue(&tq_scheduler);
 		schedule();
 	}
@@ -83,16 +71,7 @@
 {
 	current->priority = -100;
 	while(1) {
-		if(pgtable_cache_size > PGTCACHE_LOW_WATER) {
-			do {
-				if(pgd_quicklist)
-					free_page((unsigned long) get_pgd_fast());
-				if(pmd_quicklist)
-					free_page((unsigned long) get_pmd_fast());
-				if(pte_quicklist)
-					free_page((unsigned long) get_pte_fast());
-			} while(pgtable_cache_size > PGTCACHE_HIGH_WATER);
-		}
+		check_pgt_cache();
 		if(tq_scheduler) {
 			lock_kernel();
 			run_task_queue(&tq_scheduler);
@@ -592,6 +571,10 @@
 		p->tss.flags |= SPARC_FLAG_KTHREAD;
 		p->tss.current_ds = KERNEL_DS;
 		p->tss.ctx = 0;
+		__asm__ __volatile__("flushw");
+		memcpy((void *)(p->tss.ksp + STACK_BIAS),
+		       (void *)(regs->u_regs[UREG_FP] + STACK_BIAS),
+		       sizeof(struct reg_window));
 		p->tss.kregs->u_regs[UREG_G6] = (unsigned long) p;
 	} else {
 		if(current->tss.flags & SPARC_FLAG_32BIT) {
diff -u --recursive --new-file v2.1.96/linux/arch/sparc64/kernel/psycho.c linux/arch/sparc64/kernel/psycho.c
--- v2.1.96/linux/arch/sparc64/kernel/psycho.c	Mon Jan 12 15:15:44 1998
+++ linux/arch/sparc64/kernel/psycho.c	Tue Apr 14 17:44:20 1998
@@ -1,4 +1,4 @@
-/* $Id: psycho.c,v 1.31 1998/01/10 18:26:15 ecd Exp $
+/* $Id: psycho.c,v 1.50 1998/04/10 12:29:47 ecd Exp $
  * psycho.c: Ultra/AX U2P PCI controller support.
  *
  * Copyright (C) 1997 David S. Miller (davem@caipfs.rutgers.edu)
@@ -8,6 +8,7 @@
 #include <linux/config.h>
 #include <linux/kernel.h>
 #include <linux/types.h>
+#include <linux/init.h>
 
 #include <asm/ebus.h>
 #include <asm/sbus.h> /* for sanity check... */
@@ -15,6 +16,7 @@
 #undef PROM_DEBUG
 #undef FIXUP_REGS_DEBUG
 #undef FIXUP_IRQ_DEBUG
+#undef FIXUP_VMA_DEBUG
 
 #ifdef PROM_DEBUG
 #define dprintf	prom_printf
@@ -22,6 +24,9 @@
 #define dprintf printk
 #endif
 
+unsigned long pci_dvma_offset = 0x00000000UL;
+unsigned long pci_dvma_mask   = 0xffffffffUL;
+
 #ifndef CONFIG_PCI
 
 int pcibios_present(void)
@@ -51,12 +56,12 @@
 
 #include <linux/smp.h>
 #include <linux/smp_lock.h>
-#include <linux/bios32.h>
 #include <linux/pci.h>
 
 #include <asm/io.h>
 #include <asm/oplib.h>
 #include <asm/pbm.h>
+#include <asm/apb.h>
 #include <asm/uaccess.h>
 
 struct linux_psycho *psycho_root = NULL;
@@ -95,8 +100,9 @@
 		~(sizeof(unsigned long) - 1));
 }
 
-static unsigned long psycho_iommu_init(struct linux_psycho *psycho,
-				       unsigned long memory_start)
+__initfunc(static unsigned long psycho_iommu_init(struct linux_psycho *psycho,
+						  int tsbsize,
+						  unsigned long memory_start))
 {
 	unsigned long tsbbase = PAGE_ALIGN(memory_start);
 	unsigned long control, i;
@@ -114,10 +120,10 @@
 	control &= ~(IOMMU_CTRL_DENAB);
 	psycho->psycho_regs->iommu_control = control;
 
-	memory_start = (tsbbase + ((32 * 1024) * 8));
+	memory_start = (tsbbase + ((tsbsize * 1024) * 8));
 	iopte = (unsigned long *)tsbbase;
 
-	for(i = 0; i < (32 * 1024); i++) {
+	for(i = 0; i < (tsbsize * 1024); i++) {
 		*iopte = (IOPTE_VALID | IOPTE_64K |
 			  IOPTE_CACHE | IOPTE_WRITE);
 		*iopte |= (i << 16);
@@ -128,15 +134,215 @@
 
 	control = psycho->psycho_regs->iommu_control;
 	control &= ~(IOMMU_CTRL_TSBSZ);
-	control |= (IOMMU_TSBSZ_32K | IOMMU_CTRL_TBWSZ | IOMMU_CTRL_ENAB);
+	control |= (IOMMU_CTRL_TBWSZ | IOMMU_CTRL_ENAB);
+	switch(tsbsize) {
+	case 8:
+		pci_dvma_mask   = 0x1fffffffUL;
+		control |= IOMMU_TSBSZ_8K;
+		break;
+	case 16:
+		pci_dvma_mask   = 0x3fffffffUL;
+		control |= IOMMU_TSBSZ_16K;
+		break;
+	case 32:
+		pci_dvma_mask   = 0x7fffffffUL;
+		control |= IOMMU_TSBSZ_32K;
+		break;
+	default:
+		prom_printf("iommu_init: Illegal TSB size %d\n", tsbsize);
+		prom_halt();
+		break;
+	}
 	psycho->psycho_regs->iommu_control = control;
 
 	return memory_start;
 }
 
 extern void prom_pbm_ranges_init(int node, struct linux_pbm_info *pbm);
+extern void prom_pbm_intmap_init(int node, struct linux_pbm_info *pbm);
+
+/*
+ * Poor man's PCI...
+ */
+__initfunc(unsigned long sabre_init(int pnode, unsigned long memory_start))
+{
+	struct linux_prom64_registers pr_regs[2];
+	struct linux_psycho *sabre;
+	unsigned long ctrl;
+	int tsbsize, node, err;
+	u32 busrange[2];
+	u32 vdma[2];
+	u32 portid;
+	int bus;
+
+	sabre = (struct linux_psycho *)memory_start;
+	memory_start = long_align(memory_start + sizeof(struct linux_psycho));
+
+	portid = prom_getintdefault(pnode, "upa-portid", 0xff);
+
+	memset(sabre, 0, sizeof(*sabre));
+
+	sabre->next = psycho_root;
+	psycho_root = sabre;
+
+	sabre->upa_portid = portid;
+	sabre->index = linux_num_psycho++;
+
+	/*
+	 * Map in SABRE register set and report the presence of this SABRE.
+	 */
+	err = prom_getproperty(pnode, "reg",
+			       (char *)&pr_regs[0], sizeof(pr_regs));
+	if(err == 0 || err == -1) {
+		prom_printf("SABRE: Error, cannot get U2P registers "
+			    "from PROM.\n");
+		prom_halt();
+	}
+
+	/*
+	 * First REG in property is base of entire SABRE register space.
+	 */
+	sabre->psycho_regs =
+			sparc_alloc_io((pr_regs[0].phys_addr & 0xffffffff),
+				       NULL, sizeof(struct psycho_regs),
+				       "SABRE Registers",
+				       (pr_regs[0].phys_addr >> 32), 0);
+	if(sabre->psycho_regs == NULL) {
+		prom_printf("SABRE: Error, cannot map SABRE main registers.\n");
+		prom_halt();
+	}
+
+	printk("PCI: Found SABRE, main regs at %p\n", sabre->psycho_regs);
+#ifdef PROM_DEBUG
+	dprintf("PCI: Found SABRE, main regs at %p\n", sabre->psycho_regs);
+#endif
+
+	ctrl = sabre->psycho_regs->pci_a_control;
+	ctrl = (1UL << 36) | (1UL << 34) | (1UL << 21) | (1UL << 8) | 0x0fUL;
+	sabre->psycho_regs->pci_a_control = ctrl;
+
+	/* Now map in PCI config space for entire SABRE. */
+	sabre->pci_config_space =
+			sparc_alloc_io(((pr_regs[0].phys_addr & 0xffffffff)
+								+ 0x01000000),
+				       NULL, 0x01000000,
+				       "PCI Config Space",
+				       (pr_regs[0].phys_addr >> 32), 0);
+	if(sabre->pci_config_space == NULL) {
+		prom_printf("SABRE: Error, cannot map PCI config space.\n");
+		prom_halt();
+	}
+
+	/* Report some more info. */
+	printk("SABRE: PCI config space at %p\n", sabre->pci_config_space);
+#ifdef PROM_DEBUG
+	dprintf("SABRE: PCI config space at %p\n", sabre->pci_config_space);
+#endif
+
+	err = prom_getproperty(pnode, "virtual-dma",
+			       (char *)&vdma[0], sizeof(vdma));
+	if(err == 0 || err == -1) {
+		prom_printf("SABRE: Error, cannot get virtual-dma property "
+			    "from PROM.\n");
+		prom_halt();
+	}
+
+	switch(vdma[1]) {
+		case 0x20000000:
+			tsbsize = 8;
+			break;
+		case 0x40000000:
+			tsbsize = 16;
+			break;
+		case 0x80000000:
+			tsbsize = 32;
+			break;
+		default:
+			prom_printf("SABRE: strange virtual-dma size.\n");
+			prom_halt();
+	}
+
+	memory_start = psycho_iommu_init(sabre, tsbsize, memory_start);
+	pci_dvma_offset = vdma[0];
+
+	printk("SABRE: DVMA at %08x [%08x]\n", vdma[0], vdma[1]);
+#ifdef PROM_DEBUG
+	dprintf("SABRE: DVMA at %08x [%08x]\n", vdma[0], vdma[1]);
+#endif
+
+	err = prom_getproperty(pnode, "bus-range",
+				       (char *)&busrange[0], sizeof(busrange));
+	if(err == 0 || err == -1) {
+		prom_printf("SIMBA: Error, cannot get PCI bus-range "
+			    " from PROM.\n");
+		prom_halt();
+	}
+
+	sabre->pci_first_busno = busrange[0];
+	sabre->pci_last_busno = busrange[1];
+	sabre->pci_bus = &pci_root;
+
+	/*
+	 * Handle config space reads through any Simba on APB.
+	 */
+	for (bus = sabre->pci_first_busno; bus <= sabre->pci_last_busno; bus++)
+		bus2pbm[bus] = &sabre->pbm_A;
+
+	/*
+	 * Look for APB underneath.
+	 */
+	node = prom_getchild(pnode);
+	while ((node = prom_searchsiblings(node, "pci"))) {
+		struct linux_pbm_info *pbm;
+		char namebuf[128];
+
+		err = prom_getproperty(node, "model", namebuf, sizeof(namebuf));
+		if ((err <= 0) || strncmp(namebuf, "SUNW,simba", err))
+			goto next_pci;
+
+		err = prom_getproperty(node, "bus-range",
+				       (char *)&busrange[0], sizeof(busrange));
+		if(err == 0 || err == -1) {
+			prom_printf("SIMBA: Error, cannot get PCI bus-range "
+				    " from PROM.\n");
+			prom_halt();
+		}
+
+		if (busrange[0] == 1)
+			pbm = &sabre->pbm_B;
+		else
+			pbm = &sabre->pbm_A;
+
+		pbm->parent = sabre;
+		pbm->IO_assignments = NULL;
+		pbm->MEM_assignments = NULL;
+		pbm->prom_node = node;
+
+		prom_getstring(node, "name", namebuf, sizeof(namebuf));
+		strcpy(pbm->prom_name, namebuf);
+
+		/* Now the ranges. */
+		prom_pbm_ranges_init(pnode, pbm);
+		prom_pbm_intmap_init(node, pbm);
+
+		pbm->pci_first_busno = busrange[0];
+		pbm->pci_last_busno = busrange[1];
+		memset(&pbm->pci_bus, 0, sizeof(struct pci_bus));
+
+		for (bus = pbm->pci_first_busno;
+		     bus <= pbm->pci_last_busno; bus++)
+			bus2pbm[bus] = pbm;
+
+	next_pci:
+		node = prom_getsibling(node);
+		if (!node)
+			break;
+	}
 
-unsigned long pcibios_init(unsigned long memory_start, unsigned long memory_end)
+	return memory_start;
+}
+
+__initfunc(unsigned long pcibios_init(unsigned long memory_start, unsigned long memory_end))
 {
 	struct linux_prom64_registers pr_regs[3];
 	struct linux_psycho *psycho;
@@ -144,9 +350,9 @@
 	u32 portid;
 	int node;
 
-	printk("PSYCHO: Probing for controllers.\n");
+	printk("PCI: Probing for controllers.\n");
 #ifdef PROM_DEBUG
-	dprintf("PSYCHO: Probing for controllers.\n");
+	dprintf("PCI: Probing for controllers.\n");
 #endif
 
 	memory_start = long_align(memory_start);
@@ -157,6 +363,12 @@
 		u32 busrange[2];
 		int err, is_pbm_a;
 
+		err = prom_getproperty(node, "model", namebuf, sizeof(namebuf));
+		if ((err > 0) && !strncmp(namebuf, "SUNW,sabre", err)) {
+			memory_start = sabre_init(node, memory_start);
+			goto next_pci;
+		}
+
 		psycho = (struct linux_psycho *)memory_start;
 
 		portid = prom_getintdefault(node, "upa-portid", 0xff);
@@ -200,34 +412,30 @@
 		 * Third REG in property is base of entire PSYCHO
 		 * register space.
 		 */
-		psycho->psycho_regs = sparc_alloc_io((pr_regs[2].phys_addr & 0xffffffff),
-						     NULL, sizeof(struct psycho_regs),
-						     "PSYCHO Registers",
-						     (pr_regs[2].phys_addr >> 32), 0);
+		psycho->psycho_regs =
+			sparc_alloc_io((pr_regs[2].phys_addr & 0xffffffff),
+				       NULL, sizeof(struct psycho_regs),
+				       "PSYCHO Registers",
+				       (pr_regs[2].phys_addr >> 32), 0);
 		if(psycho->psycho_regs == NULL) {
 			prom_printf("PSYCHO: Error, cannot map PSYCHO "
 				    "main registers.\n");
 			prom_halt();
 		}
 
-		printk("PSYCHO: Found controller, main regs at %p\n",
+		printk("PCI: Found PSYCHO, main regs at %p\n",
 		       psycho->psycho_regs);
 #ifdef PROM_DEBUG
-		dprintf("PSYCHO: Found controller, main regs at %p\n",
+		dprintf("PCI: Found PSYCHO, main regs at %p\n",
 			psycho->psycho_regs);
 #endif
 
 		psycho->psycho_regs->irq_retry = 0xff;
 
-#if 0
-		psycho->psycho_regs->ecc_control |= 1;
-		psycho->psycho_regs->sbuf_a_control = 0;
-		psycho->psycho_regs->sbuf_b_control = 0;
-#endif
-
 		/* Now map in PCI config space for entire PSYCHO. */
 		psycho->pci_config_space =
-			sparc_alloc_io(((pr_regs[2].phys_addr & 0xffffffff)+0x01000000),
+			sparc_alloc_io(((pr_regs[2].phys_addr & 0xffffffff)
+								+ 0x01000000),
 				       NULL, 0x01000000,
 				       "PCI Config Space",
 				       (pr_regs[2].phys_addr >> 32), 0);
@@ -244,7 +452,8 @@
 			psycho->pci_config_space);
 #endif
 
-		memory_start = psycho_iommu_init(psycho, memory_start);
+		memory_start = psycho_iommu_init(psycho, 32, memory_start);
+		pci_dvma_offset = 0x80000000UL;
 
 		is_pbm_a = ((pr_regs[0].phys_addr & 0x6000) == 0x2000);
 
@@ -268,6 +477,7 @@
 
 		/* Now the ranges. */
 		prom_pbm_ranges_init(node, pbm);
+		prom_pbm_intmap_init(node, pbm);
 
 		/* Finally grab the pci bus root array for this pbm after
 		 * having found the bus range existing under it.
@@ -282,6 +492,7 @@
 		pbm->pci_last_busno = busrange[1];
 		memset(&pbm->pci_bus, 0, sizeof(struct pci_bus));
 
+	next_pci:
 		node = prom_getsibling(node);
 		if(!node)
 			break;
@@ -308,54 +519,18 @@
 	return psycho_root != NULL;
 }
 
-int pcibios_find_device (unsigned short vendor, unsigned short device_id,
-			 unsigned short index, unsigned char *bus,
-			 unsigned char *devfn)
-{
-	unsigned int curr = 0;
-	struct pci_dev *dev;
-
-	for (dev = pci_devices; dev; dev = dev->next) {
-		if (dev->vendor == vendor && dev->device == device_id) {
-			if (curr == index) {
-				*devfn = dev->devfn;
-				*bus = dev->bus->number;
-				return PCIBIOS_SUCCESSFUL;
-			}
-			++curr;
-		}
-	}
-	return PCIBIOS_DEVICE_NOT_FOUND;
-}
-
-int pcibios_find_class (unsigned int class_code, unsigned short index,
-			unsigned char *bus, unsigned char *devfn)
-{
-	unsigned int curr = 0;
-	struct pci_dev *dev;
-
-	for (dev = pci_devices; dev; dev = dev->next) {
-		if (dev->class == class_code) {
-			if (curr == index) {
-				*devfn = dev->devfn;
-				*bus = dev->bus->number;
-				return PCIBIOS_SUCCESSFUL;
-			}
-			++curr;
-		}
-	}
-	return PCIBIOS_DEVICE_NOT_FOUND;
-}
-
 static inline struct pci_vma *pci_find_vma(struct linux_pbm_info *pbm,
 					   unsigned long start,
-					   int io)
+					   unsigned int offset, int io)
 {
 	struct pci_vma *vp = (io ? pbm->IO_assignments : pbm->MEM_assignments);
 
-	while(vp) {
-		if(vp->end > start)
+	while (vp) {
+		if (offset && (vp->offset != offset))
+			goto next;
+		if (vp->end >= start)
 			break;
+	next:
 		vp = vp->next;
 	}
 	return vp;
@@ -391,7 +566,7 @@
 		/* Check for programming errors. */
 		if(vp &&
 		   ((vp->start >= new->start && vp->start < new->end) ||
-		    ((vp->end - 1) >= new->start && (vp->end - 1) < new->end))) {
+		    (vp->end >= new->start && vp->end < new->end))) {
 			prom_printf("pci_add_vma: Wheee, overlapping %s PCI vma's\n",
 				    io ? "IO" : "MEM");
 			prom_printf("pci_add_vma: vp[%016lx:%016lx] "
@@ -414,7 +589,7 @@
 	pci_alloc_arena = NULL;
 }
 
-static void *pci_init_alloc(int size)
+__initfunc(static void *pci_init_alloc(int size))
 {
 	unsigned long start = long_align(*pci_alloc_arena);
 	void *mp = (void *)start;
@@ -439,8 +614,8 @@
 }
 
 
-static void
-pbm_reconfigure_bridges(struct linux_pbm_info *pbm, unsigned char bus)
+__initfunc(static void
+pbm_reconfigure_bridges(struct linux_pbm_info *pbm, unsigned char bus))
 {
 	unsigned int devfn, l, class;
 	unsigned char hdr_type = 0;
@@ -487,7 +662,7 @@
 	}
 }
 
-static void pbm_fixup_busno(struct linux_pbm_info *pbm, unsigned char bus)
+__initfunc(static void pbm_fixup_busno(struct linux_pbm_info *pbm, unsigned char bus))
 {
 	unsigned int nbus;
 
@@ -513,7 +688,79 @@
 }
 
 
-static void pbm_probe(struct linux_pbm_info *pbm, unsigned long *mstart)
+__initfunc(static void apb_init(struct linux_psycho *sabre))
+{
+	struct pci_dev *pdev;
+	unsigned short stmp;
+	unsigned int itmp;
+
+	for (pdev = sabre->pci_bus->devices; pdev; pdev = pdev->sibling) {
+		if (pdev->vendor == PCI_VENDOR_ID_SUN &&
+		    pdev->device == PCI_DEVICE_ID_SUN_SIMBA) {
+
+			pci_read_config_word(pdev, PCI_COMMAND, &stmp);
+			stmp |= PCI_COMMAND_SERR | PCI_COMMAND_PARITY |
+				PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY |
+				PCI_COMMAND_IO;
+			pci_write_config_word(pdev, PCI_COMMAND, stmp);
+
+			pci_write_config_word(pdev, PCI_STATUS, 0xffff);
+			pci_write_config_word(pdev, PCI_SEC_STATUS, 0xffff);
+
+			pci_read_config_word(pdev, PCI_BRIDGE_CONTROL, &stmp);
+			stmp = PCI_BRIDGE_CTL_MASTER_ABORT |
+			       PCI_BRIDGE_CTL_SERR |
+			       PCI_BRIDGE_CTL_PARITY;
+			pci_write_config_word(pdev, PCI_BRIDGE_CONTROL, stmp);
+
+			pci_read_config_dword(pdev, APB_PCI_CONTROL_HIGH, &itmp);
+			itmp = APB_PCI_CTL_HIGH_SERR |
+			       APB_PCI_CTL_HIGH_ARBITER_EN;
+			pci_write_config_dword(pdev, APB_PCI_CONTROL_HIGH, itmp);
+
+			pci_read_config_dword(pdev, APB_PCI_CONTROL_LOW, &itmp);
+			itmp = APB_PCI_CTL_LOW_ARB_PARK |
+			       APB_PCI_CTL_LOW_ERRINT_EN | 0x0f;
+			pci_write_config_dword(pdev, APB_PCI_CONTROL_LOW, itmp);
+
+			/*
+			 * Setup Registers for Guaranteed Completion.
+			 */
+			pci_write_config_byte(pdev, APB_PRIMARY_MASTER_RETRY_LIMIT, 0);
+			pci_write_config_byte(pdev, APB_SECONDARY_MASTER_RETRY_LIMIT, 0);
+			pci_write_config_byte(pdev, APB_PIO_TARGET_RETRY_LIMIT, 0x80);
+			pci_write_config_byte(pdev, APB_PIO_TARGET_LATENCY_TIMER, 0);
+			pci_write_config_byte(pdev, APB_DMA_TARGET_RETRY_LIMIT, 0x80);
+			pci_write_config_byte(pdev, APB_DMA_TARGET_LATENCY_TIMER, 0);
+		}
+	}
+}
+
+__initfunc(static void sabre_probe(struct linux_psycho *sabre,
+				   unsigned long *mstart))
+{
+	struct pci_bus *pbus = sabre->pci_bus;
+	static unsigned char busno = 0;
+
+	pbus->number = pbus->secondary = busno;
+	pbus->sysdata = sabre;
+
+	pbus->subordinate = pci_scan_bus(pbus, mstart);
+	busno = pbus->subordinate + 1;
+
+	for(pbus = pbus->children; pbus; pbus = pbus->next) {
+		if (pbus->number == sabre->pbm_A.pci_first_busno)
+			memcpy(&sabre->pbm_A.pci_bus, pbus, sizeof(*pbus));
+		if (pbus->number == sabre->pbm_B.pci_first_busno)
+			memcpy(&sabre->pbm_B.pci_bus, pbus, sizeof(*pbus));
+	}
+
+	apb_init(sabre);
+}
+
+
+__initfunc(static void pbm_probe(struct linux_pbm_info *pbm,
+				 unsigned long *mstart))
 {
 	static struct pci_bus *pchain = NULL;
 	struct pci_bus *pbus = &pbm->pci_bus;
@@ -552,9 +799,9 @@
 	}
 }
 
-static int pdev_to_pnode_sibtraverse(struct linux_pbm_info *pbm,
-				     struct pci_dev *pdev,
-				     int node)
+__initfunc(static int pdev_to_pnode_sibtraverse(struct linux_pbm_info *pbm,
+						struct pci_dev *pdev,
+						int node))
 {
 	struct linux_prom_pci_registers pregs[PROMREG_MAX];
 	int err;
@@ -583,7 +830,7 @@
 	return 0;
 }
 
-static void pdev_cookie_fillin(struct linux_pbm_info *pbm, struct pci_dev *pdev)
+__initfunc(static void pdev_cookie_fillin(struct linux_pbm_info *pbm, struct pci_dev *pdev))
 {
 	struct pcidev_cookie *pcp;
 	int node = prom_getchild(pbm->prom_node);
@@ -597,24 +844,37 @@
 	pdev->sysdata = pcp;
 }
 
-static void fill_in_pbm_cookies(struct linux_pbm_info *pbm)
+__initfunc(static void fill_in_pbm_cookies(struct pci_bus *pbus,
+					   struct linux_pbm_info *pbm))
 {
-	struct pci_bus *pbtmp, *pbus = &pbm->pci_bus;
 	struct pci_dev *pdev;
 
-	for(pbtmp = pbus->children; pbtmp; pbtmp = pbtmp->children)
-		pbtmp->sysdata = pbm;
+	pbus->sysdata = pbm;
+
+	for(pdev = pbus->devices; pdev; pdev = pdev->sibling)
+		pdev_cookie_fillin(pbm, pdev);
+
+	for(pbus = pbus->children; pbus; pbus = pbus->next)
+		fill_in_pbm_cookies(pbus, pbm);
+}
+
+__initfunc(static void sabre_cookie_fillin(struct linux_psycho *sabre))
+{
+	struct pci_bus *pbus = sabre->pci_bus;
 
-	for( ; pbus; pbus = pbus->children)
-		for(pdev = pbus->devices; pdev; pdev = pdev->sibling)
-			pdev_cookie_fillin(pbm, pdev);
+	for(pbus = pbus->children; pbus; pbus = pbus->next) {
+		if (pbus->number == sabre->pbm_A.pci_first_busno)
+			pdev_cookie_fillin(&sabre->pbm_A, pbus->self);
+		else if (pbus->number == sabre->pbm_B.pci_first_busno)
+			pdev_cookie_fillin(&sabre->pbm_B, pbus->self);
+	}
 }
 
 /* Walk PROM device tree under PBM, looking for 'assigned-address'
  * properties, and recording them in pci_vma's linked in via
  * PBM->assignments.
  */
-static int gimme_ebus_assignments(int node, struct linux_prom_pci_registers *aregs)
+__initfunc(static int gimme_ebus_assignments(int node, struct linux_prom_pci_registers *aregs))
 {
 	struct linux_prom_ebus_ranges erng[PROMREG_MAX];
 	int err, iter;
@@ -632,18 +892,26 @@
 		ap->phys_hi = ep->parent_phys_hi;
 		ap->phys_mid = ep->parent_phys_mid;
 		ap->phys_lo = ep->parent_phys_lo;
+
+		ap->size_hi = 0;
+		ap->size_lo = ep->size;
 	}
 	return err;
 }
 
-static void assignment_process(struct linux_pbm_info *pbm, int node)
+__initfunc(static void assignment_process(struct linux_pbm_info *pbm, int node))
 {
 	struct linux_prom_pci_registers aregs[PROMREG_MAX];
 	char pname[256];
 	int err, iter, numa;
 
 	err = prom_getproperty(node, "name", (char *)&pname[0], sizeof(pname));
-	if(strncmp(pname, "ebus", 4) == 0) {
+	if (err > 0)
+		pname[err] = 0;
+#ifdef FIXUP_VMA_DEBUG
+	dprintf("%s: %s\n", __FUNCTION__, err > 0 ? pname : "???");
+#endif
+	if(strcmp(pname, "ebus") == 0) {
 		numa = gimme_ebus_assignments(node, &aregs[0]);
 	} else {
 		err = prom_getproperty(node, "assigned-addresses",
@@ -653,7 +921,7 @@
 		if(err == 0 || err == -1)
 			return;
 
-		numa = (err / sizeof(struct linux_prom_pci_ranges));
+		numa = (err / sizeof(struct linux_prom_pci_registers));
 	}
 
 	for(iter = 0; iter < numa; iter++) {
@@ -667,8 +935,6 @@
 		io = (space == 1);
 
 		breg = (ap->phys_hi & 0xff);
-		if(breg == PCI_ROM_ADDRESS)
-			continue;
 
 		vp = pci_vma_alloc();
 
@@ -677,21 +943,20 @@
 		 * XXX either due to it's layout so...
 		 */
 		vp->start = ap->phys_lo;
-		vp->end = vp->start + ap->size_lo;
-		vp->base_reg = breg;
-
-		/* Sanity */
-		if(io && (vp->end & ~(0xffff))) {
-			prom_printf("assignment_process: Out of range PCI I/O "
-				    "[%08lx:%08lx]\n", vp->start, vp->end);
-			prom_halt();
-		}
+		vp->end = vp->start + ap->size_lo - 1;
+		vp->offset = (ap->phys_hi & 0xffffff);
 
 		pci_add_vma(pbm, vp, io);
+
+#ifdef FIXUP_VMA_DEBUG
+		dprintf("%s: BaseReg %02x", pname, breg);
+		dprintf(" %s vma [%08x,%08x]\n",
+			io ? "I/O" : breg == PCI_ROM_ADDRESS ? "ROM" : "MEM", vp->start, vp->end);
+#endif
 	}
 }
 
-static void assignment_walk_siblings(struct linux_pbm_info *pbm, int node)
+__initfunc(static void assignment_walk_siblings(struct linux_pbm_info *pbm, int node))
 {
 	while(node) {
 		int child = prom_getchild(node);
@@ -704,17 +969,112 @@
 	}
 }
 
-static void record_assignments(struct linux_pbm_info *pbm)
+static inline void record_assignments(struct linux_pbm_info *pbm)
 {
+	struct pci_vma *vp;
+
+	if (pbm->parent->pci_bus) {
+		/*
+		 * Disallow anything that is not in our IO/MEM map on SIMBA.
+		 */
+		struct pci_bus *pbus = pbm->parent->pci_bus;
+		struct pci_dev *pdev;
+		unsigned char map;
+		int bit;
+
+		for (pdev = pbus->devices; pdev; pdev = pdev->sibling) {
+			struct pcidev_cookie *pcp = pdev->sysdata;
+			if (!pcp) {
+				prom_printf("record_assignments: "
+					"no pcidev_cookie for pdev %02x\n",
+					pdev->devfn);
+				prom_halt();
+			}
+			if (pcp->pbm == pbm)
+				break;
+		}
+
+		if (!pdev) {
+			prom_printf("record_assignments: no pdev for PBM\n");
+			prom_halt();
+		}
+
+		pci_read_config_byte(pdev, APB_IO_ADDRESS_MAP, &map);
+#ifdef FIXUP_VMA_DEBUG
+		dprintf("%s: IO   %02x\n", __FUNCTION__, map);
+#endif
+		for (bit = 0; bit < 8; bit++) {
+			if (!(map & (1 << bit))) {
+				vp = pci_vma_alloc();
+				vp->start = (bit << 21);
+				vp->end = vp->start + (1 << 21) - 1;
+				vp->offset = 0;
+				pci_add_vma(pbm, vp, 1);
+#ifdef FIXUP_VMA_DEBUG
+				dprintf("%s: IO   prealloc vma [%08x,%08x]\n",
+					__FUNCTION__, vp->start, vp->end);
+#endif
+			}
+		}
+		pci_read_config_byte(pdev, APB_MEM_ADDRESS_MAP, &map);
+#ifdef FIXUP_VMA_DEBUG
+		dprintf("%s: MEM  %02x\n", __FUNCTION__, map);
+#endif
+		for (bit = 0; bit < 8; bit++) {
+			if (!(map & (1 << bit))) {
+				vp = pci_vma_alloc();
+				vp->start = (bit << 29);
+				vp->end = vp->start + (1 << 29) - 1;
+				vp->offset = 0;
+				pci_add_vma(pbm, vp, 0);
+#ifdef FIXUP_VMA_DEBUG
+				dprintf("%s: MEM  prealloc vma [%08x,%08x]\n",
+					__FUNCTION__, vp->start, vp->end);
+#endif
+			}
+		}
+	}
+
 	assignment_walk_siblings(pbm, prom_getchild(pbm->prom_node));
+
+	/*
+	 * Protect ISA IO space from being used.
+	 */
+	vp = pci_find_vma(pbm, 0, 0, 1);
+	if (!vp || 0x400 <= vp->start) {
+		vp = pci_vma_alloc();
+		vp->start = 0;
+		vp->end = vp->start + 0x400 - 1;
+		vp->offset = 0;
+		pci_add_vma(pbm, vp, 1);
+	}
+
+#ifdef FIXUP_VMA_DEBUG
+	dprintf("PROM IO assignments for PBM %s:\n",
+		pbm == &pbm->parent->pbm_A ? "A" : "B");
+	vp = pbm->IO_assignments;
+	while (vp) {
+		dprintf("  [%08x,%08x] (%s)\n", vp->start, vp->end,
+			vp->offset ? "Register" : "Unmapped");
+		vp = vp->next;
+	}
+	dprintf("PROM MEM assignments for PBM %s:\n",
+		pbm == &pbm->parent->pbm_A ? "A" : "B");
+	vp = pbm->MEM_assignments;
+	while (vp) {
+		dprintf("  [%08x,%08x] (%s)\n", vp->start, vp->end,
+			vp->offset ? "Register" : "Unmapped");
+		vp = vp->next;
+	}
+#endif
 }
 
-static void fixup_regs(struct pci_dev *pdev,
-		       struct linux_pbm_info *pbm,
-		       struct linux_prom_pci_registers *pregs,
-		       int nregs,
-		       struct linux_prom_pci_registers *assigned,
-		       int numaa)
+__initfunc(static void fixup_regs(struct pci_dev *pdev,
+				  struct linux_pbm_info *pbm,
+				  struct linux_prom_pci_registers *pregs,
+				  int nregs,
+				  struct linux_prom_pci_registers *assigned,
+				  int numaa))
 {
 	int preg, rng;
 	int IO_seen = 0;
@@ -724,34 +1084,39 @@
 		struct linux_prom_pci_registers *ap = NULL;
 		int bustype = (pregs[preg].phys_hi >> 24) & 0x3;
 		int bsreg, brindex;
+		unsigned int rtmp;
 		u64 pci_addr;
 
 		if(bustype == 0) {
 			/* Config space cookie, nothing to do. */
 			if(preg != 0)
-				printk("%s: strange, config space not 0\n",
-				       __FUNCTION__);
+				printk("%s %02x.%02x [%04x,%04x]: "
+				       "strange, config space not 0\n",
+				       __FUNCTION__,
+				       pdev->bus->number, pdev->devfn,
+				       pdev->vendor, pdev->device);
 			continue;
 		} else if(bustype == 3) {
 			/* XXX add support for this... */
-			printk("%s: Warning, ignoring 64-bit PCI memory space, "
+			printk("%s %02x.%02x [%04x,%04x]: "
+			       "Warning, ignoring 64-bit PCI memory space, "
 			       "tell Eddie C. Dost (ecd@skynet.be).\n",
-			       __FUNCTION__);
+			       __FUNCTION__,
+			       pdev->bus->number, pdev->devfn,
+			       pdev->vendor, pdev->device);
 			continue;
 		}
-		bsreg = (pregs[preg].phys_hi & 0xff);
 
-		/* We can safely ignore these. */
-		if(bsreg == PCI_ROM_ADDRESS)
-			continue;
+		bsreg = (pregs[preg].phys_hi & 0xff);
 
 		/* Sanity */
 		if((bsreg < PCI_BASE_ADDRESS_0) ||
-		   (bsreg > (PCI_BASE_ADDRESS_5 + 4)) ||
+		   ((bsreg > (PCI_BASE_ADDRESS_5 + 4)) && (bsreg != PCI_ROM_ADDRESS)) ||
 		   (bsreg & 3)) {
-			printk("%s: [%04x:%04x]: "
+			printk("%s %02x.%02x [%04x:%04x]: "
 			       "Warning, ignoring bogus basereg [%x]\n",
-			       __FUNCTION__, pdev->vendor, pdev->device, bsreg);
+			       __FUNCTION__, pdev->bus->number, pdev->devfn,
+			       pdev->vendor, pdev->device, bsreg);
 			printk("  PROM reg: %08x.%08x.%08x %08x.%08x\n",
 			       pregs[preg].phys_hi, pregs[preg].phys_mid,
 			       pregs[preg].phys_lo, pregs[preg].size_hi,
@@ -798,7 +1163,16 @@
 			/* AIEEE */
 			prom_printf("fixup_doit: YIEEE, cannot find PBM ranges\n");
 		}
-		pdev->base_address[brindex] = (unsigned long)__va(pci_addr);
+		if (bsreg == PCI_ROM_ADDRESS) {
+			pdev->rom_address = (unsigned long)__va(pci_addr);
+			pdev->rom_address |= 1;
+			/*
+			 * Enable access to the ROM.
+			 */
+			pci_read_config_dword(pdev, PCI_ROM_ADDRESS, &rtmp);
+			pci_write_config_dword(pdev, PCI_ROM_ADDRESS, rtmp | 1);
+		} else
+			pdev->base_address[brindex] = (unsigned long)__va(pci_addr);
 
 		/* Preserve I/O space bit. */
 		if(bustype == 0x1) {
@@ -811,15 +1185,18 @@
 
 	/* Now handle assignments PROM did not take care of. */
 	if(nregs) {
+		unsigned int rtmp, ridx;
+		unsigned int offset, base;
+		struct pci_vma *vp;
+		u64 pci_addr;
 		int breg;
 
 		for(breg = PCI_BASE_ADDRESS_0; breg <= PCI_BASE_ADDRESS_5; breg += 4) {
-			unsigned int rtmp, ridx = ((breg - PCI_BASE_ADDRESS_0) >> 2);
-			unsigned int base = (unsigned int)pdev->base_address[ridx];
-			struct pci_vma *vp;
-			u64 pci_addr;
 			int io;
 
+			ridx = ((breg - PCI_BASE_ADDRESS_0) >> 2);
+			base = (unsigned int)pdev->base_address[ridx];
+
 			if(pdev->base_address[ridx] > PAGE_OFFSET)
 				continue;
 
@@ -827,19 +1204,14 @@
 			base &= ~((io ?
 				   PCI_BASE_ADDRESS_IO_MASK :
 				   PCI_BASE_ADDRESS_MEM_MASK));
-			vp = pci_find_vma(pbm, base, io);
+			offset = (pdev->bus->number << 16) | (pdev->devfn << 8) | breg;
+			vp = pci_find_vma(pbm, base, offset, io);
 			if(!vp || vp->start > base) {
 				unsigned int size, new_base;
 
-				pcibios_read_config_dword(pdev->bus->number,
-							  pdev->devfn,
-							  breg, &rtmp);
-				pcibios_write_config_dword(pdev->bus->number,
-							   pdev->devfn,
-							   breg, 0xffffffff);
-				pcibios_read_config_dword(pdev->bus->number,
-							  pdev->devfn,
-							  breg, &size);
+				pci_read_config_dword(pdev, breg, &rtmp);
+				pci_write_config_dword(pdev, breg, 0xffffffff);
+				pci_read_config_dword(pdev, breg, &size);
 				if(io)
 					size &= ~1;
 				size = (~(size) + 1);
@@ -847,7 +1219,8 @@
 					continue;
 
 				new_base = 0;
-				for(vp=pci_find_vma(pbm,new_base,io); ; vp=vp->next) {
+				for(vp = pci_find_vma(pbm, new_base, 0, io); ;
+				    vp = vp->next) {
 					if(!vp || new_base + size <= vp->start)
 						break;
 					new_base = (vp->end + (size - 1)) & ~(size-1);
@@ -859,26 +1232,27 @@
 				}
 				vp = pci_vma_alloc();
 				vp->start = new_base;
-				vp->end = vp->start + size;
-				vp->base_reg = breg;
+				vp->end = vp->start + size - 1;
+				vp->offset = offset;
 
-				/* Sanity */
-				if(io && vp->end & ~(0xffff)) {
-					prom_printf("PCI: Out of range PCI I/O "
-						    "[%08lx:%08lx] during fixup\n",
-						    vp->start, vp->end);
-					prom_halt();
-				}
 				pci_add_vma(pbm, vp, io);
 
+#ifdef FIXUP_VMA_DEBUG
+				dprintf("%02x.%02x.%x: BaseReg %02x",
+					pdev->bus->number,
+					PCI_SLOT(pdev->devfn),
+					PCI_FUNC(pdev->devfn),
+					breg);
+				dprintf(" %s vma [%08x,%08x]\n",
+					io ? "I/O" : breg == PCI_ROM_ADDRESS ? "ROM" : "MEM", vp->start, vp->end);
+#endif
 				rtmp = new_base;
+				pci_read_config_dword(pdev, breg, &base);
 				if(io)
-					rtmp |= (rtmp & PCI_BASE_ADDRESS_IO_MASK);
+					rtmp |= (base & ~PCI_BASE_ADDRESS_IO_MASK);
 				else
-					rtmp |= (rtmp & PCI_BASE_ADDRESS_MEM_MASK);
-				pcibios_write_config_dword(pdev->bus->number,
-							   pdev->devfn,
-							   breg, rtmp);
+					rtmp |= (base & ~PCI_BASE_ADDRESS_MEM_MASK);
+				pci_write_config_dword(pdev, breg, rtmp);
 
 				/* Apply PBM ranges and update pci_dev. */
 				pci_addr = new_base;
@@ -912,13 +1286,93 @@
 				}
 			}
 		}
+
+		/*
+		 * Handle PCI_ROM_ADDRESS.
+		 */
+		breg = PCI_ROM_ADDRESS;
+		base = (unsigned int)pdev->rom_address;
+
+		if(pdev->rom_address > PAGE_OFFSET)
+			goto rom_address_done;
+
+		base &= PCI_ROM_ADDRESS_MASK;
+		offset = (pdev->bus->number << 16) | (pdev->devfn << 8) | breg;
+		vp = pci_find_vma(pbm, base, offset, 0);
+		if(!vp || vp->start > base) {
+			unsigned int size, new_base;
+
+			pci_read_config_dword(pdev, breg, &rtmp);
+			pci_write_config_dword(pdev, breg, 0xffffffff);
+			pci_read_config_dword(pdev, breg, &size);
+			size &= ~1;
+			size = (~(size) + 1);
+			if(!size)
+				goto rom_address_done;
+
+			new_base = 0;
+			for(vp = pci_find_vma(pbm, new_base, 0, 0); ; vp = vp->next) {
+				if(!vp || new_base + size <= vp->start)
+					break;
+				new_base = (vp->end + (size - 1)) & ~(size-1);
+			}
+			if(vp && (new_base + size > vp->start)) {
+				prom_printf("PCI: Impossible full MEM space.\n");
+				prom_halt();
+			}
+			vp = pci_vma_alloc();
+			vp->start = new_base;
+			vp->end = vp->start + size - 1;
+			vp->offset = offset;
+
+			pci_add_vma(pbm, vp, 0);
+
+#ifdef FIXUP_VMA_DEBUG
+			dprintf("%02x.%02x.%x: BaseReg %02x",
+				pdev->bus->number,
+				PCI_SLOT(pdev->devfn),
+				PCI_FUNC(pdev->devfn),
+				breg);
+			dprintf(" %s vma [%08x,%08x]\n",
+				"ROM", vp->start, vp->end);
+#endif
+
+			rtmp = new_base;
+			pci_read_config_dword(pdev, breg, &base);
+			rtmp |= (base & ~PCI_ROM_ADDRESS_MASK);
+			pci_write_config_dword(pdev, breg, rtmp);
+
+			/* Apply PBM ranges and update pci_dev. */
+			pci_addr = new_base;
+			for(rng = 0; rng < pbm->num_pbm_ranges; rng++) {
+				struct linux_prom_pci_ranges *rp;
+				int rspace;
+
+				rp = &pbm->pbm_ranges[rng];
+				rspace = (rp->child_phys_hi >> 24) & 3;
+				if(rspace != 2)
+					continue;
+				pci_addr += ((u64)rp->parent_phys_lo);
+				pci_addr += (((u64)rp->parent_phys_hi)<<32UL);
+				break;
+			}
+			if(rng == pbm->num_pbm_ranges) {
+				/* AIEEE */
+				prom_printf("fixup_doit: YIEEE, cannot find "
+					    "PBM ranges\n");
+			}
+			pdev->rom_address = (unsigned long)__va(pci_addr);
+
+			pdev->rom_address |= (base & ~PCI_ROM_ADDRESS_MASK);
+			MEM_seen = 1;
+		}
+	rom_address_done:
+
 	}
 	if(IO_seen || MEM_seen) {
 		unsigned int l;
 
-		pcibios_read_config_dword(pdev->bus->number,
-					  pdev->devfn,
-					  PCI_COMMAND, &l);
+		pci_read_config_dword(pdev, PCI_COMMAND, &l);
 #ifdef FIXUP_REGS_DEBUG
 		dprintf("[");
 #endif
@@ -937,9 +1391,7 @@
 #ifdef FIXUP_REGS_DEBUG
 		dprintf("]");
 #endif
-		pcibios_write_config_dword(pdev->bus->number,
-					   pdev->devfn,
-					   PCI_COMMAND, l);
+		pci_write_config_dword(pdev, PCI_COMMAND, l);
 	}
 
 #ifdef FIXUP_REGS_DEBUG
@@ -955,7 +1407,7 @@
 #define imap_offset(__member) \
 	((unsigned long)(&(((struct psycho_regs *)0)->__member)))
 
-static unsigned long psycho_pcislot_imap_offset(unsigned long ino)
+__initfunc(static unsigned long psycho_pcislot_imap_offset(unsigned long ino))
 {
 	unsigned int bus, slot;
 
@@ -963,17 +1415,20 @@
 	slot = (ino & 0x0c) >> 2;
 
 	if(bus == 0) {
-		/* Perform a sanity check, we might as well.
-		 * PBM A only has 2 PCI slots.
-		 */
-		if(slot > 1) {
-			prom_printf("pcislot_imap: Bogus slot on PBM A (%ld)\n", slot);
-			prom_halt();
-		}
-		if(slot == 0)
+		switch(slot) {
+		case 0:
 			return imap_offset(imap_a_slot0);
-		else
+		case 1:
 			return imap_offset(imap_a_slot1);
+		case 2:
+			return imap_offset(imap_a_slot2);
+		case 3:
+			return imap_offset(imap_a_slot3);
+		default:
+			prom_printf("pcislot_imap: IMPOSSIBLE [%d:%d]\n",
+				    bus, slot);
+			prom_halt();
+		}
 	} else {
 		switch(slot) {
 		case 0:
@@ -988,13 +1443,12 @@
 			prom_printf("pcislot_imap: IMPOSSIBLE [%d:%d]\n",
 				    bus, slot);
 			prom_halt();
-			return 0; /* Make gcc happy */
-		};
+		}
 	}
 }
 
 /* Exported for EBUS probing layer. */
-unsigned int psycho_irq_build(struct linux_pbm_info *pbm, unsigned int full_ino)
+__initfunc(unsigned int psycho_irq_build(struct linux_pbm_info *pbm, unsigned int full_ino))
 {
 	unsigned long imap_off, ign, ino;
 
@@ -1089,9 +1543,62 @@
 	return pci_irq_encode(imap_off, pbm->parent->index, ign, ino);
 }
 
-static void fixup_irq(struct pci_dev *pdev,
-		      struct linux_pbm_info *pbm,
-		      int node)
+__initfunc(static int pbm_intmap_match(struct linux_pbm_info *pbm,
+				       struct pci_dev *pdev,
+				       struct linux_prom_pci_registers *preg,
+				       unsigned int *interrupt))
+{
+	struct linux_prom_pci_registers ppreg;
+	unsigned int hi, mid, lo, irq;
+	int i;
+
+	if (!pbm->num_pbm_intmap)
+		return 0;
+
+	/*
+	 * Underneath a bridge, use register of parent bridge.
+	 */
+	if (pdev->bus->number != pbm->pci_first_busno) {
+		struct pcidev_cookie *pcp = pdev->bus->self->sysdata;
+		int node;
+
+		if (!pcp)
+			goto out;
+
+		node = pcp->prom_node;
+
+		i = prom_getproperty(node, "reg", (char*)&ppreg, sizeof(ppreg));
+		if(i == 0 || i == -1)
+			goto out;
+
+		preg = &ppreg;
+	}
+
+	hi = preg->phys_hi & pbm->pbm_intmask.phys_hi;
+	mid = preg->phys_mid & pbm->pbm_intmask.phys_mid;
+	lo = preg->phys_lo & pbm->pbm_intmask.phys_lo;
+	irq = *interrupt & pbm->pbm_intmask.interrupt;
+	for (i = 0; i < pbm->num_pbm_intmap; i++) {
+		if ((pbm->pbm_intmap[i].phys_hi == hi) &&
+		    (pbm->pbm_intmap[i].phys_mid == mid) &&
+		    (pbm->pbm_intmap[i].phys_lo == lo) &&
+		    (pbm->pbm_intmap[i].interrupt == irq)) {
+			*interrupt = pbm->pbm_intmap[i].cinterrupt;
+			return *interrupt;
+		}
+	}
+
+out:
+	prom_printf("pbm_intmap_match: IRQ [%08x.%08x.%08x.%08x] "
+		    "not found in interrupt-map\n", preg->phys_hi,
+		    preg->phys_mid, preg->phys_lo, *interrupt);
+	prom_halt();
+}
+
+__initfunc(static void fixup_irq(struct pci_dev *pdev,
+				 struct linux_pbm_info *pbm,
+				 struct linux_prom_pci_registers *preg,
+				 int node))
 {
 	unsigned int prom_irq, portid = pbm->parent->upa_portid;
 	unsigned char pci_irq_line = pdev->irq;
@@ -1102,13 +1609,24 @@
 #endif
 	err = prom_getproperty(node, "interrupts", (void *)&prom_irq, sizeof(prom_irq));
 	if(err == 0 || err == -1) {
-		prom_printf("fixup_irq: No interrupts property for dev[%04x:%04x]\n",
-			    pdev->vendor, pdev->device);
-		prom_halt();
+#ifdef FIXUP_IRQ_DEBUG
+		dprintf("No interrupts property.\n");
+#endif
+		pdev->irq = 0;
+		return;
 	}
 
+	/* See if we find a matching interrupt-map entry. */
+	if (pbm_intmap_match(pbm, pdev, preg, &prom_irq)) {
+		pdev->irq = psycho_irq_build(pbm,
+					     (pbm->parent->upa_portid << 6)
+					     | prom_irq);
+#ifdef FIXUP_IRQ_DEBUG
+		dprintf("interrupt-map specified prom_irq[%x] pdev->irq[%x]",
+		        prom_irq, pdev->irq);
+#endif
 	/* See if fully specified already (ie. for onboard devices like hme) */
-	if(((prom_irq & PSYCHO_IMAP_IGN) >> 6) == pbm->parent->upa_portid) {
+	} else if(((prom_irq & PSYCHO_IMAP_IGN) >> 6) == pbm->parent->upa_portid) {
 		pdev->irq = psycho_irq_build(pbm, prom_irq);
 #ifdef FIXUP_IRQ_DEBUG
 		dprintf("fully specified prom_irq[%x] pdev->irq[%x]",
@@ -1147,7 +1665,7 @@
 				slot = (pdev->bus->self->devfn >> 3) - 2;
 
 			/* Use low slot number bits of child as IRQ line. */
-			line = (line + ((pdev->devfn >> 3) - 4)) % 4;
+			line = (pdev->devfn >> 3) & 0x03;
 		}
 		slot = (slot << 2);
 
@@ -1159,16 +1677,10 @@
 		do {
 			unsigned char iline, ipin;
 
-			(void)pcibios_read_config_byte(pdev->bus->number,
-						       pdev->devfn,
-						       PCI_INTERRUPT_PIN,
-						       &ipin);
-			(void)pcibios_read_config_byte(pdev->bus->number,
-						       pdev->devfn,
-						       PCI_INTERRUPT_LINE,
-						       &iline);
-			dprintf("FIXED portid[%x] bus[%x] slot[%x] line[%x] irq[%x] "
-			        "iline[%x] ipin[%x] prom_irq[%x]",
+			pci_read_config_byte(pdev, PCI_INTERRUPT_PIN, &ipin);
+			pci_read_config_byte(pdev, PCI_INTERRUPT_LINE, &iline);
+			dprintf("FIXED portid[%x] bus[%x] slot[%x] line[%x] "
+				"irq[%x] iline[%x] ipin[%x] prom_irq[%x]",
 			        portid, bus>>4, slot>>2, line, pdev->irq,
 			        iline, ipin, prom_irq);
 		} while(0);
@@ -1178,21 +1690,19 @@
 	/*
 	 * Write the INO to config space PCI_INTERRUPT_LINE.
 	 */
-	(void)pcibios_write_config_byte(pdev->bus->number,
-					pdev->devfn,
-					PCI_INTERRUPT_LINE,
-					pdev->irq & PCI_IRQ_INO);
+	pci_write_config_byte(pdev, PCI_INTERRUPT_LINE,
+			      pdev->irq & PCI_IRQ_INO);
 
 #ifdef FIXUP_IRQ_DEBUG
 	dprintf("\n");
 #endif
 }
 
-static void fixup_doit(struct pci_dev *pdev,
-		       struct linux_pbm_info *pbm,
-		       struct linux_prom_pci_registers *pregs,
-		       int nregs,
-		       int node)
+__initfunc(static void fixup_doit(struct pci_dev *pdev,
+				  struct linux_pbm_info *pbm,
+				  struct linux_prom_pci_registers *pregs,
+				  int nregs,
+				  int node))
 {
 	struct linux_prom_pci_registers assigned[PROMREG_MAX];
 	int numaa, err;
@@ -1209,12 +1719,12 @@
 	fixup_regs(pdev, pbm, pregs, nregs, &assigned[0], numaa);
 
 	/* Next, fixup interrupt numbers. */
-	fixup_irq(pdev, pbm, node);
+	fixup_irq(pdev, pbm, &pregs[0], node);
 }
 
-static void fixup_pci_dev(struct pci_dev *pdev,
-			  struct pci_bus *pbus,
-			  struct linux_pbm_info *pbm)
+__initfunc(static void fixup_pci_dev(struct pci_dev *pdev,
+				     struct pci_bus *pbus,
+				     struct linux_pbm_info *pbm))
 {
 	struct linux_prom_pci_registers pregs[PROMREG_MAX];
 	struct pcidev_cookie *pcp = pdev->sysdata;
@@ -1225,18 +1735,12 @@
 		unsigned short cmd;
 
 		/* First, enable bus mastering. */
-		pcibios_read_config_word(pdev->bus->number,
-					 pdev->devfn,
-					 PCI_COMMAND, &cmd);
+		pci_read_config_word(pdev, PCI_COMMAND, &cmd);
 		cmd |= PCI_COMMAND_MASTER;
-		pcibios_write_config_word(pdev->bus->number,
-					  pdev->devfn,
-					  PCI_COMMAND, cmd);
+		pci_write_config_word(pdev, PCI_COMMAND, cmd);
 
 		/* Now, set cache line size to 64-bytes. */
-		pcibios_write_config_byte(pdev->bus->number,
-					  pdev->devfn,
-					  PCI_CACHE_LINE_SIZE, 64);
+		pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, 64);
 	}
 
 	/* Ignore if this is one of the PBM's, EBUS, or a
@@ -1246,8 +1750,17 @@
 	if((pdev->class >> 8 == PCI_CLASS_BRIDGE_PCI) ||
 	   (pdev->class >> 8 == PCI_CLASS_BRIDGE_HOST) ||
 	   (pdev->class >> 8 == PCI_CLASS_BRIDGE_OTHER) ||
-	   (pcp == NULL))
+	   (pcp == NULL)) {
+		/*
+		 * Prevent access to PCI_ROM_ADDRESS, in case present
+		 * as we don't fixup the address.
+		 */
+		if (pdev->rom_address) {
+			pci_write_config_dword(pdev, PCI_ROM_ADDRESS, 0);
+			pdev->rom_address = 0;
+		}
 		return;
+	}
 
 	node = pcp->prom_node;
 
@@ -1260,20 +1773,30 @@
 	nregs = (err / sizeof(pregs[0]));
 
 	fixup_doit(pdev, pbm, &pregs[0], nregs, node);
+
+	/* Enable bus mastering on IDE interfaces. */
+	if ((pdev->class >> 8 == PCI_CLASS_STORAGE_IDE)
+	    && (pdev->class & 0x80)) {
+		unsigned short cmd;
+
+		pci_read_config_word(pdev, PCI_COMMAND, &cmd);
+		cmd |= PCI_COMMAND_MASTER;
+		pci_write_config_word(pdev, PCI_COMMAND, cmd);
+	}
 }
 
-static void fixup_pci_bus(struct pci_bus *pbus, struct linux_pbm_info *pbm)
+__initfunc(static void fixup_pci_bus(struct pci_bus *pbus, struct linux_pbm_info *pbm))
 {
 	struct pci_dev *pdev;
 
 	for(pdev = pbus->devices; pdev; pdev = pdev->sibling)
 		fixup_pci_dev(pdev, pbus, pbm);
 
-	for(pbus = pbus->children; pbus; pbus = pbus->children)
+	for(pbus = pbus->children; pbus; pbus = pbus->next)
 		fixup_pci_bus(pbus, pbm);
 }
 
-static void fixup_addr_irq(struct linux_pbm_info *pbm)
+__initfunc(static void fixup_addr_irq(struct linux_pbm_info *pbm))
 {
 	struct pci_bus *pbus = &pbm->pci_bus;
 
@@ -1286,14 +1809,16 @@
 /* Walk all PCI devices probes, fixing up base registers and IRQ registers.
  * We use OBP for most of this work.
  */
-static void psycho_final_fixup(struct linux_psycho *psycho)
+__initfunc(static void psycho_final_fixup(struct linux_psycho *psycho))
 {
 	/* Second, fixup base address registers and IRQ lines... */
-	fixup_addr_irq(&psycho->pbm_A);
-	fixup_addr_irq(&psycho->pbm_B);
+	if (psycho->pbm_A.parent)
+		fixup_addr_irq(&psycho->pbm_A);
+	if (psycho->pbm_B.parent)
+		fixup_addr_irq(&psycho->pbm_B);
 }
 
-unsigned long pcibios_fixup(unsigned long memory_start, unsigned long memory_end)
+__initfunc(unsigned long pcibios_fixup(unsigned long memory_start, unsigned long memory_end))
 {
 	struct linux_psycho *psycho;
 
@@ -1312,11 +1837,16 @@
 	 */
 
 	for (psycho = psycho_root; psycho; psycho = psycho->next) {
-		/* Probe busses under PBM B. */
-		pbm_probe(&psycho->pbm_B, &memory_start);
+		/* Probe bus on builtin PCI. */
+		if (psycho->pci_bus)
+			sabre_probe(psycho, &memory_start);
+		else {
+			/* Probe busses under PBM B. */
+			pbm_probe(&psycho->pbm_B, &memory_start);
 
-		/* Probe busses under PBM A. */
-		pbm_probe(&psycho->pbm_A, &memory_start);
+			/* Probe busses under PBM A. */
+			pbm_probe(&psycho->pbm_A, &memory_start);
+		}
 	}
 
 	pci_init_alloc_init(&memory_start);
@@ -1327,8 +1857,13 @@
 	 * a pci_dev cookie (PBM+PROM_NODE, for pci_dev's).
 	 */
 	for (psycho = psycho_root; psycho; psycho = psycho->next) {
-		fill_in_pbm_cookies(&psycho->pbm_A);
-		fill_in_pbm_cookies(&psycho->pbm_B);
+		if (psycho->pci_bus)
+			sabre_cookie_fillin(psycho);
+
+		fill_in_pbm_cookies(&psycho->pbm_A.pci_bus,
+				    &psycho->pbm_A);
+		fill_in_pbm_cookies(&psycho->pbm_B.pci_bus,
+				    &psycho->pbm_B);
 
 		/* See what OBP has taken care of already. */
 		record_assignments(&psycho->pbm_A);
@@ -1373,11 +1908,157 @@
 static inline int
 out_of_range(struct linux_pbm_info *pbm, unsigned char bus, unsigned char devfn)
 {
-	return (((pbm == &pbm->parent->pbm_B) && PCI_SLOT(devfn) > 4) ||
-		((pbm == &pbm->parent->pbm_A) && PCI_SLOT(devfn) > 6) ||
+	return ((pbm->parent == 0) ||
+		((pbm == &pbm->parent->pbm_B) && (bus == pbm->pci_first_busno) && PCI_SLOT(devfn) > 4) ||
+		((pbm == &pbm->parent->pbm_A) && (bus == pbm->pci_first_busno) && PCI_SLOT(devfn) > 6) ||
 		(pci_probe_enable == 0));
 }
 
+static inline int
+sabre_out_of_range(unsigned char devfn)
+{
+	return ((PCI_SLOT(devfn) == 0) && (PCI_FUNC(devfn) > 0)) ||
+	       ((PCI_SLOT(devfn) == 1) && (PCI_FUNC(devfn) > 1)) ||
+	       (PCI_SLOT(devfn) > 1);
+}
+
+static int
+sabre_read_config_byte(struct linux_pbm_info *pbm,
+		       unsigned char bus, unsigned char devfn,
+		       unsigned char where, unsigned char *value)
+{
+	if (bus)
+		return pbm_read_config_byte(pbm, bus, devfn, where, value);
+
+	if (sabre_out_of_range(devfn)) {
+		*value = 0xff;
+		return PCIBIOS_SUCCESSFUL;
+	}
+
+	if (where < 8) {
+		unsigned short tmp;
+
+		pbm_read_config_word(pbm, bus, devfn, where & ~1, &tmp);
+		if (where & 1)
+			*value = tmp >> 8;
+		else
+			*value = tmp & 0xff;
+		return PCIBIOS_SUCCESSFUL;
+	} else
+		return pbm_read_config_byte(pbm, bus, devfn, where, value);
+}
+
+static int
+sabre_read_config_word(struct linux_pbm_info *pbm,
+		       unsigned char bus, unsigned char devfn,
+		       unsigned char where, unsigned short *value)
+{
+	if (bus)
+		return pbm_read_config_word(pbm, bus, devfn, where, value);
+
+	if (sabre_out_of_range(devfn)) {
+		*value = 0xffff;
+		return PCIBIOS_SUCCESSFUL;
+	}
+
+	if (where < 8)
+		return pbm_read_config_word(pbm, bus, devfn, where, value);
+	else {
+		unsigned char tmp;
+
+		pbm_read_config_byte(pbm, bus, devfn, where, &tmp);
+		*value = tmp;
+		pbm_read_config_byte(pbm, bus, devfn, where + 1, &tmp);
+		*value |= tmp << 8;
+		return PCIBIOS_SUCCESSFUL;
+	}
+}
+
+static int
+sabre_read_config_dword(struct linux_pbm_info *pbm,
+			unsigned char bus, unsigned char devfn,
+			unsigned char where, unsigned int *value)
+{
+	unsigned short tmp;
+
+	if (bus)
+		return pbm_read_config_dword(pbm, bus, devfn, where, value);
+
+	if (sabre_out_of_range(devfn)) {
+		*value = 0xffffffff;
+		return PCIBIOS_SUCCESSFUL;
+	}
+
+	sabre_read_config_word(pbm, bus, devfn, where, &tmp);
+	*value = tmp;
+	sabre_read_config_word(pbm, bus, devfn, where + 2, &tmp);
+	*value |= tmp << 16;
+	return PCIBIOS_SUCCESSFUL;
+}
+
+static int
+sabre_write_config_byte(struct linux_pbm_info *pbm,
+			unsigned char bus, unsigned char devfn,
+			unsigned char where, unsigned char value)
+{
+	if (bus)
+		return pbm_write_config_byte(pbm, bus, devfn, where, value);
+
+	if (sabre_out_of_range(devfn))
+		return PCIBIOS_SUCCESSFUL;
+
+	if (where < 8) {
+		unsigned short tmp;
+
+		pbm_read_config_word(pbm, bus, devfn, where & ~1, &tmp);
+		if (where & 1) {
+			value &= 0x00ff;
+			value |= tmp << 8;
+		} else {
+			value &= 0xff00;
+			value |= tmp;
+		}
+		return pbm_write_config_word(pbm, bus, devfn, where & ~1, tmp);
+	} else
+		return pbm_write_config_byte(pbm, bus, devfn, where, value);
+}
+
+static int
+sabre_write_config_word(struct linux_pbm_info *pbm,
+			unsigned char bus, unsigned char devfn,
+			unsigned char where, unsigned short value)
+{
+	if (bus)
+		return pbm_write_config_word(pbm, bus, devfn, where, value);
+
+	if (sabre_out_of_range(devfn))
+		return PCIBIOS_SUCCESSFUL;
+
+	if (where < 8)
+		return pbm_write_config_word(pbm, bus, devfn, where, value);
+	else {
+		pbm_write_config_byte(pbm, bus, devfn, where, value & 0xff);
+		pbm_write_config_byte(pbm, bus, devfn, where + 1, value >> 8);
+		return PCIBIOS_SUCCESSFUL;
+	}
+}
+
+static int
+sabre_write_config_dword(struct linux_pbm_info *pbm,
+			 unsigned char bus, unsigned char devfn,
+			 unsigned char where, unsigned int value)
+{
+	if (bus)
+		return pbm_write_config_dword(pbm, bus, devfn, where, value);
+
+	if (sabre_out_of_range(devfn))
+		return PCIBIOS_SUCCESSFUL;
+
+	sabre_write_config_word(pbm, bus, devfn, where, value & 0xffff);
+	sabre_write_config_word(pbm, bus, devfn, where + 2, value >> 16);
+	return PCIBIOS_SUCCESSFUL;
+}
+
 static int
 pbm_read_config_byte(struct linux_pbm_info *pbm,
 		     unsigned char bus, unsigned char devfn,
@@ -1574,36 +2255,60 @@
 int pcibios_read_config_byte (unsigned char bus, unsigned char devfn,
 			      unsigned char where, unsigned char *value)
 {
-	return pbm_read_config_byte(bus2pbm[bus], bus, devfn, where, value);
+	struct linux_pbm_info *pbm = bus2pbm[bus];
+
+	if (pbm && pbm->parent && pbm->parent->pci_bus)
+		return sabre_read_config_byte(pbm, bus, devfn, where, value);
+	return pbm_read_config_byte(pbm, bus, devfn, where, value);
 }
 
 int pcibios_read_config_word (unsigned char bus, unsigned char devfn,
 			      unsigned char where, unsigned short *value)
 {
-	return pbm_read_config_word(bus2pbm[bus], bus, devfn, where, value);
+	struct linux_pbm_info *pbm = bus2pbm[bus];
+
+	if (pbm && pbm->parent && pbm->parent->pci_bus)
+		return sabre_read_config_word(pbm, bus, devfn, where, value);
+	return pbm_read_config_word(pbm, bus, devfn, where, value);
 }
 
 int pcibios_read_config_dword (unsigned char bus, unsigned char devfn,
 			       unsigned char where, unsigned int *value)
 {
-	return pbm_read_config_dword(bus2pbm[bus], bus, devfn, where, value);
+	struct linux_pbm_info *pbm = bus2pbm[bus];
+
+	if (pbm && pbm->parent && pbm->parent->pci_bus)
+		return sabre_read_config_dword(pbm, bus, devfn, where, value);
+	return pbm_read_config_dword(pbm, bus, devfn, where, value);
 }
 
 int pcibios_write_config_byte (unsigned char bus, unsigned char devfn,
 			       unsigned char where, unsigned char value)
 {
-	return pbm_write_config_byte(bus2pbm[bus], bus, devfn, where, value);
+	struct linux_pbm_info *pbm = bus2pbm[bus];
+
+	if (pbm && pbm->parent && pbm->parent->pci_bus)
+		return sabre_write_config_byte(pbm, bus, devfn, where, value);
+	return pbm_write_config_byte(pbm, bus, devfn, where, value);
 }
 
 int pcibios_write_config_word (unsigned char bus, unsigned char devfn,
 			       unsigned char where, unsigned short value)
 {
+	struct linux_pbm_info *pbm = bus2pbm[bus];
+
+	if (pbm && pbm->parent && pbm->parent->pci_bus)
+		return sabre_write_config_word(pbm, bus, devfn, where, value);
 	return pbm_write_config_word(bus2pbm[bus], bus, devfn, where, value);
 }
 
 int pcibios_write_config_dword (unsigned char bus, unsigned char devfn,
 			        unsigned char where, unsigned int value)
 {
+	struct linux_pbm_info *pbm = bus2pbm[bus];
+
+	if (pbm && pbm->parent && pbm->parent->pci_bus)
+		return sabre_write_config_dword(pbm, bus, devfn, where, value);
 	return pbm_write_config_dword(bus2pbm[bus], bus, devfn, where, value);
 }
 
@@ -1690,6 +2395,11 @@
 	unlock_kernel();
 
 	return err;
+}
+
+__initfunc(char *pcibios_setup(char *str))
+{
+	return str;
 }
 
 #endif
diff -u --recursive --new-file v2.1.96/linux/arch/sparc64/kernel/setup.c linux/arch/sparc64/kernel/setup.c
--- v2.1.96/linux/arch/sparc64/kernel/setup.c	Mon Jan 12 15:15:44 1998
+++ linux/arch/sparc64/kernel/setup.c	Tue Apr 14 17:44:21 1998
@@ -1,4 +1,4 @@
-/*  $Id: setup.c,v 1.18 1997/12/18 02:43:00 ecd Exp $
+/*  $Id: setup.c,v 1.20 1998/02/24 17:02:39 jj Exp $
  *  linux/arch/sparc64/kernel/setup.c
  *
  *  Copyright (C) 1995,1996  David S. Miller (davem@caip.rutgers.edu)
@@ -55,11 +55,6 @@
 
 unsigned int phys_bytes_of_ram, end_of_phys_memory;
 
-unsigned long bios32_init(unsigned long memory_start, unsigned long memory_end)
-{
-	return memory_start;
-}
-
 /* Typing sync at the prom prompt calls the function pointed to by
  * the sync callback which I set to the following function.
  * This should sync all filesystems and return, for now it just
@@ -413,14 +408,17 @@
 extern char *sparc_cpu_type[];
 extern char *sparc_fpu_type[];
 
-extern char *smp_info(void);
-extern char *mmu_info(void);
+extern int smp_info(char *);
+extern int smp_bogo(char *);
+extern int mmu_info(char *);
 
 int get_cpuinfo(char *buffer)
 {
 	int cpuid=smp_processor_id();
+	int len;
 
-	return sprintf(buffer, "cpu\t\t: %s\n"
+	len = sprintf(buffer, 
+	    "cpu\t\t: %s\n"
             "fpu\t\t: %s\n"
             "promlib\t\t: Version 3 Revision %d\n"
             "prom\t\t: %d.%d.%d\n"
@@ -429,33 +427,22 @@
 	    "ncpus active\t: %d\n"
 #ifndef __SMP__
             "BogoMips\t: %lu.%02lu\n"
-#else
-	    "Cpu0Bogo\t: %lu.%02lu\n"
-	    "Cpu1Bogo\t: %lu.%02lu\n"
-	    "Cpu2Bogo\t: %lu.%02lu\n"
-	    "Cpu3Bogo\t: %lu.%02lu\n"
-#endif
-	    "%s"
-#ifdef __SMP__
-	    "%s"
 #endif
 	    ,
             sparc_cpu_type[cpuid],
             sparc_fpu_type[cpuid],
             prom_rev, prom_prev >> 16, (prom_prev >> 8) & 0xff, prom_prev & 0xff,
-	    linux_num_cpus, smp_num_cpus,
+	    linux_num_cpus, smp_num_cpus
 #ifndef __SMP__
-            loops_per_sec/500000, (loops_per_sec/5000) % 100,
-#else
-	    cpu_data[0].udelay_val/500000, (cpu_data[0].udelay_val/5000)%100,
-	    cpu_data[1].udelay_val/500000, (cpu_data[1].udelay_val/5000)%100,
-	    cpu_data[2].udelay_val/500000, (cpu_data[2].udelay_val/5000)%100,
-	    cpu_data[3].udelay_val/500000, (cpu_data[3].udelay_val/5000)%100,
+            , loops_per_sec/500000, (loops_per_sec/5000) % 100
 #endif
-	    mmu_info()
+	    );
 #ifdef __SMP__
-	    , smp_info()
+	len += smp_bogo(buffer + len);
 #endif
-            );
-
+	len += mmu_info(buffer + len);
+#ifdef __SMP__
+	len += smp_info(buffer + len);
+#endif
+	return len;
 }
diff -u --recursive --new-file v2.1.96/linux/arch/sparc64/kernel/signal32.c linux/arch/sparc64/kernel/signal32.c
--- v2.1.96/linux/arch/sparc64/kernel/signal32.c	Mon Jan 12 15:15:44 1998
+++ linux/arch/sparc64/kernel/signal32.c	Tue Apr 14 17:44:21 1998
@@ -1,4 +1,4 @@
-/*  $Id: signal32.c,v 1.34 1997/12/15 15:04:49 jj Exp $
+/*  $Id: signal32.c,v 1.35 1998/04/01 07:00:43 davem Exp $
  *  arch/sparc64/kernel/signal32.c
  *
  *  Copyright (C) 1991, 1992  Linus Torvalds
@@ -1023,6 +1023,7 @@
 		spin_lock_irq(&current->sigmask_lock);
 		sigorsets(&current->blocked,&current->blocked,&ka->sa.sa_mask);
 		sigaddset(&current->blocked,signr);
+		recalc_sigpending(current);
 		spin_unlock_irq(&current->sigmask_lock);
 	}
 }
diff -u --recursive --new-file v2.1.96/linux/arch/sparc64/kernel/smp.c linux/arch/sparc64/kernel/smp.c
--- v2.1.96/linux/arch/sparc64/kernel/smp.c	Mon Jan 12 15:15:44 1998
+++ linux/arch/sparc64/kernel/smp.c	Tue Apr 14 17:44:21 1998
@@ -12,6 +12,7 @@
 #include <linux/interrupt.h>
 #include <linux/kernel_stat.h>
 #include <linux/delay.h>
+#include <linux/init.h>
 
 #include <asm/head.h>
 #include <asm/ptrace.h>
@@ -25,12 +26,14 @@
 #include <asm/hardirq.h>
 #include <asm/softirq.h>
 #include <asm/uaccess.h>
+#include <asm/timer.h>
 
 #define __KERNEL_SYSCALLS__
 #include <linux/unistd.h>
 
 extern int linux_num_cpus;
 extern void calibrate_delay(void);
+extern unsigned prom_cpu_nodes[];
 
 volatile int smp_processors_ready = 0;
 unsigned long cpu_present_map = 0;
@@ -39,35 +42,46 @@
 
 struct cpuinfo_sparc cpu_data[NR_CPUS] __attribute__ ((aligned (64)));
 
-static unsigned char boot_cpu_id = 0;
+static unsigned char boot_cpu_id __initdata = 0;
 static int smp_activated = 0;
 
 volatile int cpu_number_map[NR_CPUS];
-volatile int cpu_logical_map[NR_CPUS];
+volatile int __cpu_logical_map[NR_CPUS];
 
 struct klock_info klock_info = { KLOCK_CLEAR, 0 };
 
-void smp_setup(char *str, int *ints)
+__initfunc(void smp_setup(char *str, int *ints))
 {
 	/* XXX implement me XXX */
 }
 
-static char smp_buf[512];
+int smp_info(char *buf)
+{
+	int len = 7, i;
+	
+	strcpy(buf, "State:\n");
+	for (i = 0; i < NR_CPUS; i++)
+		if(cpu_present_map & (1UL << i))
+			len += sprintf(buf + len,
+					"CPU%d:\t\t%s\n",
+					i, klock_info.akp == i ? "akp" : "online");
+	return len;
+}
 
-char *smp_info(void)
+int smp_bogo(char *buf)
 {
-	/* XXX not SMP safe and need to support up to 64 penguins */
-	sprintf(smp_buf,
-"        CPU0\t\tCPU1\t\tCPU2\t\tCPU3\n"
-"State:  %s\t\t%s\t\t%s\t\t%s\n",
-(cpu_present_map & 1) ? ((klock_info.akp == 0) ? "akp" : "online") : "offline",
-(cpu_present_map & 2) ? ((klock_info.akp == 1) ? "akp" : "online") : "offline",
-(cpu_present_map & 4) ? ((klock_info.akp == 2) ? "akp" : "online") : "offline",
-(cpu_present_map & 8) ? ((klock_info.akp == 3) ? "akp" : "online") : "offline");
-	return smp_buf;
+	int len = 0, i;
+	
+	for (i = 0; i < NR_CPUS; i++)
+		if(cpu_present_map & (1UL << i))
+			len += sprintf(buf + len,
+				       "Cpu%dBogo\t: %lu.%02lu\n",
+				       i, cpu_data[i].udelay_val / 500000,
+				       (cpu_data[i].udelay_val / 5000) % 100);
+	return len;
 }
 
-void smp_store_cpu_info(int id)
+__initfunc(void smp_store_cpu_info(int id))
 {
 	cpu_data[id].udelay_val			= loops_per_sec;
 	cpu_data[id].irq_count			= 0;
@@ -80,7 +94,7 @@
 
 extern void distribute_irqs(void);
 
-void smp_commence(void)
+__initfunc(void smp_commence(void))
 {
 	distribute_irqs();
 }
@@ -92,7 +106,7 @@
 extern void inherit_locked_prom_mappings(int save_p);
 extern void cpu_probe(void);
 
-void smp_callin(void)
+__initfunc(void smp_callin(void))
 {
 	int cpuid = hard_smp_processor_id();
 
@@ -156,22 +170,24 @@
 
 extern unsigned long smp_trampoline;
 
-void smp_boot_cpus(void)
+__initfunc(void smp_boot_cpus(void))
 {
 	int cpucount = 0, i;
 
 	printk("Entering UltraSMPenguin Mode...\n");
+	boot_cpu_id = hard_smp_processor_id();
 	smp_tickoffset_init();
 	__sti();
 	cpu_present_map = 0;
 	for(i = 0; i < linux_num_cpus; i++)
-		cpu_present_map |= (1UL << i);
+		cpu_present_map |= (1UL << linux_cpus[i].mid);
 	for(i = 0; i < NR_CPUS; i++) {
 		cpu_number_map[i] = -1;
-		cpu_logical_map[i] = -1;
+		__cpu_logical_map[i] = -1;
 	}
 	cpu_number_map[boot_cpu_id] = 0;
-	cpu_logical_map[0] = boot_cpu_id;
+	prom_cpu_nodes[boot_cpu_id] = linux_cpus[0].prom_node;
+	__cpu_logical_map[0] = boot_cpu_id;
 	klock_info.akp = boot_cpu_id;
 	current->processor = boot_cpu_id;
 	smp_store_cpu_info(boot_cpu_id);
@@ -188,13 +204,18 @@
 			unsigned long entry = (unsigned long)(&smp_trampoline);
 			struct task_struct *p;
 			int timeout;
+			int no;
+			extern unsigned long phys_base;
 
-			entry -= KERNBASE;
+			entry += phys_base - KERNBASE;
 			kernel_thread(start_secondary, NULL, CLONE_PID);
 			p = task[++cpucount];
 			p->processor = i;
 			callin_flag = 0;
-			prom_startcpu(linux_cpus[i].prom_node,
+			for (no = 0; no < linux_num_cpus; no++)
+				if (linux_cpus[no].mid == i)
+					break;
+			prom_startcpu(linux_cpus[no].prom_node,
 				      entry, ((unsigned long)p));
 			for(timeout = 0; timeout < 5000000; timeout++) {
 				if(callin_flag)
@@ -202,8 +223,9 @@
 				udelay(100);
 			}
 			if(callin_flag) {
-				cpu_number_map[i] = i;
-				cpu_logical_map[i] = i;
+				cpu_number_map[i] = cpucount;
+				prom_cpu_nodes[i] = linux_cpus[no].prom_node;
+				__cpu_logical_map[cpucount] = i;
 			} else {
 				cpucount--;
 				printk("Processor %d is stuck.\n", i);
@@ -248,9 +270,9 @@
 
 /* #define XCALL_DEBUG */
 
-static inline void xcall_deliver(u64 data0, u64 data1, u64 data2, u64 pstate, int cpu)
+static inline void xcall_deliver(u64 data0, u64 data1, u64 data2, u64 pstate, unsigned long cpu)
 {
-	u64 result, target = (((unsigned long)linux_cpus[cpu].mid) << 14) | 0x70;
+	u64 result, target = (cpu << 14) | 0x70;
 	int stuck;
 
 #ifdef XCALL_DEBUG
@@ -307,12 +329,15 @@
 	if(smp_processors_ready) {
 		unsigned long mask = (cpu_present_map & ~(1UL<<smp_processor_id()));
 		u64 pstate, data0 = (((u64)ctx)<<32 | (((u64)func) & 0xffffffff));
-		int i, ncpus = smp_num_cpus;
+		int i, ncpus = smp_num_cpus - 1;
 
 		__asm__ __volatile__("rdpr %%pstate, %0" : "=r" (pstate));
-		for(i = 0; i < ncpus; i++) {
-			if(mask & (1UL << i))
+		for(i = 0; i < NR_CPUS; i++) {
+			if(mask & (1UL << i)) {
 				xcall_deliver(data0, data1, data2, pstate, i);
+				ncpus--;
+			}
+			if (!ncpus) break;
 		}
 		/* NOTE: Caller runs local copy on master. */
 	}
@@ -489,13 +514,14 @@
 #endif
 }
 
-static unsigned long real_tick_offset, current_tick_offset;
+static unsigned long current_tick_offset;
 
 #define prof_multiplier(__cpu)		cpu_data[(__cpu)].multiplier
 #define prof_counter(__cpu)		cpu_data[(__cpu)].counter
 
 extern void update_one_process(struct task_struct *p, unsigned long ticks,
-			       unsigned long user, unsigned long system);
+			       unsigned long user, unsigned long system,
+			       int cpu);
 
 void smp_percpu_timer_interrupt(struct pt_regs *regs)
 {
@@ -503,32 +529,62 @@
 	int cpu = smp_processor_id();
 	int user = user_mode(regs);
 
+	/*
+	 * Check for level 14 softint.
+	 */
+	if (!(get_softint() & (1UL << 0))) {
+		extern void handler_irq(int, struct pt_regs *);
+
+		handler_irq(14, regs);
+		return;
+	}
+
 	clear_softint((1UL << 0));
 	do {
 		if(!user)
 			sparc64_do_profile(regs->tpc);
 		if(!--prof_counter(cpu)) {
+
+			if (cpu == boot_cpu_id) {
+				extern void irq_enter(int, int);
+				extern void irq_exit(int, int);
+
+				irq_enter(cpu, 0);
+				kstat.irqs[cpu][0]++;
+
+				timer_tick_interrupt(regs);
+
+				irq_exit(cpu, 0);
+			}
+
 			if(current->pid) {
-				unsigned int *inc_me;
+				unsigned int *inc, *inc2;
 
-				update_one_process(current, 1, user, !user);
+				update_one_process(current, 1, user, !user, cpu);
 				if(--current->counter < 0) {
 					current->counter = 0;
 					need_resched = 1;
 				}
 
 				if(user) {
-					if(current->priority < DEF_PRIORITY)
-						inc_me = &kstat.cpu_nice;
-					else
-						inc_me = &kstat.cpu_user;
+					if(current->priority < DEF_PRIORITY) {
+						inc = &kstat.cpu_nice;
+						inc2 = &kstat.per_cpu_nice[cpu];
+					} else {
+						inc = &kstat.cpu_user;
+						inc2 = &kstat.per_cpu_user[cpu];
+					}
 				} else {
-					inc_me = &kstat.cpu_system;
+					inc = &kstat.cpu_system;
+					inc2 = &kstat.per_cpu_system[cpu];
 				}
-				atomic_inc((atomic_t *)inc_me);
+				atomic_inc((atomic_t *)inc);
+				atomic_inc((atomic_t *)inc2);
 			}
+
 			prof_counter(cpu) = prof_multiplier(cpu);
 		}
+
 		__asm__ __volatile__("rd	%%tick_cmpr, %0\n\t"
 				     "add	%0, %2, %0\n\t"
 				     "wr	%0, 0x0, %%tick_cmpr\n\t"
@@ -538,12 +594,55 @@
 	} while (tick >= compare);
 }
 
-static void smp_setup_percpu_timer(void)
+__initfunc(static void smp_setup_percpu_timer(void))
 {
 	int cpu = smp_processor_id();
 
 	prof_counter(cpu) = prof_multiplier(cpu) = 1;
 
+	if (cpu == boot_cpu_id) {
+		extern unsigned long tl0_itick;
+		extern unsigned long tl0_smp_itick;
+		unsigned long flags;
+
+		save_flags(flags); cli();
+
+		/*
+		 * Steal TICK_INT interrupts from timer_interrupt().
+		 */
+		__asm__ __volatile__("
+			.globl	tl0_smp_itick
+			b,pt	%%xcc, 1f
+			 nop
+
+		tl0_smp_itick:
+			rdpr	%%pil, %%g2
+			wrpr	%%g0, 15, %%pil
+			b,pt	%%xcc, etrap_irq
+			 rd	%%pc, %%g7
+			call	smp_percpu_timer_interrupt
+			 add	%%sp, %0, %%o0
+			b,pt	%%xcc, rtrap
+			 clr	%%l6
+
+		1:"
+			: /* no outputs */
+			: "i" (STACK_BIAS + REGWIN_SZ));
+
+		memcpy(&tl0_itick, &tl0_smp_itick, 8 * 4);
+
+		__asm__ __volatile__("
+			membar	#StoreStore
+			flush	%0 + 0x00
+			flush	%0 + 0x08
+			flush	%0 + 0x10
+			flush	%0 + 0x18"
+			: /* no outputs */
+			: "r" (&tl0_itick));
+
+		restore_flags(flags);
+	}
+
 	__asm__ __volatile__("rd	%%tick, %%g1\n\t"
 			     "add	%%g1, %0, %%g1\n\t"
 			     "wr	%%g1, 0x0, %%tick_cmpr"
@@ -552,22 +651,17 @@
 			     : "g1");
 }
 
-static void smp_tickoffset_init(void)
+__initfunc(static void smp_tickoffset_init(void))
 {
-	int node;
-
-	node = linux_cpus[0].prom_node;
-	real_tick_offset = prom_getint(node, "clock-frequency");
-	real_tick_offset = real_tick_offset / HZ;
-	current_tick_offset = real_tick_offset;
+	current_tick_offset = timer_tick_offset;
 }
 
-int setup_profiling_timer(unsigned int multiplier)
+__initfunc(int setup_profiling_timer(unsigned int multiplier))
 {
 	unsigned long flags;
 	int i;
 
-	if((!multiplier) || (real_tick_offset / multiplier) < 1000)
+	if((!multiplier) || (timer_tick_offset / multiplier) < 1000)
 		return -EINVAL;
 
 	save_and_cli(flags);
@@ -575,7 +669,7 @@
 		if(cpu_present_map & (1UL << i))
 			prof_multiplier(i) = multiplier;
 	}
-	current_tick_offset = (real_tick_offset / multiplier);
+	current_tick_offset = (timer_tick_offset / multiplier);
 	restore_flags(flags);
 
 	return 0;
diff -u --recursive --new-file v2.1.96/linux/arch/sparc64/kernel/sparc64_ksyms.c linux/arch/sparc64/kernel/sparc64_ksyms.c
--- v2.1.96/linux/arch/sparc64/kernel/sparc64_ksyms.c	Mon Jan 12 15:15:44 1998
+++ linux/arch/sparc64/kernel/sparc64_ksyms.c	Tue Apr 14 17:44:21 1998
@@ -1,4 +1,4 @@
-/* $Id: sparc64_ksyms.c,v 1.27 1997/11/19 07:57:46 jj Exp $
+/* $Id: sparc64_ksyms.c,v 1.33 1998/04/06 16:09:40 jj Exp $
  * arch/sparc64/kernel/sparc64_ksyms.c: Sparc64 specific ksyms support.
  *
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
@@ -50,6 +50,7 @@
 	short revents;
 };
 
+extern unsigned prom_cpu_nodes[NR_CPUS];
 extern void die_if_kernel(char *str, struct pt_regs *regs);
 extern unsigned long sunos_mmap(unsigned long, unsigned long, unsigned long,
 				unsigned long, unsigned long, unsigned long);
@@ -112,6 +113,8 @@
 #else
 EXPORT_SYMBOL(local_irq_count);
 #endif
+EXPORT_SYMBOL(enable_irq);
+EXPORT_SYMBOL(disable_irq);
 EXPORT_SYMBOL_PRIVATE(_lock_kernel);
 EXPORT_SYMBOL_PRIVATE(_unlock_kernel);
 
@@ -134,7 +137,9 @@
 #endif
 #if CONFIG_PCI
 EXPORT_SYMBOL(ebus_chain);
-EXPORT_SYMBOL(pci_devices);
+EXPORT_SYMBOL(pci_dvma_offset);
+EXPORT_SYMBOL(pci_dvma_mask);
+EXPORT_SYMBOL(empty_zero_page);
 #endif
 
 /* Solaris/SunOS binary compatibility */
@@ -171,7 +176,6 @@
 
 /* sparc library symbols */
 EXPORT_SYMBOL(bcopy);
-EXPORT_SYMBOL(memscan);
 EXPORT_SYMBOL(strlen);
 EXPORT_SYMBOL(strnlen);
 EXPORT_SYMBOL(strcpy);
@@ -179,7 +183,6 @@
 EXPORT_SYMBOL(strcat);
 EXPORT_SYMBOL(strncat);
 EXPORT_SYMBOL(strcmp);
-EXPORT_SYMBOL(strncmp);
 EXPORT_SYMBOL(strchr);
 EXPORT_SYMBOL(strrchr);
 EXPORT_SYMBOL(strpbrk);
@@ -201,13 +204,9 @@
 EXPORT_SYMBOL(sys_getppid);
 EXPORT_SYMBOL(svr4_getcontext);
 EXPORT_SYMBOL(svr4_setcontext);
-EXPORT_SYMBOL(linux_cpus);
+EXPORT_SYMBOL(prom_cpu_nodes);
 EXPORT_SYMBOL(sys_ioctl);
 EXPORT_SYMBOL(sys32_ioctl);
-#endif
-
-#ifdef CONFIG_MATHEMU_MODULE
-EXPORT_SYMBOL(handle_mathemu);
 #endif
 
 /* Special internal versions of library functions. */
diff -u --recursive --new-file v2.1.96/linux/arch/sparc64/kernel/sunos_ioctl32.c linux/arch/sparc64/kernel/sunos_ioctl32.c
--- v2.1.96/linux/arch/sparc64/kernel/sunos_ioctl32.c	Mon Jan 12 15:15:44 1998
+++ linux/arch/sparc64/kernel/sunos_ioctl32.c	Tue Apr 14 17:44:21 1998
@@ -1,4 +1,4 @@
-/* $Id: sunos_ioctl32.c,v 1.5 1997/09/18 10:37:57 rth Exp $
+/* $Id: sunos_ioctl32.c,v 1.9 1998/03/29 10:10:53 davem Exp $
  * sunos_ioctl32.c: SunOS ioctl compatability on sparc64.
  *
  * Copyright (C) 1995 Miguel de Icaza (miguel@nuclecu.unam.mx)
@@ -18,6 +18,7 @@
 #include <linux/netdevice.h>
 #include <linux/if_arp.h>
 #include <linux/fs.h>
+#include <linux/file.h>
 #include <linux/mm.h>
 #include <linux/smp.h>
 #include <linux/smp_lock.h>
@@ -88,15 +89,12 @@
 
 asmlinkage int sunos_ioctl (int fd, u32 cmd, u32 arg)
 {
-	struct file *filp;
 	int ret = -EBADF;
 
 	lock_kernel();
 	if(fd >= SUNOS_NR_OPEN)
 		goto out;
-
-	filp = current->files->fd[fd];
-	if(!filp)
+	if(!fcheck(fd))
 		goto out;
 
 	if(cmd == TIOCSETD) {
@@ -168,7 +166,7 @@
 		ret = sys32_ioctl(fd, SIOCGIFBRDADDR, arg);
 		goto out;
 	case _IOW('i', 24, struct ifreq32):
-		ret = sys32_ioctl(fd, SIOCGIFBRDADDR, arg);
+		ret = sys32_ioctl(fd, SIOCSIFBRDADDR, arg);
 		goto out;
 	case _IOWR('i', 25, struct ifreq32):
 		ret = sys32_ioctl(fd, SIOCGIFNETMASK, arg);
diff -u --recursive --new-file v2.1.96/linux/arch/sparc64/kernel/sys32.S linux/arch/sparc64/kernel/sys32.S
--- v2.1.96/linux/arch/sparc64/kernel/sys32.S	Mon Jan 12 15:15:44 1998
+++ linux/arch/sparc64/kernel/sys32.S	Tue Apr 14 17:44:21 1998
@@ -1,4 +1,4 @@
-/* $Id: sys32.S,v 1.4 1997/09/09 17:13:29 jj Exp $
+/* $Id: sys32.S,v 1.5 1998/03/24 05:57:56 ecd Exp $
  * sys32.S: I-cache tricks for 32-bit compatability layer simple
  *          conversions.
  *
@@ -24,7 +24,7 @@
 
 	.align		32
 	.globl		sys32_lseek
-	.globl		sys32_chmod, sys32_chown, sys32_mknod
+	.globl		sys32_chmod, sys32_chown, sys32_lchown, sys32_mknod
 sys32_lseek:
 	sra		%o1, 0, %o1
 	mov		%o7, %g1
@@ -45,6 +45,15 @@
 	srl		%o1, 16, %o1
 	srl		%o2, 16, %o2
 	call		sys_chown
+	 mov		%g1, %o7
+sys32_lchown:
+	sll		%o1, 16, %o1
+	mov		%o7, %g1
+	sll		%o2, 16, %o2
+	srl		%o0, 0, %o0
+	srl		%o1, 16, %o1
+	srl		%o2, 16, %o2
+	call		sys_lchown
 	 mov		%g1, %o7
 sys32_mknod:
 	sll		%o2, 16, %o2
diff -u --recursive --new-file v2.1.96/linux/arch/sparc64/kernel/sys_sparc.c linux/arch/sparc64/kernel/sys_sparc.c
--- v2.1.96/linux/arch/sparc64/kernel/sys_sparc.c	Mon Jan 12 15:15:44 1998
+++ linux/arch/sparc64/kernel/sys_sparc.c	Tue Apr 14 17:44:21 1998
@@ -1,4 +1,4 @@
-/* $Id: sys_sparc.c,v 1.9 1997/12/11 15:15:44 jj Exp $
+/* $Id: sys_sparc.c,v 1.13 1998/03/29 10:10:52 davem Exp $
  * linux/arch/sparc64/kernel/sys_sparc.c
  *
  * This file contains various random system calls that
@@ -10,6 +10,7 @@
 #include <linux/types.h>
 #include <linux/sched.h>
 #include <linux/fs.h>
+#include <linux/file.h>
 #include <linux/mm.h>
 #include <linux/sem.h>
 #include <linux/msg.h>
@@ -40,7 +41,7 @@
 	unsigned long ret;
 
 	lock_kernel();
-	if(brk >= 0x80000000000ULL) {	/* VM hole */
+	if(brk >= 0x80000000000UL) {	/* VM hole */
 		ret = current->mm->brk;
 		goto out;
 	}
@@ -128,6 +129,16 @@
 	if (call <= SHMCTL) 
 		switch (call) {
 		case SHMAT:
+			if (first >= 0) {
+				extern struct shmid_ds *shm_segs[];
+				struct shmid_ds *shp = shm_segs[(unsigned int) first % SHMMNI];
+				if (shp == IPC_UNUSED || shp == IPC_NOID) {
+					err = -ENOMEM;
+					if ((unsigned long)ptr >= 0x80000000000UL - shp->shm_segsz &&
+					    (unsigned long)ptr < 0xfffff80000000000UL)
+						goto out; /* Somebody is trying to fool us */
+				}
+			}
 			err = sys_shmat (first, (char *) ptr, second, (ulong *) third);
 			goto out;
 		case SHMDT:
@@ -162,29 +173,39 @@
 
 	lock_kernel();
 	if (!(flags & MAP_ANONYMOUS)) {
-		if (fd >= NR_OPEN || !(file = current->files->fd[fd])){
+		file = fget(fd);
+		if (!file)
 			goto out;
-	    }
 	}
 	retval = -ENOMEM;
+	len = PAGE_ALIGN(len);
 	if(!(flags & MAP_FIXED) && !addr) {
 		addr = get_unmapped_area(addr, len);
-		if(!addr){
-			goto out;
-		}
+		if(!addr)
+			goto out_putf;
 	}
 
-	/* See asm-sparc64/uaccess.h */
 	retval = -EINVAL;
-	if((len > (TASK_SIZE - PAGE_SIZE)) || (addr > (TASK_SIZE-len-PAGE_SIZE)))
-		goto out;
-
-	if(addr >= 0x80000000000ULL) {
-		retval = current->mm->brk;
-		goto out;
+	if (current->tss.flags & SPARC_FLAG_32BIT) {
+		if (len > 0xf0000000UL || addr > 0xf0000000UL - len)
+			goto out_putf;
+	} else {
+		if (len >= 0x80000000000UL || 
+		    (addr < 0x80000000000UL &&
+		     addr > 0x80000000000UL-len))
+			goto out_putf;
+		if (addr >= 0x80000000000ULL && addr < 0xfffff80000000000UL) {
+			/* VM hole */
+			retval = current->mm->brk;
+			goto out_putf;
+		}
 	}
 
 	retval = do_mmap(file, addr, len, prot, flags, off);
+
+out_putf:
+	if (file)
+		fput(file);
 out:
 	unlock_kernel();
 	return retval;
diff -u --recursive --new-file v2.1.96/linux/arch/sparc64/kernel/sys_sparc32.c linux/arch/sparc64/kernel/sys_sparc32.c
--- v2.1.96/linux/arch/sparc64/kernel/sys_sparc32.c	Mon Jan 12 15:15:44 1998
+++ linux/arch/sparc64/kernel/sys_sparc32.c	Tue Apr 14 17:44:21 1998
@@ -1,7 +1,7 @@
-/* $Id: sys_sparc32.c,v 1.71 1997/12/11 15:15:11 jj Exp $
+/* $Id: sys_sparc32.c,v 1.77 1998/03/29 10:10:50 davem Exp $
  * sys_sparc32.c: Conversion between 32bit and 64bit native syscalls.
  *
- * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
  * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
  *
  * These routines maintain argument size conversion between 32bit and 64bit
@@ -10,11 +10,12 @@
 
 #include <linux/config.h>
 #include <linux/kernel.h>
+#include <linux/sched.h>
 #include <linux/fs.h> 
+#include <linux/file.h> 
 #include <linux/signal.h>
 #include <linux/utime.h>
 #include <linux/resource.h>
-#include <linux/sched.h>
 #include <linux/times.h>
 #include <linux/utime.h>
 #include <linux/utsname.h>
@@ -41,6 +42,7 @@
 #include <linux/module.h>
 #include <linux/poll.h>
 #include <linux/personality.h>
+#include <linux/stat.h>
 
 #include <asm/types.h>
 #include <asm/ipc.h>
@@ -59,7 +61,6 @@
 extern char * getname_quicklist;
 extern int getname_quickcount;
 extern struct semaphore getname_quicklock;
-extern int kerneld_msqid;
 
 /* Tuning: increase locality by reusing same pages again...
  * if getname_quicklist becomes too long on low memory machines, either a limit
@@ -324,19 +325,9 @@
 				if (!p) err = -ENOMEM;
 				else {
 					err = 0;
-					if (first == kerneld_msqid) {
-						*(int *)p->mtext = 0;
-						if (get_user(p->mtype, &(((struct msgbuf32 *)A(ptr))->mtype)) ||
-						    __copy_from_user(&p->mtext[4], &(((struct msgbuf32 *)A(ptr))->mtext[0]), 4) ||
-						    __copy_from_user(&p->mtext[8], &(((struct msgbuf32 *)A(ptr))->mtext[4]), second-4))
-							err = -EFAULT;
-						else
-							second += 4;
-					} else {
-						if (get_user(p->mtype, &(((struct msgbuf32 *)A(ptr))->mtype)) ||
-						    __copy_from_user(p->mtext, &(((struct msgbuf32 *)A(ptr))->mtext), second))
-							err = -EFAULT;
-					}
+					if (get_user(p->mtype, &(((struct msgbuf32 *)A(ptr))->mtype)) ||
+					    __copy_from_user(p->mtext, &(((struct msgbuf32 *)A(ptr))->mtext), second))
+						err = -EFAULT;
 					if (!err) {
 						mm_segment_t old_fs = get_fs();
 						set_fs (KERNEL_DS);
@@ -379,18 +370,9 @@
 				if (err < 0)
 					goto out;
 
-				if (first == kerneld_msqid) {
-					if (put_user (p->mtype, &(((struct msgbuf32 *)A(ptr))->mtype)) ||
-					    __copy_to_user(&(((struct msgbuf32 *)A(ptr))->mtext[0]), &p->mtext[4], 4) ||
-					    __copy_to_user(&(((struct msgbuf32 *)A(ptr))->mtext[4]), &p->mtext[8], err-8))
-						err = -EFAULT;
-					else
-						err -= 4;
-				} else {
-					if (put_user (p->mtype, &(((struct msgbuf32 *)A(ptr))->mtype)) ||
-					    __copy_to_user(&(((struct msgbuf32 *)A(ptr))->mtext), p->mtext, err))
-						err = -EFAULT;
-				}
+				if (put_user (p->mtype, &(((struct msgbuf32 *)A(ptr))->mtype)) ||
+				    __copy_to_user(&(((struct msgbuf32 *)A(ptr))->mtext), p->mtext, err))
+					err = -EFAULT;
 				kfree (p);
 				goto out;
 			}
@@ -939,14 +921,12 @@
 {
 	int error = -EBADF;
 	struct file * file;
+	struct inode * inode;
 	struct readdir_callback32 buf;
 
 	lock_kernel();
-	if(fd >= NR_OPEN)
-		goto out;
-
-	file = current->files->fd[fd];
-	if(!file)
+	file = fget(fd);
+	if (!file)
 		goto out;
 
 	buf.count = 0;
@@ -954,12 +934,18 @@
 
 	error = -ENOTDIR;
 	if (!file->f_op || !file->f_op->readdir)
-		goto out;
-
+		goto out_putf;
+	
+	inode = file->f_dentry->d_inode;
+	down(&inode->i_sem);
 	error = file->f_op->readdir(file, &buf, fillonedir);
+	up(&inode->i_sem);
 	if (error < 0)
-		goto out;
+		goto out_putf;
 	error = buf.count;
+
+out_putf:
+	fput(file);
 out:
 	unlock_kernel();
 	return error;
@@ -1006,16 +992,14 @@
 asmlinkage int sys32_getdents(unsigned int fd, u32 dirent, unsigned int count)
 {
 	struct file * file;
+	struct inode * inode;
 	struct linux_dirent32 * lastdirent;
 	struct getdents_callback32 buf;
 	int error = -EBADF;
 
 	lock_kernel();
-	if(fd >= NR_OPEN)
-		goto out;
-
-	file = current->files->fd[fd];
-	if(!file)
+	file = fget(fd);
+	if (!file)
 		goto out;
 
 	buf.current_dir = (struct linux_dirent32 *) A(dirent);
@@ -1025,17 +1009,22 @@
 
 	error = -ENOTDIR;
 	if (!file->f_op || !file->f_op->readdir)
-		goto out;
+		goto out_putf;
 
+	inode = file->f_dentry->d_inode;
+	down(&inode->i_sem);
 	error = file->f_op->readdir(file, &buf, filldir);
+	up(&inode->i_sem);
 	if (error < 0)
-		goto out;
+		goto out_putf;
 	lastdirent = buf.previous;
 	error = buf.error;
 	if(lastdirent) {
 		put_user(file->f_pos, &lastdirent->d_off);
 		error = count - buf.count;
 	}
+out_putf:
+	fput(file);
 out:
 	unlock_kernel();
 	return error;
@@ -1258,6 +1247,27 @@
 	return ret;
 }
 
+extern asmlinkage int sys_xstat(int ver, char *filename, struct stat64 * statbuf);
+
+asmlinkage int sys32_xstat(int ver, u32 file, u32 statbuf)
+{
+	switch (ver & __XSTAT_VER_MASK) {
+	case __XSTAT_VER_1:
+		switch (ver & __XSTAT_VER_TYPEMASK) {
+		case __XSTAT_VER_XSTAT:
+			return sys32_newstat(file, statbuf);
+		case __XSTAT_VER_LXSTAT:
+			return sys32_newlstat(file, statbuf);
+		case __XSTAT_VER_FXSTAT:
+			return sys32_newfstat(file, statbuf);
+		}
+		return -EINVAL;
+	case __XSTAT_VER_2:
+		return sys_xstat(ver, (char *)A(file), (struct stat64 *)A(statbuf));
+	}
+	return -EINVAL;
+}
+
 extern asmlinkage int sys_sysfs(int option, unsigned long arg1, unsigned long arg2);
 
 asmlinkage int sys32_sysfs(int option, u32 arg1, u32 arg2)
@@ -2222,18 +2232,19 @@
 	char address[MAX_SOCK_ADDR];
 	struct iovec iov[UIO_FASTIOV];
 	unsigned char ctl[sizeof(struct cmsghdr) + 20];
-	struct msghdr kern_msg;
-	int err;
-	int total_len;
 	unsigned char *ctl_buf = ctl;
+	struct msghdr kern_msg;
+	int err, total_len;
 
 	if(msghdr_from_user32_to_kern(&kern_msg, (struct msghdr32 *)A(user_msg)))
 		return -EFAULT;
 	if(kern_msg.msg_iovlen > UIO_MAXIOV)
 		return -EINVAL;
-	total_len = verify_iovec32(&kern_msg, iov, address, VERIFY_READ);
-	if(total_len < 0)
-		return total_len;
+	err = verify_iovec32(&kern_msg, iov, address, VERIFY_READ);
+	if (err < 0)
+		goto out;
+	total_len = err;
+
 	if(kern_msg.msg_controllen) {
 		struct cmsghdr32 *ucmsg = (struct cmsghdr32 *)kern_msg.msg_control;
 		unsigned long *kcmsg;
@@ -2241,41 +2252,40 @@
 
 		if(kern_msg.msg_controllen > sizeof(ctl) &&
 		   kern_msg.msg_controllen <= 256) {
+			err = -ENOBUFS;
 			ctl_buf = kmalloc(kern_msg.msg_controllen, GFP_KERNEL);
-			if(!ctl_buf) {
-				if(kern_msg.msg_iov != iov)
-					kfree(kern_msg.msg_iov);
-				return -ENOBUFS;
-			}
+			if(!ctl_buf)
+				goto out_freeiov;
 		}
 		__get_user(cmlen, &ucmsg->cmsg_len);
 		kcmsg = (unsigned long *) ctl_buf;
 		*kcmsg++ = (unsigned long)cmlen;
+		err = -EFAULT;
 		if(copy_from_user(kcmsg, &ucmsg->cmsg_level,
-				  kern_msg.msg_controllen - sizeof(__kernel_size_t32))) {
-			if(ctl_buf != ctl)
-				kfree_s(ctl_buf, kern_msg.msg_controllen);
-			if(kern_msg.msg_iov != iov)
-				kfree(kern_msg.msg_iov);
-			return -EFAULT;
-		}
+				  kern_msg.msg_controllen - sizeof(__kernel_size_t32)))
+			goto out_freectl;
 		kern_msg.msg_control = ctl_buf;
 	}
 	kern_msg.msg_flags = user_flags;
 
 	lock_kernel();
-	if(current->files->fd[fd]->f_flags & O_NONBLOCK)
-		kern_msg.msg_flags |= MSG_DONTWAIT;
-	if((sock = sockfd_lookup(fd, &err)) != NULL) {
+	sock = sockfd_lookup(fd, &err);
+	if (sock != NULL) {
+		if (sock->file->f_flags & O_NONBLOCK)
+			kern_msg.msg_flags |= MSG_DONTWAIT;
 		err = sock_sendmsg(sock, &kern_msg, total_len);
 		sockfd_put(sock);
 	}
 	unlock_kernel();
 
+out_freectl:
+	/* N.B. Use kfree here, as kern_msg.msg_controllen might change? */
 	if(ctl_buf != ctl)
-		kfree_s(ctl_buf, kern_msg.msg_controllen);
+		kfree(ctl_buf);
+out_freeiov:
 	if(kern_msg.msg_iov != iov)
 		kfree(kern_msg.msg_iov);
+out:
 	return err;
 }
 
@@ -2299,17 +2309,18 @@
 	uaddr = kern_msg.msg_name;
 	uaddr_len = &((struct msghdr32 *)A(user_msg))->msg_namelen;
 	err = verify_iovec32(&kern_msg, iov, addr, VERIFY_WRITE);
-	if(err < 0)
-		return err;
+	if (err < 0)
+		goto out;
 	total_len = err;
 
 	cmsg_ptr = (unsigned long) kern_msg.msg_control;
 	kern_msg.msg_flags = 0;
 
 	lock_kernel();
-	if(current->files->fd[fd]->f_flags & O_NONBLOCK)
-		user_flags |= MSG_DONTWAIT;
-	if((sock = sockfd_lookup(fd, &err)) != NULL) {
+	sock = sockfd_lookup(fd, &err);
+	if (sock != NULL) {
+		if (sock->file->f_flags & O_NONBLOCK)
+			user_flags |= MSG_DONTWAIT;
 		err = sock_recvmsg(sock, &kern_msg, total_len, user_flags);
 		if(err >= 0)
 			len = err;
@@ -2317,8 +2328,6 @@
 	}
 	unlock_kernel();
 
-	if(kern_msg.msg_iov != iov)
-		kfree(kern_msg.msg_iov);
 	if(uaddr != NULL && err >= 0)
 		err = move_addr_to_user(addr, kern_msg.msg_namelen, uaddr, uaddr_len);
 	if(err >= 0) {
@@ -2330,6 +2339,10 @@
 					 &((struct msghdr32 *)A(user_msg))->msg_controllen);
 		}
 	}
+
+	if(kern_msg.msg_iov != iov)
+		kfree(kern_msg.msg_iov);
+out:
 	if(err < 0)
 		return err;
 	return len;
@@ -2838,25 +2851,25 @@
 #else /* CONFIG_MODULES */
 
 asmlinkage unsigned long
-sys_create_module(const char *name_user, size_t size)
+sys32_create_module(const char *name_user, size_t size)
 {
 	return -ENOSYS;
 }
 
 asmlinkage int
-sys_init_module(const char *name_user, struct module *mod_user)
+sys32_init_module(const char *name_user, struct module *mod_user)
 {
 	return -ENOSYS;
 }
 
 asmlinkage int
-sys_delete_module(const char *name_user)
+sys32_delete_module(const char *name_user)
 {
 	return -ENOSYS;
 }
 
 asmlinkage int
-sys_query_module(const char *name_user, int which, char *buf, size_t bufsize,
+sys32_query_module(const char *name_user, int which, char *buf, size_t bufsize,
 		 size_t *ret)
 {
 	/* Let the program know about the new interface.  Not that
@@ -2868,7 +2881,7 @@
 }
 
 asmlinkage int
-sys_get_kernel_syms(struct kernel_sym *table)
+sys32_get_kernel_syms(struct kernel_sym *table)
 {
 	return -ENOSYS;
 }
@@ -3326,4 +3339,20 @@
 				  __kernel_size_t32 count, u32 pos)
 {
 	return sys_pwrite(fd, (char *) A(ubuf), count, pos);
+}
+
+
+extern asmlinkage int sys_personality(unsigned long);
+
+asmlinkage int sys32_personality(unsigned long personality)
+{
+	int ret;
+	lock_kernel();
+	if (current->personality == PER_LINUX32 && personality == PER_LINUX)
+		personality = PER_LINUX32;
+	ret = sys_personality(personality);
+	unlock_kernel();
+	if (ret == PER_LINUX32)
+		ret = PER_LINUX;
+	return ret;
 }
diff -u --recursive --new-file v2.1.96/linux/arch/sparc64/kernel/sys_sunos32.c linux/arch/sparc64/kernel/sys_sunos32.c
--- v2.1.96/linux/arch/sparc64/kernel/sys_sunos32.c	Mon Jan 12 15:15:44 1998
+++ linux/arch/sparc64/kernel/sys_sunos32.c	Tue Apr 14 17:44:21 1998
@@ -1,4 +1,4 @@
-/* $Id: sys_sunos32.c,v 1.7 1997/12/11 15:15:19 jj Exp $
+/* $Id: sys_sunos32.c,v 1.11 1998/03/29 10:10:55 davem Exp $
  * sys_sunos32.c: SunOS binary compatability layer on sparc64.
  *
  * Copyright (C) 1995, 1996, 1997 David S. Miller (davem@caip.rutgers.edu)
@@ -16,6 +16,7 @@
 #include <linux/mm.h>
 #include <linux/swap.h>
 #include <linux/fs.h>
+#include <linux/file.h>
 #include <linux/resource.h>
 #include <linux/ipc.h>
 #include <linux/shm.h>
@@ -70,23 +71,30 @@
 		flags &= ~MAP_NORESERVE;
 	}
 	retval = -EBADF;
-	if(!(flags & MAP_ANONYMOUS))
-		if(fd >= SUNOS_NR_OPEN || !(file = current->files->fd[fd]))
+	if(!(flags & MAP_ANONYMOUS)) {
+		if(fd >= SUNOS_NR_OPEN)
 			goto out;
+ 		file = fget(fd);
+		if (!file)
+			goto out;
+		if (file->f_dentry && file->f_dentry->d_inode) {
+			struct inode * inode = file->f_dentry->d_inode;
+			if(MAJOR(inode->i_rdev) == MEM_MAJOR &&
+			   MINOR(inode->i_rdev) == 5) {
+				flags |= MAP_ANONYMOUS;
+				fput(file);
+				file = NULL;
+			}
+		}
+	}
+
 	retval = -ENOMEM;
 	if(!(flags & MAP_FIXED) && !addr) {
 		unsigned long attempt = get_unmapped_area(addr, len);
 		if(!attempt || (attempt >= 0xf0000000UL))
-			goto out;
+			goto out_putf;
 		addr = (u32) attempt;
 	}
-	if(file->f_dentry && file->f_dentry->d_inode) {
-		if(MAJOR(file->f_dentry->d_inode->i_rdev) == MEM_MAJOR &&
-		   MINOR(file->f_dentry->d_inode->i_rdev) == 5) {
-			flags |= MAP_ANONYMOUS;
-			file = 0;
-		}
-	}
 	if(!(flags & MAP_FIXED))
 		addr = 0;
 	ret_type = flags & _MAP_NEW;
@@ -98,6 +106,9 @@
 			 (unsigned long) off);
 	if(!ret_type)
 		retval = ((retval < 0xf0000000) ? 0 : retval);
+out_putf:
+	if (file)
+		fput(file);
 out:
 	unlock_kernel();
 	return (u32) retval;
@@ -372,6 +383,7 @@
 asmlinkage int sunos_getdents(unsigned int fd, u32 u_dirent, int cnt)
 {
 	struct file * file;
+	struct inode * inode;
 	struct sunos_dirent * lastdirent;
 	struct sunos_dirent_callback buf;
 	int error = -EBADF;
@@ -381,32 +393,39 @@
 	if(fd >= SUNOS_NR_OPEN)
 		goto out;
 
-	file = current->files->fd[fd];
+	file = fget(fd);
 	if(!file)
 		goto out;
 
 	error = -ENOTDIR;
 	if (!file->f_op || !file->f_op->readdir)
-		goto out;
+		goto out_putf;
 
 	error = -EINVAL;
 	if(cnt < (sizeof(struct sunos_dirent) + 255))
-		goto out;
+		goto out_putf;
 
 	buf.curr = (struct sunos_dirent *) dirent;
 	buf.previous = NULL;
 	buf.count = cnt;
 	buf.error = 0;
 
+	inode = file->f_dentry->d_inode;
+	down(&inode->i_sem);
 	error = file->f_op->readdir(file, &buf, sunos_filldir);
+	up(&inode->i_sem);
 	if (error < 0)
-		goto out;
+		goto out_putf;
+
 	lastdirent = buf.previous;
 	error = buf.error;
 	if (lastdirent) {
 		put_user(file->f_pos, &lastdirent->d_off);
 		error = cnt - buf.count;
 	}
+
+out_putf:
+	fput(file);
 out:
 	unlock_kernel();
 	return error;
@@ -454,43 +473,51 @@
 asmlinkage int sunos_getdirentries(unsigned int fd, u32 u_dirent,
 				   int cnt, u32 u_basep)
 {
+	void *dirent = (void *) A(u_dirent);
+	unsigned int *basep = (unsigned int *)A(u_basep);
 	struct file * file;
+	struct inode * inode;
 	struct sunos_direntry * lastdirent;
-	struct sunos_direntry_callback buf;
 	int error = -EBADF;
-	void *dirent = (void *) A(u_dirent);
-	unsigned int *basep = (unsigned int *)A(u_basep);
+	struct sunos_direntry_callback buf;
 
 	lock_kernel();
 	if(fd >= SUNOS_NR_OPEN)
 		goto out;
 
-	file = current->files->fd[fd];
+	file = fget(fd);
 	if(!file)
 		goto out;
 
 	error = -ENOTDIR;
 	if (!file->f_op || !file->f_op->readdir)
-		goto out;
+		goto out_putf;
 
 	error = -EINVAL;
 	if(cnt < (sizeof(struct sunos_direntry) + 255))
-		goto out;
+		goto out_putf;
 
 	buf.curr = (struct sunos_direntry *) dirent;
 	buf.previous = NULL;
 	buf.count = cnt;
 	buf.error = 0;
 
+	inode = file->f_dentry->d_inode;
+	down(&inode->i_sem);
 	error = file->f_op->readdir(file, &buf, sunos_filldirentry);
+	up(&inode->i_sem);
 	if (error < 0)
-		goto out;
+		goto out_putf;
+
 	lastdirent = buf.previous;
 	error = buf.error;
 	if (lastdirent) {
 		put_user(file->f_pos, basep);
 		error = cnt - buf.count;
 	}
+
+out_putf:
+	fput(file);
 out:
 	unlock_kernel();
 	return error;
@@ -622,14 +649,28 @@
 extern asmlinkage int
 sys32_select(int n, u32 inp, u32 outp, u32 exp, u32 tvp);
 
-asmlinkage int sunos_select(int width, u32 inp, u32 outp, u32 exp, u32 tvp)
+struct timeval32
+{
+	int tv_sec, tv_usec;
+};
+
+asmlinkage int sunos_select(int width, u32 inp, u32 outp, u32 exp, u32 tvp_x)
 {
 	int ret;
 
 	/* SunOS binaries expect that select won't change the tvp contents */
 	lock_kernel();
 	current->personality |= STICKY_TIMEOUTS;
-	ret = sys32_select (width, inp, outp, exp, tvp);
+	ret = sys32_select (width, inp, outp, exp, tvp_x);
+	if (ret == -EINTR && tvp_x) {
+		struct timeval32 *tvp = (struct timeval32 *)A(tvp_x);
+		time_t sec, usec;
+
+		__get_user(sec, &tvp->tv_sec);
+		__get_user(usec, &tvp->tv_usec);
+		if (sec == 0 && usec == 0)
+			ret = 0;
+	}
 	unlock_kernel();
 	return ret;
 }
@@ -1297,8 +1338,11 @@
 
 static inline int check_nonblock(int ret, int fd)
 {
-	if (ret == -EAGAIN && (current->files->fd[fd]->f_flags & O_NDELAY))
-		return -SUNOS_EWOULDBLOCK;
+	if (ret == -EAGAIN) {
+		struct file * file = fcheck(fd);
+		if (file && (file->f_flags & O_NDELAY))
+			ret = -SUNOS_EWOULDBLOCK;
+	}
 	return ret;
 }
 
@@ -1370,12 +1414,42 @@
 	return ret;
 }
 
+extern asmlinkage int sys_setsockopt(int fd, int level, int optname,
+				     char *optval, int optlen);
+
+asmlinkage int sunos_socket(int family, int type, int protocol)
+{
+	int ret, one = 1;
+
+	lock_kernel();
+	ret = sys_socket(family, type, protocol);
+	if (ret < 0)
+		goto out;
+
+	sys_setsockopt(ret, SOL_SOCKET, SO_BSDCOMPAT,
+		       (char *)&one, sizeof(one));
+out:
+	unlock_kernel();
+	return ret;
+}
+
 asmlinkage int sunos_accept(int fd, u32 sa, u32 addrlen)
 {
-	int ret;
+	int ret, one = 1;
 
 	lock_kernel();
-	ret = check_nonblock(sys_accept(fd, (struct sockaddr *)A(sa), (int *)A(addrlen)), fd);
+	while (1) {
+		ret = check_nonblock(sys_accept(fd, (struct sockaddr *)A(sa),
+						(int *)A(addrlen)), fd);
+		if (ret != -ENETUNREACH && ret != -EHOSTUNREACH)
+			break;
+	}
+	if (ret < 0)
+		goto out;
+
+	sys_setsockopt(ret, SOL_SOCKET, SO_BSDCOMPAT,
+		       (char *)&one, sizeof(one));
+out:
 	unlock_kernel();
 	return ret;
 }
diff -u --recursive --new-file v2.1.96/linux/arch/sparc64/kernel/systbls.S linux/arch/sparc64/kernel/systbls.S
--- v2.1.96/linux/arch/sparc64/kernel/systbls.S	Mon Jan 12 15:15:44 1998
+++ linux/arch/sparc64/kernel/systbls.S	Tue Apr 14 17:44:21 1998
@@ -1,4 +1,4 @@
-/* $Id: systbls.S,v 1.37 1997/12/24 17:27:31 ecd Exp $
+/* $Id: systbls.S,v 1.41 1998/03/24 05:57:57 ecd Exp $
  * systbls.S: System call entry point tables for OS compatibility.
  *            The native Linux system call table lives here also.
  *
@@ -19,8 +19,8 @@
 sys_call_table32:
 /*0*/	.word sys_setup, sys_exit, sys_fork, sys_read, sys_write
 /*5*/	.word sys_open, sys_close, sys32_wait4, sys_creat, sys_link
-/*10*/  .word sys_unlink, sunos_execv, sys_chdir, sys_nis_syscall, sys32_mknod
-/*15*/	.word sys32_chmod, sys32_chown, sparc_brk, sys_nis_syscall, sys32_lseek
+/*10*/  .word sys_unlink, sunos_execv, sys_chdir, sys32_xstat, sys32_mknod
+/*15*/	.word sys32_chmod, sys32_lchown, sparc_brk, sys_xmknod, sys32_lseek
 /*20*/	.word sys_getpid, sys_nis_syscall, sys_nis_syscall, sys_setuid, sys_getuid
 /*25*/	.word sys_time, sys_ptrace, sys_alarm, sys_nis_syscall, sys32_pause
 /*30*/	.word sys32_utime, sys_nis_syscall, sys_nis_syscall, sys_access, sys_nice
@@ -55,7 +55,7 @@
 	.word sys_setsid, sys_fchdir, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall
 /*180*/	.word sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys32_sigpending, sys32_query_module
 	.word sys_setpgid, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys32_newuname
-/*190*/	.word sys32_init_module, sys_personality, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall
+/*190*/	.word sys32_init_module, sys32_personality, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall
 	.word sys_nis_syscall, sys_nis_syscall, sys_getppid, sys32_sigaction, sys_sgetmask
 /*200*/	.word sys_ssetmask, sys_sigsuspend, sys32_newlstat, sys_uselib, old32_readdir
 	.word sys_nis_syscall, sys32_socketcall, sys_syslog, sys_nis_syscall, sys_nis_syscall
@@ -78,8 +78,8 @@
 sys_call_table:
 /*0*/	.word sys_setup, sys_exit, sys_fork, sys_read, sys_write
 /*5*/	.word sys_open, sys_close, sys_wait4, sys_creat, sys_link
-/*10*/  .word sys_unlink, sunos_execv, sys_chdir, sys_nis_syscall, sys_mknod
-/*15*/	.word sys_chmod, sys_chown, sparc_brk, sys_nis_syscall, sys_lseek
+/*10*/  .word sys_unlink, sunos_execv, sys_chdir, sys_xstat, sys_mknod
+/*15*/	.word sys_chmod, sys_lchown, sparc_brk, sys_xmknod, sys_lseek
 /*20*/	.word sys_getpid, sys_nis_syscall, sys_nis_syscall, sys_setuid, sys_getuid
 /*25*/	.word sys_time, sys_ptrace, sys_alarm, sys_nis_syscall, sys_nis_syscall
 /*30*/	.word sys_utime, sys_nis_syscall, sys_nis_syscall, sys_access, sys_nice
@@ -117,7 +117,7 @@
 /*190*/	.word sys_init_module, sys_personality, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall
 	.word sys_nis_syscall, sys_nis_syscall, sys_getppid, sys_sigaction, sys_sgetmask
 /*200*/	.word sys_ssetmask, sys_sigsuspend, sys_newlstat, sys_uselib, sys_nis_syscall
-	.word sys_nis_syscall, sys_nis_syscall, sys_syslog, sys_nis_syscall, sys_nis_syscall
+	.word sys_nis_syscall, sys_socketcall, sys_syslog, sys_nis_syscall, sys_nis_syscall
 /*210*/	.word sys_idle, sys_nis_syscall, sys_waitpid, sys_swapoff, sys_sysinfo
 	.word sys_ipc, sys_sigreturn, sys_clone, sys_nis_syscall, sys_adjtimex
 /*220*/	.word sys_sigprocmask, sys_create_module, sys_delete_module, sys_get_kernel_syms, sys_getpgid
@@ -139,7 +139,7 @@
 	.word sys_close, sunos_wait4, sys_creat
 	.word sys_link, sys_unlink, sunos_execv
 	.word sys_chdir, sunos_nosys, sys32_mknod
-	.word sys32_chmod, sys32_chown, sunos_brk
+	.word sys32_chmod, sys32_lchown, sunos_brk
 	.word sunos_nosys, sys32_lseek, sunos_getpid
 	.word sunos_nosys, sunos_nosys, sunos_nosys
 	.word sunos_getuid, sunos_nosys, sys_ptrace
@@ -166,7 +166,7 @@
 	.word sys32_getitimer, sys_gethostname, sys_sethostname
 	.word sunos_getdtablesize, sys_dup2, sunos_nop
 	.word sys32_fcntl, sunos_select, sunos_nop
-	.word sys_fsync, sys_setpriority, sys_socket
+	.word sys_fsync, sys_setpriority, sunos_socket
 	.word sys_connect, sunos_accept
 /*100*/	.word sys_getpriority, sunos_send, sunos_recv
 	.word sunos_nosys, sys_bind, sunos_setsockopt
diff -u --recursive --new-file v2.1.96/linux/arch/sparc64/kernel/time.c linux/arch/sparc64/kernel/time.c
--- v2.1.96/linux/arch/sparc64/kernel/time.c	Thu Sep  4 12:54:48 1997
+++ linux/arch/sparc64/kernel/time.c	Tue Apr 14 17:44:21 1998
@@ -1,7 +1,8 @@
-/* $Id: time.c,v 1.12 1997/08/22 20:12:13 davem Exp $
+/* $Id: time.c,v 1.13 1998/03/15 17:23:47 ecd Exp $
  * time.c: UltraSparc timer and TOD clock support.
  *
  * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1998 Eddie C. Dost   (ecd@skynet.be)
  *
  * Based largely on code which is:
  *
@@ -42,28 +43,63 @@
  * NOTE: On SUN5 systems the ticker interrupt comes in using 2
  *       interrupts, one at level14 and one with softint bit 0.
  */
-extern struct sun5_timer *linux_timers;
+unsigned long timer_tick_offset;
+static unsigned long timer_tick_compare;
+static unsigned long timer_ticks_per_usec;
 
-static void timer_interrupt(int irq, void *dev_id, struct pt_regs * regs)
+static __inline__ void timer_check_rtc(void)
 {
 	/* last time the cmos clock got updated */
 	static long last_rtc_update=0;
 
-	__asm__ __volatile__("ldx	[%0], %%g0"
-			     : /* no outputs */
-			     : "r" (&((linux_timers)->limit0)));
-
-	do_timer(regs);
-
 	/* Determine when to update the Mostek clock. */
 	if (time_state != TIME_BAD && xtime.tv_sec > last_rtc_update + 660 &&
 	    xtime.tv_usec > 500000 - (tick >> 1) &&
-	    xtime.tv_usec < 500000 + (tick >> 1))
-	  if (set_rtc_mmss(xtime.tv_sec) == 0)
-	    last_rtc_update = xtime.tv_sec;
-	  else
-	    last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */
+	    xtime.tv_usec < 500000 + (tick >> 1)) {
+		if (set_rtc_mmss(xtime.tv_sec) == 0)
+			last_rtc_update = xtime.tv_sec;
+		else
+			last_rtc_update = xtime.tv_sec - 600;
+			/* do it again in 60 s */
+	}
+}
+
+static void timer_interrupt(int irq, void *dev_id, struct pt_regs * regs)
+{
+	unsigned long ticks;
+
+	do {
+		do_timer(regs);
+
+		__asm__ __volatile__("
+			rd	%%tick_cmpr, %0
+			add	%0, %2, %0
+			wr	%0, 0, %%tick_cmpr
+			rd	%%tick, %1"
+			: "=&r" (timer_tick_compare), "=r" (ticks)
+			: "r" (timer_tick_offset));
+	} while (ticks >= timer_tick_compare);
+
+	timer_check_rtc();
+}
+
+#ifdef __SMP__
+void timer_tick_interrupt(struct pt_regs *regs)
+{
+	do_timer(regs);
+
+	/*
+	 * Only keep timer_tick_offset uptodate, but don't set TICK_CMPR.
+	 */
+	__asm__ __volatile__("
+		rd	%%tick_cmpr, %0
+		add	%0, %1, %0"
+		: "=&r" (timer_tick_compare)
+		: "r" (timer_tick_offset));
+
+	timer_check_rtc();
 }
+#endif
 
 /* Converts Gregorian date to seconds since 1970-01-01 00:00:00.
  * Assumes input in normal date format, i.e. 1980-12-31 23:59:59
@@ -318,29 +354,32 @@
 	 */
 }
 
-extern void init_timers(void (*func)(int, void *, struct pt_regs *));
+extern void init_timers(void (*func)(int, void *, struct pt_regs *),
+			unsigned long *);
 
 __initfunc(void sun4u_start_timers(void))
 {
-	init_timers(timer_interrupt);
+	unsigned long clock;
+
+	init_timers(timer_interrupt, &clock);
+	timer_tick_offset = clock / HZ;
+	timer_ticks_per_usec = clock / 1000000;
 }
 
 static __inline__ unsigned long do_gettimeoffset(void)
 {
-	unsigned long offset = 0;
-	unsigned int count;
-
-	/* XXX -DaveM */
-#if 0
-	count = (*master_l10_counter >> 10) & 0x1fffff;
-#else
-	count = 0;
-#endif
+	unsigned long ticks;
 
-	if(test_bit(TIMER_BH, &bh_active))
-		offset = 1000000;
+	__asm__ __volatile__("
+		rd	%%tick, %%g1
+		add	%1, %%g1, %0
+		sub	%0, %2, %0
+"
+		: "=r" (ticks)
+		: "r" (timer_tick_offset), "r" (timer_tick_compare)
+		: "g1", "g2");
 
-	return offset + count;
+	return ticks / timer_ticks_per_usec;
 }
 
 void do_gettimeofday(struct timeval *tv)
@@ -353,13 +392,16 @@
 	 * nucleus atomic quad 128-bit loads.
 	 */
 	__asm__ __volatile__("
-	sethi	%hi(linux_timers), %o1
+	sethi	%hi(timer_tick_offset), %g3
 	sethi	%hi(xtime), %g2
-	ldx	[%o1 + %lo(linux_timers)], %g3
+	sethi	%hi(timer_tick_compare), %g1
+	ldx	[%g3 + %lo(timer_tick_offset)], %g3
 	or	%g2, %lo(xtime), %g2
+	or	%g1, %lo(timer_tick_compare), %g1
 1:	ldda	[%g2] 0x24, %o4
 	membar	#LoadLoad | #MemIssue
-	ldx	[%g3], %o1
+	rd	%tick, %o1
+	ldx	[%g1], %g7
 	membar	#LoadLoad | #MemIssue
 	ldda	[%g2] 0x24, %o2
 	membar	#LoadLoad
@@ -367,24 +409,28 @@
 	xor	%o5, %o3, %o3
 	orcc	%o2, %o3, %g0
 	bne,pn	%xcc, 1b
-	 cmp	%o1, 0
-	bge,pt	%icc, 1f
-	 sethi	%hi(tick), %o3
-	ldx	[%o3 + %lo(tick)], %o3
-	sethi	%hi(0x1fffff), %o2
-	or	%o2, %lo(0x1fffff), %o2
-	add	%o5, %o3, %o5
-	and	%o1, %o2, %o1
-1:	add	%o5, %o1, %o5
-	sethi	%hi(1000000), %o2
+	 sethi	%hi(lost_ticks), %o2
+	sethi	%hi(timer_ticks_per_usec), %o3
+	ldx	[%o2 + %lo(lost_ticks)], %o2
+	add	%g3, %o1, %o1
+	ldx	[%o3 + %lo(timer_ticks_per_usec)], %o3
+	sub	%o1, %g7, %o1
+	brz,pt	%o2, 1f
+	 udivx	%o1, %o3, %o1
+	sethi	%hi(10000), %g2
+	or	%g2, %lo(10000), %g2
+	add	%o1, %g2, %o1
+1:	sethi	%hi(1000000), %o2
+	srlx	%o5, 32, %o5
 	or	%o2, %lo(1000000), %o2
+	add	%o5, %o1, %o5
 	cmp	%o5, %o2
 	bl,a,pn	%xcc, 1f
 	 stx	%o4, [%o0 + 0x0]
 	add	%o4, 0x1, %o4
 	sub	%o5, %o2, %o5
 	stx	%o4, [%o0 + 0x0]
-1:	stx	%o5, [%o0 + 0x8]");
+1:	st	%o5, [%o0 + 0x8]");
 }
 
 void do_settimeofday(struct timeval *tv)
@@ -401,6 +447,7 @@
 	time_state = TIME_BAD;
 	time_maxerror = 0x70000000;
 	time_esterror = 0x70000000;
+
 	sti();
 }
 
diff -u --recursive --new-file v2.1.96/linux/arch/sparc64/kernel/trampoline.S linux/arch/sparc64/kernel/trampoline.S
--- v2.1.96/linux/arch/sparc64/kernel/trampoline.S	Sat Aug 16 09:51:09 1997
+++ linux/arch/sparc64/kernel/trampoline.S	Tue Apr 14 17:44:21 1998
@@ -1,4 +1,4 @@
-/* $Id: trampoline.S,v 1.2 1997/07/28 02:57:32 davem Exp $
+/* $Id: trampoline.S,v 1.3 1998/02/22 21:06:11 jj Exp $
  * trampoline.S: Jump start slave processors on sparc64.
  *
  * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
@@ -172,8 +172,10 @@
 	mov	%o2, %g6
 
 	wrpr	%o1, (PSTATE_MG | PSTATE_IE), %pstate
-	sethi	%hi(0x1ff8), %g2
-	or	%g2, %lo(0x1ff8), %g2
+#define KERN_HIGHBITS ((_PAGE_VALID | _PAGE_SZ4MB) ^ 0xfffff80000000000)
+	sethi   %uhi(KERN_HIGHBITS), %g2
+	sllx    %g2, 32, %g2
+#undef KERN_HIGHBITS
 	ldx	[%o2 + AOFF_task_mm], %g6
 	ldx	[%g6 + AOFF_mm_pgd], %g6
 	clr	%g7
diff -u --recursive --new-file v2.1.96/linux/arch/sparc64/kernel/traps.c linux/arch/sparc64/kernel/traps.c
--- v2.1.96/linux/arch/sparc64/kernel/traps.c	Tue Mar 17 22:18:14 1998
+++ linux/arch/sparc64/kernel/traps.c	Tue Apr 14 17:44:21 1998
@@ -1,4 +1,4 @@
-/* $Id: traps.c,v 1.44 1998/01/09 16:39:35 jj Exp $
+/* $Id: traps.c,v 1.49 1998/04/06 16:09:38 jj Exp $
  * arch/sparc64/kernel/traps.c
  *
  * Copyright (C) 1995,1997 David S. Miller (davem@caip.rutgers.edu)
@@ -191,7 +191,7 @@
 		die_if_kernel ("Kernel bad trap", regs);
         current->tss.sig_desc = SUBSIG_BADTRAP(lvl - 0x100);
         current->tss.sig_address = regs->tpc;
-        send_sig(SIGILL, current, 1);
+        force_sig(SIGILL, current);
 	unlock_kernel ();
 }
 
@@ -225,7 +225,9 @@
 			return;
 		}
 	}
-	send_sig(SIGSEGV, current, 1);
+	lock_kernel();
+	force_sig(SIGSEGV, current);
+	unlock_kernel();
 }
 
 #ifdef CONFIG_PCI
@@ -235,16 +237,35 @@
 extern volatile int pci_poke_faulted;
 #endif
 
+/* When access exceptions happen, we must do this. */
+static __inline__ void clean_and_reenable_l1_caches(void)
+{
+	unsigned long va;
+
+	/* Clean 'em. */
+	for(va =  0; va < (PAGE_SIZE << 1); va += 32) {
+		spitfire_put_icache_tag(va, 0x0);
+		spitfire_put_dcache_tag(va, 0x0);
+	}
+
+	/* Re-enable. */
+	__asm__ __volatile__("flush %%g6\n\t"
+			     "membar #Sync\n\t"
+			     "stxa %0, [%%g0] %1\n\t"
+			     "membar #Sync"
+			     : /* no outputs */
+			     : "r" (LSU_CONTROL_IC | LSU_CONTROL_DC |
+				    LSU_CONTROL_IM | LSU_CONTROL_DM),
+			     "i" (ASI_LSU_CONTROL)
+			     : "memory");
+}
+
 void do_dae(struct pt_regs *regs)
 {
 #ifdef CONFIG_PCI
-#ifdef DEBUG_PCI_POKES
-	prom_printf(" (POKE ");
-#endif
 	if(pci_poke_in_progress) {
-		unsigned long va;
 #ifdef DEBUG_PCI_POKES
-		prom_printf("tpc[%016lx] tnpc[%016lx] ",
+		prom_printf(" (POKE tpc[%016lx] tnpc[%016lx] ",
 			    regs->tpc, regs->tnpc);
 #endif
 		pci_poke_faulted = 1;
@@ -255,39 +276,30 @@
 		prom_printf("PCI) ");
 		/* prom_halt(); */
 #endif
-		/* Re-enable I/D caches, Ultra turned them off. */
-		for(va =  0; va < (PAGE_SIZE << 1); va += 32) {
-			spitfire_put_icache_tag(va, 0x0);
-			spitfire_put_dcache_tag(va, 0x0);
-		}
-		__asm__ __volatile__("flush %%g6\n\t"
-				     "membar #Sync\n\t"
-				     "stxa %0, [%%g0] %1\n\t"
-				     "membar #Sync"
-				     : /* no outputs */
-				     : "r" (LSU_CONTROL_IC | LSU_CONTROL_DC |
-					    LSU_CONTROL_IM | LSU_CONTROL_DM),
-				       "i" (ASI_LSU_CONTROL)
-				     : "memory");
+		clean_and_reenable_l1_caches();
 		return;
 	}
-#ifdef DEBUG_PCI_POKES
-	prom_printf("USER) ");
-	prom_printf("tpc[%016lx] tnpc[%016lx]\n");
-	prom_halt();
 #endif
-#endif
-	send_sig(SIGSEGV, current, 1);
+	clean_and_reenable_l1_caches();
+	lock_kernel();
+	force_sig(SIGSEGV, current);
+	unlock_kernel();
 }
 
 void instruction_access_exception (struct pt_regs *regs)
 {
-	send_sig(SIGSEGV, current, 1);
+	clean_and_reenable_l1_caches();
+
+	lock_kernel();
+	force_sig(SIGSEGV, current);
+	unlock_kernel();
 }
 
 void do_iae(struct pt_regs *regs)
 {
-	send_sig(SIGSEGV, current, 1);
+	lock_kernel();
+	force_sig(SIGSEGV, current);
+	unlock_kernel();
 }
 
 void do_fpe_common(struct pt_regs *regs)
@@ -312,11 +324,7 @@
 	do_fpe_common(regs);
 }
 
-#ifdef CONFIG_MATHEMU_MODULE
-volatile int (*handle_mathemu)(struct pt_regs *, struct fpustate *) = NULL;
-#else
 extern int do_mathemu(struct pt_regs *, struct fpustate *);
-#endif
 
 void do_fpother(struct pt_regs *regs)
 {
@@ -326,18 +334,7 @@
 	switch ((f->fsr & 0x1c000)) {
 	case (2 << 14): /* unfinished_FPop */
 	case (3 << 14): /* unimplemented_FPop */
-#ifdef CONFIG_MATHEMU_MODULE
-#ifdef CONFIG_KMOD
-		if (!handle_mathemu)
-			request_module("math-emu");
-#endif
-		if (handle_mathemu)
-			ret = handle_mathemu(regs, f);
-#else
-#ifdef CONFIG_MATHEMU
 		ret = do_mathemu(regs, f);
-#endif
-#endif
 		break;
 	}
 	if (ret) return;
@@ -576,27 +573,33 @@
 #else
 #error SMP not supported on sparc64 yet
 #endif
+
+#if 0
+/* Broken */
 	int size = prom_getintdefault(node, "ecache-size", 512*1024);
 	int i, j;
-	unsigned long addr, page_nr;
+	unsigned long addr;
+	struct page *page, *end;
 
 	regs->tpc = regs->tnpc;
 	regs->tnpc = regs->tnpc + 4;
 	if (!suser()) return;
 	size >>= PAGE_SHIFT;
 	addr = PAGE_OFFSET - PAGE_SIZE;
+	page = mem_map - 1;
+	end = mem_map + max_mapnr;
 	for (i = 0; i < size; i++) {
 		do {
 			addr += PAGE_SIZE;
-			page_nr = MAP_NR(addr);
-			if (page_nr >= max_mapnr) {
+			page++;
+			if (page >= end)
 				return;
-			}
-		} while (!PageReserved (mem_map + page_nr));
+		} while (!PageReserved(page));
 		/* E-Cache line size is 64B. Let us pollute it :)) */
 		for (j = 0; j < PAGE_SIZE; j += 64)
 			__asm__ __volatile__ ("ldx [%0 + %1], %%g1" : : "r" (j), "r" (addr) : "g1");
 	}
+#endif
 }
 #endif
 
diff -u --recursive --new-file v2.1.96/linux/arch/sparc64/kernel/ttable.S linux/arch/sparc64/kernel/ttable.S
--- v2.1.96/linux/arch/sparc64/kernel/ttable.S	Mon Jan 12 15:15:44 1998
+++ linux/arch/sparc64/kernel/ttable.S	Tue Apr 14 17:44:21 1998
@@ -1,4 +1,4 @@
-/* $Id: ttable.S,v 1.22 1997/10/16 07:07:46 jj Exp $
+/* $Id: ttable.S,v 1.23 1998/03/15 17:23:48 ecd Exp $
  * ttable.S: Sparc V9 Trap Table(s) with SpitFire extensions.
  *
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
@@ -6,7 +6,7 @@
 
 #include <linux/config.h>
 
-	.globl	sparc64_ttable_tl0, sparc64_ttable_tl1
+	.globl	sparc64_ttable_tl0, tl0_itick, sparc64_ttable_tl1,
 
 sparc64_ttable_tl0:
 tl0_resv000:	BOOT_KERNEL BTRAP(0x1) BTRAP(0x2) BTRAP(0x3)
@@ -45,7 +45,7 @@
 tl0_irq9:	TRAP_IRQ(handler_irq, 9)  TRAP_IRQ(handler_irq, 10)
 tl0_irq11:	TRAP_IRQ(handler_irq, 11) TRAP_IRQ(handler_irq, 12)
 tl0_irq13:	TRAP_IRQ(handler_irq, 13)
-tl0_itick:	TRAP_TICK
+tl0_itick:	TRAP_IRQ(handler_irq, 14)
 tl0_irq15:	TRAP_IRQ(handler_irq, 15)
 tl0_resv050:	BTRAP(0x50) BTRAP(0x51) BTRAP(0x52) BTRAP(0x53) BTRAP(0x54) BTRAP(0x55)
 tl0_resv056:	BTRAP(0x56) BTRAP(0x57) BTRAP(0x58) BTRAP(0x59) BTRAP(0x5a) BTRAP(0x5b)
diff -u --recursive --new-file v2.1.96/linux/arch/sparc64/lib/VIScsumcopy.S linux/arch/sparc64/lib/VIScsumcopy.S
--- v2.1.96/linux/arch/sparc64/lib/VIScsumcopy.S	Thu Sep  4 12:54:48 1997
+++ linux/arch/sparc64/lib/VIScsumcopy.S	Tue Apr 14 17:44:21 1998
@@ -1,4 +1,4 @@
-/* $Id: VIScsumcopy.S,v 1.2 1997/08/19 15:25:22 jj Exp $
+/* $Id: VIScsumcopy.S,v 1.4 1998/04/01 08:29:52 davem Exp $
  * VIScsumcopy.S: High bandwidth IP checksumming with simultaneous
  *            copying utilizing the UltraSparc Visual Instruction Set.
  *
@@ -393,22 +393,22 @@
 	add		%src, 128, %src		/*  IEU0	Group			*/
 	ldda		[%src-128] %asi, %f0	/*  Load	Group			*/
 	ldda		[%src-64] %asi, %f16	/*  Load	Group			*/
-	fmovd		%f48, %f62		/*  FPA		Group			*/
-	faligndata	%f0, %f2, %f48		/*  FPA		Group			*/
-	fcmpgt32	%f32, %f2, %x1		/*  FPM		Group			*/
+	fmovd		%f48, %f62		/*  FPA		Group	f0 available	*/
+	faligndata	%f0, %f2, %f48		/*  FPA		Group	f2 available	*/
+	fcmpgt32	%f32, %f2, %x1		/*  FPM		Group	f4 available	*/
 	fpadd32		%f0, %f62, %f0		/*  FPA					*/
-	fcmpgt32	%f32, %f4, %x2		/*  FPM		Group			*/
+	fcmpgt32	%f32, %f4, %x2		/*  FPM		Group	f6 available	*/
 	faligndata	%f2, %f4, %f50		/*  FPA					*/
-	fcmpgt32	%f62, %f0, %x3		/*  FPM		Group			*/
+	fcmpgt32	%f62, %f0, %x3		/*  FPM		Group	f8 available	*/
 	faligndata	%f4, %f6, %f52		/*  FPA					*/
-	fcmpgt32	%f32, %f6, %x4		/*  FPM		Group			*/
+	fcmpgt32	%f32, %f6, %x4		/*  FPM		Group	f10 available	*/
 	inc		%x1			/*  IEU0				*/
 	faligndata	%f6, %f8, %f54		/*  FPA					*/
-	fcmpgt32	%f32, %f8, %x5		/*  FPM		Group			*/
+	fcmpgt32	%f32, %f8, %x5		/*  FPM		Group	f12 available	*/
 	srl		%x1, 1, %x1		/*  IEU0				*/
 	inc		%x2			/*  IEU1				*/
 	faligndata	%f8, %f10, %f56		/*  FPA					*/
-	fcmpgt32	%f32, %f10, %x6		/*  FPM		Group			*/
+	fcmpgt32	%f32, %f10, %x6		/*  FPM		Group	f14 available	*/
 	srl		%x2, 1, %x2		/*  IEU0				*/
 	add		%sum, %x1, %sum		/*  IEU1				*/
 	faligndata	%f10, %f12, %f58	/*  FPA					*/
@@ -451,6 +451,7 @@
 	add		%src, 128 - 8, %src	/*  IEU0	Group			*/
 	ldda		[%src-128] %asi, %f0	/*  Load	Group			*/
 	ldda		[%src-64] %asi, %f16	/*  Load	Group			*/
+	fmovd		%f0, %f58		/*  FPA		Group			*/
 	fmovd		%f48, %f0		/*  FPA		Group			*/
 	fcmpgt32	%f32, %f2, %x2		/*  FPM		Group			*/
 	faligndata	%f2, %f4, %f48		/*  FPA					*/
@@ -503,9 +504,10 @@
 	add		%src, 128 - 16, %src	/*  IEU0	Group			*/
 	ldda		[%src-128] %asi, %f0	/*  Load	Group			*/
 	ldda		[%src-64] %asi, %f16	/*  Load	Group			*/
+	fmovd		%f0, %f56		/*  FPA		Group			*/
 	fmovd		%f48, %f0		/*  FPA		Group			*/	
 	sub		%dst, 64, %dst		/*  IEU0				*/
-	fzero		%f2			/*  FPA		Group			*/
+	fpsub32		%f2, %f2, %f2		/*  FPA		Group			*/
 	fcmpgt32	%f32, %f4, %x3		/*  FPM		Group			*/
 	faligndata	%f4, %f6, %f48		/*  FPA					*/
 	fcmpgt32	%f32, %f6, %x4		/*  FPM		Group			*/
@@ -552,10 +554,11 @@
 	add		%src, 128 - 24, %src	/*  IEU0	Group			*/
 	ldda		[%src-128] %asi, %f0	/*  Load	Group			*/
 	ldda		[%src-64] %asi, %f16	/*  Load	Group			*/
+	fmovd		%f0, %f54		/*  FPA		Group			*/
 	fmovd		%f48, %f0		/*  FPA		Group			*/
 	sub		%dst, 64, %dst		/*  IEU0				*/
-	fzero		%f2			/*  FPA		Group			*/
-	fzero		%f4			/*  FPA		Group			*/
+	fpsub32		%f2, %f2, %f2		/*  FPA		Group			*/
+	fpsub32		%f4, %f4, %f4		/*  FPA		Group			*/
 	fcmpgt32	%f32, %f6, %x4		/*  FPM		Group			*/
 	faligndata	%f6, %f8, %f48		/*  FPA					*/
 	fcmpgt32	%f32, %f8, %x5		/*  FPM		Group			*/
@@ -597,11 +600,12 @@
 	add		%src, 128 - 32, %src	/*  IEU0	Group			*/
 	ldda		[%src-128] %asi, %f0	/*  Load	Group			*/
 	ldda		[%src-64] %asi, %f16	/*  Load	Group			*/
+	fmovd		%f0, %f52		/*  FPA		Group			*/
 	fmovd		%f48, %f0		/*  FPA		Group			*/
 	sub		%dst, 64, %dst		/*  IEU0				*/
-	fzero		%f2			/*  FPA		Group			*/
-	fzero		%f4			/*  FPA		Group			*/
-	fzero		%f6			/*  FPA		Group			*/
+	fpsub32		%f2, %f2, %f2		/*  FPA		Group			*/
+	fpsub32		%f4, %f4, %f4		/*  FPA		Group			*/
+	fpsub32		%f6, %f6, %f6		/*  FPA		Group			*/
 	clr		%x4			/*  IEU0				*/
 	fcmpgt32	%f32, %f8, %x5		/*  FPM		Group			*/
 	faligndata	%f8, %f10, %f48		/*  FPA					*/
@@ -697,9 +701,9 @@
 	clr		%x6			/*  IEU0				*/
 	fcmpgt32	%f32, %f12, %x7		/*  FPM		Group			*/
 	sub		%dst, 64, %dst		/*  IEU0				*/
-	faligndata	%f12, %f14, %f48	/*  FPA					*/
 	fcmpgt32	%f32, %f14, %x8		/*  FPM		Group			*/
-	fmovd		%f14, %f50		/*  FPA					*/
+	faligndata	%f12, %f14, %f48	/*  FPA					*/
+	fmovd		%f14, %f50		/*  FPA		Group			*/
 vis6:	DO_THE_TRICK(	f0,f2,f4,f6,f8,f10,f12,f14,f16,f18,f20,f22,f24,f26,f28,f30,	
 			,f52,f54,f56,f58,f60,f62,f48,f50,f50,
 			,LDBLK(f32),	,,,,,,STBLK,,
diff -u --recursive --new-file v2.1.96/linux/arch/sparc64/math-emu/Makefile linux/arch/sparc64/math-emu/Makefile
--- v2.1.96/linux/arch/sparc64/math-emu/Makefile	Mon Jan 12 15:15:44 1998
+++ linux/arch/sparc64/math-emu/Makefile	Tue Apr 14 17:44:21 1998
@@ -1,5 +1,5 @@
 #
-# Makefile for the FPU Quad (long double) instruction emulation.
+# Makefile for the FPU instruction emulation.
 #
 # Note! Dependencies are done automagically by 'make dep', which also
 # removes any old dependencies. DON'T put your own dependencies here
@@ -16,18 +16,10 @@
 		fmuls.o fmuld.o fdivs.o fdivd.o fsmuld.o		\
 		fstoi.o fdtoi.o fstox.o fdtox.o fstod.o fdtos.o
 
-ifeq ($(CONFIG_MATHEMU),m)
-M_OBJS   := $(O_TARGET)
-endif
-
 .S.s:
 	$(CPP) -D__ASSEMBLY__ -ansi $< -o $*.s
 
 .S.o:
 	$(CC) -D__ASSEMBLY__ -ansi -c $< -o $*.o
-
-ifneq ($(CONFIG_MATHEMU),y)
-do_it_all:
-endif
 
 include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v2.1.96/linux/arch/sparc64/math-emu/double.h linux/arch/sparc64/math-emu/double.h
--- v2.1.96/linux/arch/sparc64/math-emu/double.h	Mon Jan 12 15:15:44 1998
+++ linux/arch/sparc64/math-emu/double.h	Tue Apr 14 17:44:21 1998
@@ -3,7 +3,7 @@
  */
 
 #if _FP_W_TYPE_SIZE < 32
-#error "Here's a nickle kid.  Go buy yourself a real computer."
+#error "Here's a nickel kid.  Go buy yourself a real computer."
 #endif
 
 #if _FP_W_TYPE_SIZE < 64
diff -u --recursive --new-file v2.1.96/linux/arch/sparc64/math-emu/fabsq.c linux/arch/sparc64/math-emu/fabsq.c
--- v2.1.96/linux/arch/sparc64/math-emu/fabsq.c	Mon Jan 12 15:15:44 1998
+++ linux/arch/sparc64/math-emu/fabsq.c	Tue Apr 14 17:44:21 1998
@@ -1,18 +1,5 @@
-#include "soft-fp.h"
-#include "quad.h"
-
 int FABSQ(unsigned long *rd, unsigned long *rs2)
 {
-/*
-	FP_DECL_Q(A); FP_DECL_Q(R);
-
-	__FP_UNPACK_Q(A, rs2);
-	_FP_FRAC_COPY_2(R, A);
-	R_c = A_c;
-	R_e = A_e;
-	R_s = 0;
-	__FP_PACK_Q(rd, R);
- */
 	rd[0] = rs2[0] & 0x7fffffffffffffffUL;
 	rd[1] = rs2[1];
 	return 1;
diff -u --recursive --new-file v2.1.96/linux/arch/sparc64/math-emu/fcmpeq.c linux/arch/sparc64/math-emu/fcmpeq.c
--- v2.1.96/linux/arch/sparc64/math-emu/fcmpeq.c	Mon Jan 12 15:15:44 1998
+++ linux/arch/sparc64/math-emu/fcmpeq.c	Tue Apr 14 17:44:21 1998
@@ -11,11 +11,8 @@
 	rd = (void *)(((long)rd)&~3);
 	__FP_UNPACK_Q(A, rs1);
 	__FP_UNPACK_Q(B, rs2);
-	FP_CMP_Q(ret, A, B, 3);
-	switch (ret) {
-	case 1: ret = 2; break;
-	case -1: ret = 1; break;
-	}
+	FP_CMP_Q(ret, B, A, 3);
+	if (ret == -1) ret = 2;
 	fsr = *(unsigned long *)rd;
 	switch (fccno) {
 	case 0: fsr &= ~0xc00; fsr |= (ret << 10); break;
diff -u --recursive --new-file v2.1.96/linux/arch/sparc64/math-emu/fcmpq.c linux/arch/sparc64/math-emu/fcmpq.c
--- v2.1.96/linux/arch/sparc64/math-emu/fcmpq.c	Mon Jan 12 15:15:44 1998
+++ linux/arch/sparc64/math-emu/fcmpq.c	Tue Apr 14 17:44:21 1998
@@ -11,11 +11,8 @@
 	rd = (void *)(((long)rd)&~3);
 	__FP_UNPACK_Q(A, rs1);
 	__FP_UNPACK_Q(B, rs2);
-	FP_CMP_Q(ret, A, B, 3);
-	switch (ret) {
-	case 1: ret = 2; break;
-	case -1: ret = 1; break;
-	}
+	FP_CMP_Q(ret, B, A, 3);
+	if (ret == -1) ret = 2;
 	fsr = *(unsigned long *)rd;
 	switch (fccno) {
 	case 0: fsr &= ~0xc00; fsr |= (ret << 10); break;
diff -u --recursive --new-file v2.1.96/linux/arch/sparc64/math-emu/fnegq.c linux/arch/sparc64/math-emu/fnegq.c
--- v2.1.96/linux/arch/sparc64/math-emu/fnegq.c	Mon Jan 12 15:15:44 1998
+++ linux/arch/sparc64/math-emu/fnegq.c	Tue Apr 14 17:44:21 1998
@@ -1,18 +1,7 @@
-#include "soft-fp.h"
-#include "quad.h"
-
 int FNEGQ(unsigned long *rd, unsigned long *rs2)
 {
-/*
-	FP_DECL_Q(A); FP_DECL_Q(R);
-
-	__FP_UNPACK_Q(A, rs2);
-	FP_NEG_Q(R, A);
-	__FP_PACK_Q(rd, R);
- */
 	rd[0] = rs2[0] ^ 0x8000000000000000UL;
 	rd[1] = rs2[1];
 	return 1;
 }
 
-                
diff -u --recursive --new-file v2.1.96/linux/arch/sparc64/math-emu/math.c linux/arch/sparc64/math-emu/math.c
--- v2.1.96/linux/arch/sparc64/math-emu/math.c	Mon Jan 12 15:15:44 1998
+++ linux/arch/sparc64/math-emu/math.c	Tue Apr 14 17:44:21 1998
@@ -1,4 +1,4 @@
-/* $Id: math.c,v 1.3 1997/10/15 07:28:55 jj Exp $
+/* $Id: math.c,v 1.4 1998/04/06 16:09:57 jj Exp $
  * arch/sparc64/math-emu/math.c
  *
  * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
@@ -7,7 +7,6 @@
  * of glibc and has appropriate copyrights in it.
  */
 
-#include <linux/module.h>
 #include <linux/types.h>
 #include <linux/sched.h>
 
@@ -70,7 +69,6 @@
 
 	if(tstate & TSTATE_PRIV)
 		die_if_kernel("FPQuad from kernel", regs);
-	MOD_INC_USE_COUNT;
 	if(current->tss.flags & SPARC_FLAG_32BIT)
 		pc = (u32)pc;
 	if (get_user(insn, (u32 *)pc) != -EFAULT) {
@@ -182,28 +180,7 @@
 		func(rd, rs2, rs1);
 		regs->tpc = regs->tnpc;
 		regs->tnpc += 4;
-		MOD_DEC_USE_COUNT;
 		return 1;
 	}
-err:	MOD_DEC_USE_COUNT;
-	return 0;
+err:	return 0;
 }
-
-#ifdef MODULE
-
-MODULE_AUTHOR("Jakub Jelinek (jj@sunsite.mff.cuni.cz), Richard Henderson (rth@cygnus.com)");
-MODULE_DESCRIPTION("FPU emulation module");
-
-extern int (*handle_mathemu)(struct pt_regs *, struct fpustate *);
-
-int init_module(void)
-{
-	handle_mathemu = do_mathemu;
-	return 0;
-}
-
-void cleanup_module(void)
-{
-	handle_mathemu = NULL;
-}
-#endif
diff -u --recursive --new-file v2.1.96/linux/arch/sparc64/math-emu/op-2.h linux/arch/sparc64/math-emu/op-2.h
--- v2.1.96/linux/arch/sparc64/math-emu/op-2.h	Mon Jan 12 15:15:44 1998
+++ linux/arch/sparc64/math-emu/op-2.h	Tue Apr 14 17:44:21 1998
@@ -207,6 +207,12 @@
     R##_f1 = _FP_FRAC_WORD_4(_z,1);					\
   } while (0)
 
+/* This next macro appears to be totally broken. Fortunately nowhere
+ * seems to use it :-> The problem is that we define _z[4] but
+ * then use it in _FP_FRAC_SRS_4, which will attempt to access
+ * _z_f[n] which will cause an error. The fix probably involves 
+ * declaring it with _FP_FRAC_DECL_4, see previous macro. -- PMM 02/1998 
+ */
 #define _FP_MUL_MEAT_2_gmp(fs, R, X, Y)					\
   do {									\
     _FP_W_TYPE _x[2], _y[2], _z[4];					\
@@ -226,6 +232,11 @@
 
 /*
  * Division algorithms:
+ * This seems to be giving me difficulties -- PMM 
+ * Look, NetBSD seems to be able to comment algorithms. Can't you?
+ * I've thrown printks at the problem.
+ * This now appears to work, but I still don't really know why.
+ * Also, I don't think the result is properly normalised...
  */
 
 #define _FP_DIV_MEAT_2_udiv_64(fs, R, X, Y)				\
@@ -236,10 +247,17 @@
     _FP_W_TYPE _n_f3, _n_f2, _n_f1, _n_f0, _r_f1, _r_f0;		\
     _FP_W_TYPE _q_f1, _q_f0, _m_f1, _m_f0;				\
     _FP_W_TYPE _rmem[2], _qmem[2];					\
-									\
+    /* I think this check is to ensure that the result is normalised.   \
+     * Assuming X,Y normalised (ie in [1.0,2.0)) X/Y will be in         \
+     * [0.5,2.0). Furthermore, it will be less than 1.0 iff X < Y.      \
+     * In this case we tweak things. (this is based on comments in      \
+     * the NetBSD FPU emulation code. )                                 \
+     * We know X,Y are normalised because we ensure this as part of     \
+     * the unpacking process. -- PMM                                    \
+     */									\
     if (_FP_FRAC_GT_2(X, Y))						\
       {									\
-	R##_e++;							\
+/*	R##_e++; */							\
 	_n_f3 = X##_f1 >> 1;						\
 	_n_f2 = X##_f1 << (_FP_W_TYPE_SIZE - 1) | X##_f0 >> 1;		\
 	_n_f1 = X##_f0 << (_FP_W_TYPE_SIZE - 1);			\
@@ -247,14 +265,15 @@
       }									\
     else								\
       {									\
+	R##_e--;							\
 	_n_f3 = X##_f1;							\
 	_n_f2 = X##_f0;							\
 	_n_f1 = _n_f0 = 0;						\
       }									\
 									\
     /* Normalize, i.e. make the most significant bit of the 		\
-       denominator set.  */						\
-    _FP_FRAC_SLL_2(Y, _FP_WFRACXBITS_##fs - 1);				\
+       denominator set.  CHANGED: - 1 to nothing -- PMM */		\
+    _FP_FRAC_SLL_2(Y, _FP_WFRACXBITS_##fs /* -1 */);			\
 									\
     /* Do the 256/128 bit division given the 128-bit _fp_udivmodtf4 	\
        primitive snagged from libgcc2.c.  */				\
@@ -295,6 +314,11 @@
 									\
     R##_f1 = _q_f1;							\
     R##_f0 = _q_f0 | ((_r_f1 | _r_f0) != 0);				\
+    /* adjust so answer is normalized again. I'm not sure what the 	\
+     * final sz param should be. In practice it's never used since      \
+     * N is 1 which is always going to be < _FP_W_TYPE_SIZE...		\
+     */									\
+    /* _FP_FRAC_SRS_2(R,1,_FP_WFRACBITS_##fs);	*/			\
   } while (0)
 
 
@@ -406,3 +430,4 @@
     D##_f1 = 0;								\
     _FP_FRAC_SLL_2(D, (_FP_WFRACBITS_##dfs - _FP_WFRACBITS_##sfs));	\
   } while (0)
+
diff -u --recursive --new-file v2.1.96/linux/arch/sparc64/math-emu/op-4.h linux/arch/sparc64/math-emu/op-4.h
--- v2.1.96/linux/arch/sparc64/math-emu/op-4.h	Mon Jan 12 15:15:44 1998
+++ linux/arch/sparc64/math-emu/op-4.h	Tue Apr 14 17:44:21 1998
@@ -1,11 +1,41 @@
 /*
  * Basic four-word fraction declaration and manipulation.
+ *
+ * When adding quadword support for 32 bit machines, we need
+ * to be a little careful as double multiply uses some of these
+ * macros: (in op-2.h)
+ * _FP_MUL_MEAT_2_wide() uses _FP_FRAC_DECL_4, _FP_FRAC_WORD_4,
+ * _FP_FRAC_ADD_4, _FP_FRAC_SRS_4
+ * _FP_MUL_MEAT_2_gmp() uses _FP_FRAC_SRS_4 (and should use
+ * _FP_FRAC_DECL_4: it appears to be broken and is not used 
+ * anywhere anyway. )
+ *
+ * I've now fixed all the macros that were here from the sparc64 code.
+ * [*none* of the shift macros were correct!] -- PMM 02/1998
+ * 
+ * The only quadword stuff that remains to be coded is: 
+ * 1) the conversion to/from ints, which requires 
+ * that we check (in op-common.h) that the following do the right thing
+ * for quadwords: _FP_TO_INT(Q,4,r,X,rsz,rsg), _FP_FROM_INT(Q,4,X,r,rs,rt)
+ * 2) multiply, divide and sqrt, which require:
+ * _FP_MUL_MEAT_4_*(R,X,Y), _FP_DIV_MEAT_4_*(R,X,Y), _FP_SQRT_MEAT_4(R,S,T,X,q),
+ * This also needs _FP_MUL_MEAT_Q and _FP_DIV_MEAT_Q to be defined to
+ * some suitable _FP_MUL_MEAT_4_* macros in sfp-machine.h.
+ * [we're free to choose whatever FP_MUL_MEAT_4_* macros we need for
+ * these; they are used nowhere else. ]
  */
 
 #define _FP_FRAC_DECL_4(X)	_FP_W_TYPE X##_f[4]
 #define _FP_FRAC_COPY_4(D,S)			\
   (D##_f[0] = S##_f[0], D##_f[1] = S##_f[1],	\
    D##_f[2] = S##_f[2], D##_f[3] = S##_f[3])
+/* The _FP_FRAC_SET_n(X,I) macro is intended for use with another
+ * macro such as _FP_ZEROFRAC_n which returns n comma separated values.
+ * The result is that we get an expansion of __FP_FRAC_SET_n(X,I0,I1,I2,I3)
+ * which just assigns the In values to the array X##_f[]. 
+ * This is why the number of parameters doesn't appear to match
+ * at first glance...      -- PMM 
+ */
 #define _FP_FRAC_SET_4(X,I)	__FP_FRAC_SET_4(X, I)
 #define _FP_FRAC_HIGH_4(X)	(X##_f[3])
 #define _FP_FRAC_LOW_4(X)	(X##_f[0])
@@ -19,26 +49,32 @@
     _down = _FP_W_TYPE_SIZE - _up;					\
     for (_i = 3; _i > _skip; --_i)					\
       X##_f[_i] = X##_f[_i-_skip] << _up | X##_f[_i-_skip-1] >> _down;	\
-    X##_f[_i] <<= _up;							\
+/* bugfixed: was X##_f[_i] <<= _up;  -- PMM 02/1998 */                  \
+    X##_f[_i] = X##_f[0] << _up; 	                                \
     for (--_i; _i >= 0; --_i)						\
       X##_f[_i] = 0;							\
   } while (0)
 
+/* This one was broken too */
 #define _FP_FRAC_SRL_4(X,N)						\
   do {									\
     _FP_I_TYPE _up, _down, _skip, _i;					\
     _skip = (N) / _FP_W_TYPE_SIZE;					\
     _down = (N) % _FP_W_TYPE_SIZE;					\
     _up = _FP_W_TYPE_SIZE - _down;					\
-    for (_i = 0; _i < 4-_skip; ++_i)					\
+    for (_i = 0; _i < 3-_skip; ++_i)					\
       X##_f[_i] = X##_f[_i+_skip] >> _down | X##_f[_i+_skip+1] << _up;	\
-    X##_f[_i] >>= _down;						\
+    X##_f[_i] = X##_f[3] >> _down;			         	\
     for (++_i; _i < 4; ++_i)						\
       X##_f[_i] = 0;							\
   } while (0)
 
 
-/* Right shift with sticky-lsb.  */
+/* Right shift with sticky-lsb. 
+ * What this actually means is that we do a standard right-shift,
+ * but that if any of the bits that fall off the right hand side
+ * were one then we always set the LSbit.
+ */
 #define _FP_FRAC_SRS_4(X,N,size)					\
   do {									\
     _FP_I_TYPE _up, _down, _skip, _i;					\
@@ -48,13 +84,15 @@
     _up = _FP_W_TYPE_SIZE - _down;					\
     for (_s = _i = 0; _i < _skip; ++_i)					\
       _s |= X##_f[_i];							\
-    _s = X##_f[_i] << _up;						\
-    X##_f[0] = X##_f[_skip] >> _down | X##_f[_skip+1] << _up | (_s != 0); \
-    for (_i = 1; _i < 4-_skip; ++_i)					\
+    _s |= X##_f[_i] << _up;						\
+/* s is now != 0 if we want to set the LSbit */                         \
+    for (_i = 0; _i < 3-_skip; ++_i)					\
       X##_f[_i] = X##_f[_i+_skip] >> _down | X##_f[_i+_skip+1] << _up;	\
-    X##_f[_i] >>= _down;						\
+    X##_f[_i] = X##_f[3] >> _down;					\
     for (++_i; _i < 4; ++_i)						\
       X##_f[_i] = 0;							\
+    /* don't fix the LSB until the very end when we're sure f[0] is stable */ \
+    X##_f[0] |= (_s != 0);                                              \
   } while (0)
 
 #define _FP_FRAC_ADD_4(R,X,Y)						\
@@ -62,6 +100,92 @@
 		  X##_f[3], X##_f[2], X##_f[1], X##_f[0],		\
 		  Y##_f[3], Y##_f[2], Y##_f[1], Y##_f[0])
 
+#define _FP_FRAC_SUB_4(R,X,Y)                                           \
+  __FP_FRAC_SUB_4(R##_f[3], R##_f[2], R##_f[1], R##_f[0],		\
+		  X##_f[3], X##_f[2], X##_f[1], X##_f[0],		\
+		  Y##_f[3], Y##_f[2], Y##_f[1], Y##_f[0])
+
+#define _FP_FRAC_ADDI_4(X,I)                                            \
+  __FP_FRAC_ADDI_4(X##_f[3], X##_f[2], X##_f[1], X##_f[0], I)
+
+#define _FP_ZEROFRAC_4  0,0,0,0
+#define _FP_MINFRAC_4   0,0,0,1
+
+#define _FP_FRAC_ZEROP_4(X)     ((X##_f[0] | X##_f[1] | X##_f[2] | X##_f[3]) == 0)
+#define _FP_FRAC_NEGP_4(X)      ((_FP_WS_TYPE)X##_f[3] < 0)
+#define _FP_FRAC_OVERP_4(fs,X)  (X##_f[0] & _FP_OVERFLOW_##fs)
+
+#define _FP_FRAC_EQ_4(X,Y)                              \
+ (X##_f[0] == Y##_f[0] && X##_f[1] == Y##_f[1]          \
+  && X##_f[2] == Y##_f[2] && X##_f[3] == Y##_f[3])
+
+#define _FP_FRAC_GT_4(X,Y)                              \
+ (X##_f[3] > Y##_f[3] ||                                \
+  (X##_f[3] == Y##_f[3] && (X##_f[2] > Y##_f[2] ||      \
+   (X##_f[2] == Y##_f[2] && (X##_f[1] > Y##_f[1] ||     \
+    (X##_f[1] == Y##_f[1] && X##_f[0] > Y##_f[0])       \
+   ))                                                   \
+  ))                                                    \
+ )
+
+#define _FP_FRAC_GE_4(X,Y)                              \
+ (X##_f[3] > Y##_f[3] ||                                \
+  (X##_f[3] == Y##_f[3] && (X##_f[2] > Y##_f[2] ||      \
+   (X##_f[2] == Y##_f[2] && (X##_f[1] > Y##_f[1] ||     \
+    (X##_f[1] == Y##_f[1] && X##_f[0] >= Y##_f[0])      \
+   ))                                                   \
+  ))                                                    \
+ )
+
+
+#define _FP_FRAC_CLZ_4(R,X)             \
+  do {                                  \
+    if (X##_f[3])                       \
+    {                                   \
+        __FP_CLZ(R,X##_f[3]);           \
+    }                                   \
+    else if (X##_f[2])                  \
+    {                                   \
+        __FP_CLZ(R,X##_f[2]);           \
+        R += _FP_W_TYPE_SIZE;           \
+    }                                   \
+    else if (X##_f[1])                  \
+    {                                   \
+        __FP_CLZ(R,X##_f[2]);           \
+        R += _FP_W_TYPE_SIZE*2;         \
+    }                                   \
+    else                                \
+    {                                   \
+        __FP_CLZ(R,X##_f[0]);           \
+        R += _FP_W_TYPE_SIZE*3;         \
+    }                                   \
+  } while(0)
+
+
+#define _FP_UNPACK_RAW_4(fs, X, val)                            \
+  do {                                                          \
+    union _FP_UNION_##fs _flo; _flo.flt = (val);        	\
+    X##_f[0] = _flo.bits.frac0;                                 \
+    X##_f[1] = _flo.bits.frac1;                                 \
+    X##_f[2] = _flo.bits.frac2;                                 \
+    X##_f[3] = _flo.bits.frac3;                                 \
+    X##_e  = _flo.bits.exp;                                     \
+    X##_s  = _flo.bits.sign;                                    \
+  } while (0)
+
+#define _FP_PACK_RAW_4(fs, val, X)                              \
+  do {                                                          \
+    union _FP_UNION_##fs _flo;					\
+    _flo.bits.frac0 = X##_f[0];                                 \
+    _flo.bits.frac1 = X##_f[1];                                 \
+    _flo.bits.frac2 = X##_f[2];                                 \
+    _flo.bits.frac3 = X##_f[3];                                 \
+    _flo.bits.exp   = X##_e;                                    \
+    _flo.bits.sign  = X##_s;                                    \
+    (val) = _flo.flt;                                   	\
+  } while (0)
+
+
 /*
  * Internals 
  */
@@ -76,3 +200,98 @@
    r2 = x2 + y2 + (r1 < x1),						\
    r3 = x3 + y3 + (r2 < x2))
 #endif
+
+#ifndef __FP_FRAC_SUB_4
+#define __FP_FRAC_SUB_4(r3,r2,r1,r0,x3,x2,x1,x0,y3,y2,y1,y0)		\
+  (r0 = x0 - y0,                                                        \
+   r1 = x1 - y1 - (r0 > x0),                                            \
+   r2 = x2 - y2 - (r1 > x1),                                            \
+   r3 = x3 - y3 - (r2 > x2))
+#endif
+
+#ifndef __FP_FRAC_ADDI_4
+/* I always wanted to be a lisp programmer :-> */
+#define __FP_FRAC_ADDI_4(x3,x2,x1,x0,i)                                 \
+  (x3 += ((x2 += ((x1 += ((x0 += i) < x0)) < x1) < x2)))
+#endif
+
+/* Convert FP values between word sizes. This appears to be more
+ * complicated than I'd have expected it to be, so these might be
+ * wrong... These macros are in any case somewhat bogus because they
+ * use information about what various FRAC_n variables look like 
+ * internally [eg, that 2 word vars are X_f0 and x_f1]. But so do
+ * the ones in op-2.h and op-1.h. 
+ */
+#define _FP_FRAC_CONV_1_4(dfs, sfs, D, S)                               \
+   do {                                                                 \
+     _FP_FRAC_SRS_4(S, (_FP_WFRACBITS_##sfs - _FP_WFRACBITS_##dfs),     \
+                        _FP_WFRACBITS_##sfs);                           \
+     D##_f = S##_f[0];                                                   \
+  } while (0)
+
+#define _FP_FRAC_CONV_2_4(dfs, sfs, D, S)                               \
+   do {                                                                 \
+     _FP_FRAC_SRS_4(S, (_FP_WFRACBITS_##sfs - _FP_WFRACBITS_##dfs),     \
+                        _FP_WFRACBITS_##sfs);                           \
+     D##_f0 = S##_f[0];                                                  \
+     D##_f1 = S##_f[1];                                                  \
+  } while (0)
+
+/* Assembly/disassembly for converting to/from integral types.  
+ * No shifting or overflow handled here.
+ */
+/* Put the FP value X into r, which is an integer of size rsize. */
+#define _FP_FRAC_ASSEMBLE_4(r, X, rsize)                                \
+  do {                                                                  \
+    if (rsize <= _FP_W_TYPE_SIZE)                                       \
+      r = X##_f[0];                                                     \
+    else if (rsize <= 2*_FP_W_TYPE_SIZE)                                \
+    {                                                                   \
+      r = X##_f[1];                                                     \
+      r <<= _FP_W_TYPE_SIZE;                                            \
+      r += X##_f[0];                                                    \
+    }                                                                   \
+    else                                                                \
+    {                                                                   \
+      /* I'm feeling lazy so we deal with int == 3words (implausible)*/ \
+      /* and int == 4words as a single case.                         */ \
+      r = X##_f[3];                                                     \
+      r <<= _FP_W_TYPE_SIZE;                                            \
+      r += X##_f[2];                                                    \
+      r <<= _FP_W_TYPE_SIZE;                                            \
+      r += X##_f[1];                                                    \
+      r <<= _FP_W_TYPE_SIZE;                                            \
+      r += X##_f[0];                                                    \
+    }                                                                   \
+  } while (0)
+
+/* "No disassemble Number Five!" */
+/* move an integer of size rsize into X's fractional part. We rely on
+ * the _f[] array consisting of words of size _FP_W_TYPE_SIZE to avoid
+ * having to mask the values we store into it.
+ */
+#define _FP_FRAC_DISASSEMBLE_4(X, r, rsize)                             \
+  do {                                                                  \
+    X##_f[0] = r;                                                       \
+    X##_f[1] = (rsize <= _FP_W_TYPE_SIZE ? 0 : r >> _FP_W_TYPE_SIZE);   \
+    X##_f[2] = (rsize <= 2*_FP_W_TYPE_SIZE ? 0 : r >> 2*_FP_W_TYPE_SIZE); \
+    X##_f[3] = (rsize <= 3*_FP_W_TYPE_SIZE ? 0 : r >> 3*_FP_W_TYPE_SIZE); \
+  } while (0);
+
+#define _FP_FRAC_CONV_4_1(dfs, sfs, D, S)                               \
+   do {                                                                 \
+     D##_f[0] = S##_f;                                                  \
+     D##_f[1] = D##_f[2] = D##_f[3] = 0;                                \
+     _FP_FRAC_SLL_4(D, (_FP_WFRACBITS_##dfs - _FP_WFRACBITS_##sfs));    \
+   } while (0)
+
+#define _FP_FRAC_CONV_4_2(dfs, sfs, D, S)                               \
+   do {                                                                 \
+     D##_f[0] = S##_f0;                                                 \
+     D##_f[1] = S##_f1;                                                 \
+     D##_f[2] = D##_f[3] = 0;                                           \
+     _FP_FRAC_SLL_4(D, (_FP_WFRACBITS_##dfs - _FP_WFRACBITS_##sfs));    \
+   } while (0)
+
+/* FIXME! This has to be written */
+#define _FP_SQRT_MEAT_4(R, S, T, X, q)
diff -u --recursive --new-file v2.1.96/linux/arch/sparc64/math-emu/op-common.h linux/arch/sparc64/math-emu/op-common.h
--- v2.1.96/linux/arch/sparc64/math-emu/op-common.h	Mon Jan 12 15:15:44 1998
+++ linux/arch/sparc64/math-emu/op-common.h	Tue Apr 14 17:44:21 1998
@@ -1,3 +1,4 @@
+
 #define _FP_DECL(wc, X)			\
   _FP_I_TYPE X##_c, X##_s, X##_e;	\
   _FP_FRAC_DECL_##wc(X)
@@ -507,6 +508,29 @@
  * Convert from FP to integer
  */
 
+/* "When a NaN, infinity, large positive argument >= 2147483648.0, or 
+ * large negative argument <= -2147483649.0 is converted to an integer,
+ * the invalid_current bit...should be set and fp_exception_IEEE_754 should
+ * be raised. If the floating point invalid trap is disabled, no trap occurs
+ * and a numerical result is generated: if the sign bit of the operand
+ * is 0, the result is 2147483647; if the sign bit of the operand is 1,
+ * the result is -2147483648."
+ * Similarly for conversion to extended ints, except that the boundaries
+ * are >= 2^63, <= -(2^63 + 1), and the results are 2^63 + 1 for s=0 and
+ * -2^63 for s=1.
+ * -- SPARC Architecture Manual V9, Appendix B, which specifies how
+ * SPARCs resolve implementation dependencies in the IEEE-754 spec.
+ * I don't believe that the code below follows this. I'm not even sure
+ * it's right! 
+ * It doesn't cope with needing to convert to an n bit integer when there
+ * is no n bit integer type. Fortunately gcc provides long long so this
+ * isn't a problem for sparc32.
+ * I have, however, fixed its NaN handling to conform as above.
+ *         -- PMM 02/1998
+ * NB: rsigned is not 'is r declared signed?' but 'should the value stored
+ * in r be signed or unsigned?'. r is always(?) declared unsigned.
+ * Comments below are mine, BTW -- PMM 
+ */
 #define _FP_TO_INT(fs, wc, r, X, rsize, rsigned)				\
   do {										\
     switch (X##_c)								\
@@ -514,13 +538,14 @@
       case FP_CLS_NORMAL:							\
 	if (X##_e < 0)								\
 	  {									\
-	  case FP_CLS_NAN:							\
+	  /* case FP_CLS_NAN: see above! */					\
 	  case FP_CLS_ZERO:							\
 	    r = 0;								\
 	  }									\
 	else if (X##_e >= rsize - (rsigned != 0))				\
-	  {									\
-	  case FP_CLS_INF:							\
+	  {	/* overflow */							\
+	  case FP_CLS_NAN:                                                      \
+          case FP_CLS_INF:							\
 	    if (rsigned)							\
 	      {									\
 		r = 1;								\
@@ -604,6 +629,23 @@
 /* Count leading zeros in a word.  */
 
 #ifndef __FP_CLZ
+#if _FP_W_TYPE_SIZE < 64
+/* this is just to shut the compiler up about shifts > word length -- PMM 02/1998 */
+#define __FP_CLZ(r, x)				\
+  do {						\
+    _FP_W_TYPE _t = (x);			\
+    r = _FP_W_TYPE_SIZE - 1;			\
+    if (_t > 0xffff) r -= 16;			\
+    if (_t > 0xffff) _t >>= 16;			\
+    if (_t > 0xff) r -= 8;			\
+    if (_t > 0xff) _t >>= 8;			\
+    if (_t & 0xf0) r -= 4;			\
+    if (_t & 0xf0) _t >>= 4;			\
+    if (_t & 0xc) r -= 2;			\
+    if (_t & 0xc) _t >>= 2;			\
+    if (_t & 0x2) r -= 1;			\
+  } while (0)
+#else /* not _FP_W_TYPE_SIZE < 64 */
 #define __FP_CLZ(r, x)				\
   do {						\
     _FP_W_TYPE _t = (x);			\
@@ -620,9 +662,11 @@
     if (_t & 0xc) _t >>= 2;			\
     if (_t & 0x2) r -= 1;			\
   } while (0)
-#endif
+#endif /* not _FP_W_TYPE_SIZE < 64 */
+#endif /* ndef __FP_CLZ */
 
 #define _FP_DIV_HELP_imm(q, r, n, d)		\
   do {						\
     q = n / d, r = n % d;			\
   } while (0)
+
diff -u --recursive --new-file v2.1.96/linux/arch/sparc64/math-emu/quad.h linux/arch/sparc64/math-emu/quad.h
--- v2.1.96/linux/arch/sparc64/math-emu/quad.h	Mon Jan 12 15:15:44 1998
+++ linux/arch/sparc64/math-emu/quad.h	Tue Apr 14 17:44:21 1998
@@ -1,12 +1,17 @@
 /*
  * Definitions for IEEE Quad Precision
  */
-
-#if _FP_W_TYPE_SIZE < 64
-#error "Only stud muffins allowed, schmuck."
+#if _FP_W_TYPE_SIZE < 32
+/* It appears to be traditional to abuse 16bitters in these header files... */
+#error "Here's a nickel, kid. Go buy yourself a real computer."
 #endif
 
+#if _FP_W_TYPE_SIZE < 64
+/* This is all terribly experimental and I don't know if it'll work properly -- PMM 02/1998 */
+#define _FP_FRACTBITS_Q         (4*_FP_W_TYPE_SIZE)
+#else
 #define _FP_FRACTBITS_Q		(2*_FP_W_TYPE_SIZE)
+#endif
 
 #define _FP_FRACBITS_Q		113
 #define _FP_FRACXBITS_Q		(_FP_FRACTBITS_Q - _FP_FRACBITS_Q)
@@ -23,6 +28,66 @@
 #define _FP_OVERFLOW_Q		\
 	((_FP_W_TYPE)1 << (_FP_WFRACBITS_Q % _FP_W_TYPE_SIZE))
 
+#if _FP_W_TYPE_SIZE < 64
+
+union _FP_UNION_Q
+{
+   long double flt;
+   struct 
+   {
+#if __BYTE_ORDER == __BIG_ENDIAN
+      unsigned sign : 1;
+      unsigned exp : _FP_EXPBITS_Q;
+      unsigned long frac3 : _FP_FRACBITS_Q - (_FP_IMPLBIT_Q != 0)-(_FP_W_TYPE_SIZE * 3);
+      unsigned long frac2 : _FP_W_TYPE_SIZE;
+      unsigned long frac1 : _FP_W_TYPE_SIZE;
+      unsigned long frac0 : _FP_W_TYPE_SIZE;
+#else
+      unsigned long frac0 : _FP_W_TYPE_SIZE;
+      unsigned long frac1 : _FP_W_TYPE_SIZE;
+      unsigned long frac2 : _FP_W_TYPE_SIZE;
+      unsigned long frac3 : _FP_FRACBITS_Q - (_FP_IMPLBIT_Q != 0)-(_FP_W_TYPE_SIZE * 3);
+      unsigned exp : _FP_EXPBITS_Q;
+      unsigned sign : 1;
+#endif /* not bigendian */
+   } bits __attribute__((packed));
+};
+
+
+#define FP_DECL_Q(X)		_FP_DECL(4,X)
+#define FP_UNPACK_RAW_Q(X,val)	_FP_UNPACK_RAW_4(Q,X,val)
+#define FP_PACK_RAW_Q(val,X)	_FP_PACK_RAW_4(Q,val,X)
+
+#define FP_UNPACK_Q(X,val)		\
+  do {					\
+    _FP_UNPACK_RAW_4(Q,X,val);		\
+    _FP_UNPACK_CANONICAL(Q,4,X);	\
+  } while (0)
+
+#define FP_PACK_Q(val,X)		\
+  do {					\
+    _FP_PACK_CANONICAL(Q,4,X);		\
+    _FP_PACK_RAW_4(Q,val,X);		\
+  } while (0)
+
+#define FP_NEG_Q(R,X)		_FP_NEG(Q,4,R,X)
+#define FP_ADD_Q(R,X,Y)		_FP_ADD(Q,4,R,X,Y)
+/* single.h and double.h define FP_SUB_t this way too. However, _FP_SUB is
+ * never defined in op-common.h! Fortunately nobody seems to use the FP_SUB_t 
+ * macros: I suggest a combination of FP_NEG and FP_ADD :-> -- PMM 02/1998
+ */
+#define FP_SUB_Q(R,X,Y)		_FP_SUB(Q,4,R,X,Y)
+#define FP_MUL_Q(R,X,Y)		_FP_MUL(Q,4,R,X,Y)
+#define FP_DIV_Q(R,X,Y)		_FP_DIV(Q,4,R,X,Y)
+#define FP_SQRT_Q(R,X)		_FP_SQRT(Q,4,R,X)
+
+#define FP_CMP_Q(r,X,Y,un)	_FP_CMP(Q,4,r,X,Y,un)
+#define FP_CMP_EQ_Q(r,X,Y)	_FP_CMP_EQ(Q,4,r,X,Y)
+
+#define FP_TO_INT_Q(r,X,rsz,rsg)  _FP_TO_INT(Q,4,r,X,rsz,rsg)
+#define FP_FROM_INT_Q(X,r,rs,rt)  _FP_FROM_INT(Q,4,X,r,rs,rt)
+
+#else   /* not _FP_W_TYPE_SIZE < 64 */
 union _FP_UNION_Q
 {
   long double flt /* __attribute__((mode(TF))) */ ;
@@ -69,3 +134,5 @@
 
 #define FP_TO_INT_Q(r,X,rsz,rsg)  _FP_TO_INT(Q,2,r,X,rsz,rsg)
 #define FP_FROM_INT_Q(X,r,rs,rt)  _FP_FROM_INT(Q,2,X,r,rs,rt)
+
+#endif /* not _FP_W_TYPE_SIZE < 64 */
diff -u --recursive --new-file v2.1.96/linux/arch/sparc64/math-emu/single.h linux/arch/sparc64/math-emu/single.h
--- v2.1.96/linux/arch/sparc64/math-emu/single.h	Mon Jan 12 15:15:44 1998
+++ linux/arch/sparc64/math-emu/single.h	Tue Apr 14 17:44:21 1998
@@ -3,7 +3,7 @@
  */
 
 #if _FP_W_TYPE_SIZE < 32
-#error "Here's a nickle kid.  Go buy yourself a real computer."
+#error "Here's a nickel kid.  Go buy yourself a real computer."
 #endif
 
 #define _FP_FRACBITS_S		24
diff -u --recursive --new-file v2.1.96/linux/arch/sparc64/mm/fault.c linux/arch/sparc64/mm/fault.c
--- v2.1.96/linux/arch/sparc64/mm/fault.c	Thu Sep  4 12:54:48 1997
+++ linux/arch/sparc64/mm/fault.c	Tue Apr 14 17:44:21 1998
@@ -1,4 +1,4 @@
-/* $Id: fault.c,v 1.20 1997/08/04 16:16:51 davem Exp $
+/* $Id: fault.c,v 1.21 1998/03/25 10:43:20 jj Exp $
  * arch/sparc64/mm/fault.c: Page fault handlers for the 64-bit Sparc.
  *
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
@@ -209,7 +209,7 @@
 		} else {
 			current->tss.sig_address = address;
 			current->tss.sig_desc = SUBSIG_NOMAPPING;
-			send_sig(SIGSEGV, current, 1);
+			force_sig(SIGSEGV, current);
 			goto out;
 		}
 		unhandled_fault (address, current, regs);
diff -u --recursive --new-file v2.1.96/linux/arch/sparc64/mm/init.c linux/arch/sparc64/mm/init.c
--- v2.1.96/linux/arch/sparc64/mm/init.c	Tue Mar 17 22:18:14 1998
+++ linux/arch/sparc64/mm/init.c	Tue Apr 14 17:44:21 1998
@@ -1,8 +1,8 @@
-/*  $Id: init.c,v 1.60 1998/01/10 18:19:51 ecd Exp $
+/*  $Id: init.c,v 1.71 1998/03/27 07:00:08 davem Exp $
  *  arch/sparc64/mm/init.c
  *
  *  Copyright (C) 1996,1997 David S. Miller (davem@caip.rutgers.edu)
- *  Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ *  Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
  */
  
 #include <linux/config.h>
@@ -22,13 +22,20 @@
 #include <asm/mmu_context.h>
 #include <asm/vaddrs.h>
 
+/* Turn this off if you suspect some place in some physical memory hole
+   might get into page tables (something would be broken very much). */
+   
+#define FREE_UNUSED_MEM_MAP
+
 extern void show_net_buffers(void);
 extern unsigned long device_scan(unsigned long);
 
 struct sparc_phys_banks sp_banks[SPARC_PHYS_BANKS];
 
 /* Ugly, but necessary... -DaveM */
-unsigned long phys_base, null_pmd_table, null_pte_table;
+unsigned long phys_base;
+unsigned int null_pte_table;
+unsigned long two_null_pmd_table, two_null_pte_table;
 
 extern unsigned long empty_null_pmd_table;
 extern unsigned long empty_null_pte_table;
@@ -42,12 +49,12 @@
 
 static __inline__ void __init_pmd(pmd_t *pmdp)
 {
-	__bfill64((void *)pmdp, &null_pte_table);
+	__bfill64((void *)pmdp, &two_null_pte_table);
 }
 
 static __inline__ void __init_pgd(pgd_t *pgdp)
 {
-	__bfill64((void *)pgdp, &null_pmd_table);
+	__bfill64((void *)pgdp, &two_null_pmd_table);
 }
 
 /*
@@ -88,26 +95,36 @@
 
 void show_mem(void)
 {
-	int i,free = 0,total = 0,reserved = 0;
-	int shared = 0;
+	int free = 0,total = 0,reserved = 0;
+	int shared = 0, cached = 0;
+	struct page *page, *end;
 
 	printk("\nMem-info:\n");
 	show_free_areas();
 	printk("Free swap:       %6dkB\n",nr_swap_pages<<(PAGE_SHIFT-10));
-	i = max_mapnr;
-	while (i-- > 0) {
+	for (page = mem_map, end = mem_map + max_mapnr;
+	     page < end; page++) {
+		if (PageSkip(page)) {
+			if (page->next_hash < page)
+				break;
+			page = page->next_hash;
+		}
 		total++;
-		if (PageReserved(mem_map + i))
+		if (PageReserved(page))
 			reserved++;
-		else if (!atomic_read(&mem_map[i].count))
+		else if (PageSwapCache(page))
+			cached++;
+		else if (!atomic_read(&page->count))
 			free++;
 		else
-			shared += atomic_read(&mem_map[i].count) - 1;
+			shared += atomic_read(&page->count) - 1;
 	}
 	printk("%d pages of RAM\n",total);
 	printk("%d free pages\n",free);
 	printk("%d reserved pages\n",reserved);
 	printk("%d pages shared\n",shared);
+	printk("%d pages swap cached\n",cached);
+	printk("%ld pages in page table cache\n",pgtable_cache_size);
 	show_buffers();
 #ifdef CONFIG_NET
 	show_net_buffers();
@@ -409,14 +426,10 @@
 	spin_unlock_irqrestore(&iommu->iommu_lock, flags);
 }
 
-static char sfmmuinfo[512];
-
-char *mmu_info(void)
+int mmu_info(char *buf)
 {
 	/* We'll do the rest later to make it nice... -DaveM */
-	sprintf(sfmmuinfo, "MMU Type\t: Spitfire\n");
-
-	return sfmmuinfo;
+	return sprintf(buf, "MMU Type\t: Spitfire\n");
 }
 
 static unsigned long mempool;
@@ -633,10 +646,7 @@
 }
 
 #ifndef __SMP__
-unsigned long *pgd_quicklist = NULL;
-unsigned long *pmd_quicklist = NULL;
-unsigned long *pte_quicklist = NULL;
-unsigned long pgtable_cache_size = 0;
+struct pgtable_cache_struct pgt_quicklists;
 #endif
 
 pgd_t *get_pgd_slow(void)
@@ -653,7 +663,7 @@
 {
 	pmd_t *pmd;
 
-	pmd = (pmd_t *) __get_free_page(GFP_KERNEL);
+	pmd = (pmd_t *) __get_free_page(GFP_DMA|GFP_KERNEL);
 	if(pmd) {
 		__init_pmd(pmd);
 		pgd_set(pgd, pmd);
@@ -666,9 +676,9 @@
 {
 	pte_t *pte;
 
-	pte = (pte_t *) __get_free_page(GFP_KERNEL);
+	pte = (pte_t *) __get_free_page(GFP_DMA|GFP_KERNEL);
 	if(pte) {
-		clear_page((unsigned long)pte);
+		memset((void *)pte, 0, PTE_TABLE_SIZE);
 		pmd_set(pmd, pte);
 		return pte + offset;
 	}
@@ -737,6 +747,7 @@
 	pte_clear(ptep);
 }
 
+#if NOTUSED
 void sparc_ultra_dump_itlb(void)
 {
         int slot;
@@ -766,6 +777,7 @@
         		slot+2, spitfire_get_dtlb_tag(slot+2), spitfire_get_dtlb_data(slot+2));
         }
 }
+#endif
 
 /* paging_init() sets up the page tables */
 
@@ -808,23 +820,30 @@
 	/* Now set kernel pgd to upper alias so physical page computations
 	 * work.
 	 */
-	init_mm.pgd += ((shift) / (sizeof(pgd_t *)));
+	init_mm.pgd += ((shift) / (sizeof(pgd_t)));
 
 	/* The funny offsets are to make page table operations much quicker and
 	 * requite less state, see pgtable.h for gory details.
+	 * pgtable.h assumes null_pmd_table is null_pte_table - PAGE_SIZE, lets
+	 * check it now.
 	 */
-	null_pmd_table=__pa(((unsigned long)&empty_null_pmd_table)+shift);
 	null_pte_table=__pa(((unsigned long)&empty_null_pte_table)+shift);
+	if (null_pmd_table != __pa(((unsigned long)&empty_null_pmd_table)+shift)) {
+		prom_printf("null_p{md|te}_table broken.\n");
+		prom_halt();
+	}
+	two_null_pmd_table = (((unsigned long)null_pmd_table) << 32) | null_pmd_table;
+	two_null_pte_table = (((unsigned long)null_pte_table) << 32) | null_pte_table;
 
 	pmdp = (pmd_t *) &empty_null_pmd_table;
-	for(i = 0; i < 1024; i++)
+	for(i = 0; i < PTRS_PER_PMD; i++)
 		pmd_val(pmdp[i]) = null_pte_table;
 
-	memset((void *) &empty_null_pte_table, 0, PAGE_SIZE);
+	memset((void *) &empty_null_pte_table, 0, PTE_TABLE_SIZE);
 
 	/* Now can init the kernel/bad page tables. */
-	__bfill64((void *)swapper_pg_dir, &null_pmd_table);
-	__bfill64((void *)&empty_bad_pmd_table, &null_pte_table);
+	__bfill64((void *)swapper_pg_dir, &two_null_pmd_table);
+	__bfill64((void *)&empty_bad_pmd_table, &two_null_pte_table);
 
 	/* We use mempool to create page tables, therefore adjust it up
 	 * such that __pa() macros etc. work.
@@ -867,21 +886,34 @@
 
 __initfunc(static void taint_real_pages(unsigned long start_mem, unsigned long end_mem))
 {
-	unsigned long addr, tmp2 = 0;
+	unsigned long tmp = 0, paddr, endaddr;
+	unsigned long end = __pa(end_mem);
 
-	for(addr = PAGE_OFFSET; addr < end_mem; addr += PAGE_SIZE) {
-		if(addr >= PAGE_OFFSET && addr < start_mem)
-			addr = start_mem;
-		for(tmp2=0; sp_banks[tmp2].num_bytes != 0; tmp2++) {
-			unsigned long phys_addr = __pa(addr);
-			unsigned long base = sp_banks[tmp2].base_addr;
-			unsigned long limit = base + sp_banks[tmp2].num_bytes;
-
-			if((phys_addr >= base) && (phys_addr < limit) &&
-			   ((phys_addr + PAGE_SIZE) < limit))
-				mem_map[MAP_NR(addr)].flags &= ~(1<<PG_reserved);
-			if (phys_addr >= 0xf0000000)
-				mem_map[MAP_NR(addr)].flags &= ~(1<<PG_DMA);
+	for (paddr = __pa(start_mem); paddr < end; ) {
+		for (; sp_banks[tmp].num_bytes != 0; tmp++)
+			if (sp_banks[tmp].base_addr + sp_banks[tmp].num_bytes > paddr)
+				break;
+		if (!sp_banks[tmp].num_bytes) {
+			mem_map[paddr>>PAGE_SHIFT].flags |= (1<<PG_skip);
+			mem_map[paddr>>PAGE_SHIFT].next_hash = mem_map + (phys_base >> PAGE_SHIFT);
+			return;
+		}
+		
+		if (sp_banks[tmp].base_addr > paddr) {
+			/* Making a one or two pages PG_skip holes is not necessary */
+			if (sp_banks[tmp].base_addr - paddr > 2 * PAGE_SIZE) {
+				mem_map[paddr>>PAGE_SHIFT].flags |= (1<<PG_skip);
+				mem_map[paddr>>PAGE_SHIFT].next_hash = mem_map + (sp_banks[tmp].base_addr >> PAGE_SHIFT);
+			}
+			paddr = sp_banks[tmp].base_addr;
+		}
+		
+		endaddr = sp_banks[tmp].base_addr + sp_banks[tmp].num_bytes;
+		while (paddr < endaddr) {
+			mem_map[paddr>>PAGE_SHIFT].flags &= ~(1<<PG_reserved);
+			if (paddr >= 0xf0000000)
+				mem_map[paddr>>PAGE_SHIFT].flags &= ~(1<<PG_DMA);
+			paddr += PAGE_SIZE;
 		}
 	}
 }
@@ -891,31 +923,65 @@
 	int codepages = 0;
 	int datapages = 0;
 	int initpages = 0;
-	unsigned long tmp2, addr;
+	unsigned long addr;
 	unsigned long alias_base = phys_base + PAGE_OFFSET - (long)(&empty_zero_page);
+	struct page *page, *end;
 
 	end_mem &= PAGE_MASK;
 	max_mapnr = MAP_NR(end_mem);
 	high_memory = (void *) end_mem;
 
 	start_mem = PAGE_ALIGN(start_mem);
-	num_physpages = (start_mem - PAGE_OFFSET) >> PAGE_SHIFT;
+	num_physpages = 0;
+	
+	if (phys_base) {
+		mem_map[0].flags |= (1<<PG_skip) | (1<<PG_reserved);
+		mem_map[0].next_hash = mem_map + (phys_base >> PAGE_SHIFT);
+	}
 
-	addr = PAGE_OFFSET;
+	addr = PAGE_OFFSET + phys_base;
 	while(addr < start_mem) {
 #ifdef CONFIG_BLK_DEV_INITRD
-		if (initrd_below_start_ok && addr >= initrd_start && addr < initrd_end) {
+		if (initrd_below_start_ok && addr >= initrd_start && addr < initrd_end)
 			mem_map[MAP_NR(addr)].flags &= ~(1<<PG_reserved);
-			num_physpages--;
-		} else
+		else
 #endif	
 			mem_map[MAP_NR(addr)].flags |= (1<<PG_reserved);
 		addr += PAGE_SIZE;
 	}
 
 	taint_real_pages(start_mem, end_mem);
+	
+#ifdef FREE_UNUSED_MEM_MAP	
+	end = mem_map + max_mapnr;
+	for (page = mem_map; page < end; page++) {
+		if (PageSkip(page)) {
+			unsigned long low, high;
+			
+			low = PAGE_ALIGN((unsigned long)(page+1));
+			if (page->next_hash < page)
+				high = ((unsigned long)end) & PAGE_MASK;
+			else
+				high = ((unsigned long)page->next_hash) & PAGE_MASK;
+			while (low < high) {
+				mem_map[MAP_NR(low)].flags &= ~(1<<PG_reserved);
+				low += PAGE_SIZE;
+			}
+		}
+	}
+#endif
+	
 	for (addr = PAGE_OFFSET; addr < end_mem; addr += PAGE_SIZE) {
-		if(PageReserved(mem_map + MAP_NR(addr))) {
+		if (PageSkip(mem_map + MAP_NR(addr))) {
+			unsigned long next = mem_map[MAP_NR(addr)].next_hash - mem_map;
+			
+			next = (next << PAGE_SHIFT) + PAGE_OFFSET;
+			if (next < addr || next >= end_mem)
+				break;
+			addr = next;
+		}
+		num_physpages++;
+		if (PageReserved(mem_map + MAP_NR(addr))) {
 			if ((addr < ((unsigned long) &etext) + alias_base) && (addr >= alias_base))
 				codepages++;
 			else if((addr >= ((unsigned long)&__init_begin) + alias_base)
@@ -926,7 +992,6 @@
 			continue;
 		}
 		atomic_set(&mem_map[MAP_NR(addr)].count, 1);
-		num_physpages++;
 #ifdef CONFIG_BLK_DEV_INITRD
 		if (!initrd_start ||
 		    (addr < initrd_start || addr >= initrd_end))
@@ -934,18 +999,16 @@
 			free_page(addr);
 	}
 
-	tmp2 = nr_free_pages << PAGE_SHIFT;
-
-	printk("Memory: %luk available (%dk kernel code, %dk data, %dk init) [%016lx,%016lx]\n",
-	       tmp2 >> 10,
+	printk("Memory: %uk available (%dk kernel code, %dk data, %dk init) [%016lx,%016lx]\n",
+	       nr_free_pages << (PAGE_SHIFT-10),
 	       codepages << (PAGE_SHIFT-10),
 	       datapages << (PAGE_SHIFT-10), 
 	       initpages << (PAGE_SHIFT-10), 
 	       PAGE_OFFSET, end_mem);
 
 	freepages.low = nr_free_pages >> 7;
-	if(freepages.low < 16)
-		freepages.low = 16;
+	if(freepages.low < 48)
+		freepages.low = 48;
 	freepages.low = freepages.low + (freepages.low >> 1);
 	freepages.high = freepages.low + freepages.low;
 }
@@ -967,20 +1030,25 @@
 
 void si_meminfo(struct sysinfo *val)
 {
-	int i;
+	struct page *page, *end;
 
-	i = MAP_NR(high_memory);
 	val->totalram = 0;
 	val->sharedram = 0;
 	val->freeram = nr_free_pages << PAGE_SHIFT;
 	val->bufferram = buffermem;
-	while (i-- > 0)  {
-		if (PageReserved(mem_map + i))
+	for (page = mem_map, end = mem_map + max_mapnr;
+	     page < end; page++) {
+		if (PageSkip(page)) {
+			if (page->next_hash < page)
+				break;
+			page = page->next_hash;
+		}
+		if (PageReserved(page))
 			continue;
 		val->totalram++;
-		if (!atomic_read(&mem_map[i].count))
+		if (!atomic_read(&page->count))
 			continue;
-		val->sharedram += atomic_read(&mem_map[i].count) - 1;
+		val->sharedram += atomic_read(&page->count) - 1;
 	}
 	val->totalram <<= PAGE_SHIFT;
 	val->sharedram <<= PAGE_SHIFT;
diff -u --recursive --new-file v2.1.96/linux/arch/sparc64/mm/modutil.c linux/arch/sparc64/mm/modutil.c
--- v2.1.96/linux/arch/sparc64/mm/modutil.c	Mon Jul  7 08:18:55 1997
+++ linux/arch/sparc64/mm/modutil.c	Tue Apr 14 17:44:21 1998
@@ -1,7 +1,7 @@
-/*  $Id: modutil.c,v 1.1 1997/06/27 14:53:35 jj Exp $
+/*  $Id: modutil.c,v 1.3 1998/01/16 16:35:02 jj Exp $
  *  arch/sparc64/mm/modutil.c
  *
- *  Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ *  Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
  *  Based upon code written by Linus Torvalds and others.
  */
  
@@ -21,7 +21,7 @@
 	if (!addr)
 		return;
 	if ((PAGE_SIZE-1) & (unsigned long) addr) {
-		printk("Trying to vfree() bad address (%p)\n", addr);
+		printk("Trying to unmap module with bad address (%p)\n", addr);
 		return;
 	}
 	for (p = &modvmlist ; (tmp = *p) ; p = &tmp->next) {
@@ -33,6 +33,32 @@
 		}
 	}
 	printk("Trying to unmap nonexistent module vm area (%p)\n", addr);
+}
+
+void module_shrink(void * addr, unsigned long size)
+{
+	struct vm_struct *tmp;
+
+	if (!addr)
+		return;
+	if ((PAGE_SIZE-1) & (unsigned long) addr) {
+		printk("Trying to shrink module with bad address (%p)\n", addr);
+		return;
+	}
+	size = PAGE_ALIGN(size);
+	if (!size)
+		module_unmap(addr);
+	for (tmp = modvmlist; tmp; tmp = tmp->next) {
+		if (tmp->addr == addr) {
+			if (size > tmp->size - PAGE_SIZE) {
+				printk("Trying to expand module with module_shrink()\n");
+				return;
+			}
+			vmfree_area_pages(VMALLOC_VMADDR(tmp->addr)+size, tmp->size-size);
+			return;
+		}
+	}
+	printk("Trying to shrink nonexistent module vm area (%p)\n", addr);
 }
 
 void * module_map (unsigned long size)
diff -u --recursive --new-file v2.1.96/linux/arch/sparc64/prom/bootstr.c linux/arch/sparc64/prom/bootstr.c
--- v2.1.96/linux/arch/sparc64/prom/bootstr.c	Mon Jul  7 08:18:55 1997
+++ linux/arch/sparc64/prom/bootstr.c	Tue Apr 14 17:44:21 1998
@@ -1,8 +1,8 @@
-/* $Id: bootstr.c,v 1.4 1997/06/17 13:25:35 jj Exp $
+/* $Id: bootstr.c,v 1.5 1998/01/23 08:51:39 jj Exp $
  * bootstr.c:  Boot string/argument acquisition from the PROM.
  *
  * Copyright(C) 1995 David S. Miller (davem@caip.rutgers.edu)
- * Copyright(C) 1996 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright(C) 1996,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
  */
 
 #include <linux/string.h>
@@ -10,9 +10,9 @@
 #include <asm/oplib.h>
 
 #define BARG_LEN  256
-int bootstr_len __initdata = BARG_LEN;
-static int bootstr_valid __initdata = 0;
-static char bootstr_buf[BARG_LEN] __initdata = { 0 };
+int bootstr_len = BARG_LEN;
+static int bootstr_valid = 0;
+static char bootstr_buf[BARG_LEN] = { 0 };
 
 __initfunc(char *
 prom_getbootargs(void))
diff -u --recursive --new-file v2.1.96/linux/arch/sparc64/prom/init.c linux/arch/sparc64/prom/init.c
--- v2.1.96/linux/arch/sparc64/prom/init.c	Mon Apr 14 16:28:10 1997
+++ linux/arch/sparc64/prom/init.c	Tue Apr 14 17:44:21 1998
@@ -1,4 +1,4 @@
-/* $Id: init.c,v 1.7 1997/03/24 17:43:59 jj Exp $
+/* $Id: init.c,v 1.8 1998/03/15 10:14:44 ecd Exp $
  * init.c:  Initialize internal variables used by the PROM
  *          library functions.
  *
@@ -9,6 +9,7 @@
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/string.h>
+#include <linux/ctype.h>
 
 #include <asm/openprom.h>
 #include <asm/oplib.h>
@@ -32,11 +33,13 @@
 
 __initfunc(void prom_init(void *cif_handler, void *cif_stack))
 {
-	char buffer[80];
+	char buffer[80], *p;
+	int ints[3];
 	int node;
-	
+	int i = 0;
+
 	prom_vers = PROM_P1275;
-	
+
 	prom_cif_init(cif_handler, cif_stack);
 
 	prom_root_node = prom_getsibling(0);
@@ -46,34 +49,45 @@
 	prom_chosen_node = prom_finddevice("/chosen");
 	if (!prom_chosen_node || prom_chosen_node == -1)
 		prom_halt();
-		
+
 	prom_stdin = prom_getint (prom_chosen_node, "stdin");
 	prom_stdout = prom_getint (prom_chosen_node, "stdout");
 
 	node = prom_finddevice("/openprom");
 	if (!node || node == -1)
 		prom_halt();
-		
+
 	prom_getstring (node, "version", buffer, sizeof (buffer));
-	
+
 	prom_printf ("\n");
-	
-	if (strncmp (buffer, "OBP ", 4) || buffer[5] != '.' || buffer[7] != '.') {
-		prom_printf ("Strange OBP version `%s'.\n", buffer);
-		prom_halt ();
+
+	if (strncmp (buffer, "OBP ", 4))
+		goto strange_version;
+
+	/* Version field is expected to be 'OBP xx.yy.zz date...' */
+
+	p = buffer + 4;
+	while (p && isdigit(*p) && i < 3) {
+		ints[i++] = simple_strtoul(p, NULL, 0);
+		if ((p = strchr(p, '.')) != NULL)
+			p++;
 	}
-	/* Version field is expected to be 'OBP x.y.z date...' */
-	
-	prom_rev = buffer[6] - '0';
-	prom_prev = ((buffer[4] - '0') << 16) | 
-		    ((buffer[6] - '0') << 8) |
-		    (buffer[8] - '0');
-		    
+	if (i != 3)
+		goto strange_version;
+
+	prom_rev = ints[1];
+	prom_prev = (ints[0] << 16) | (ints[1] << 8) | ints[2];
+
 	printk ("PROMLIB: Sun IEEE Boot Prom %s\n", buffer + 4);
-	
+
 	prom_meminit();
 
 	prom_ranges_init();
 
 	/* Initialization successful. */
+	return;
+
+strange_version:
+	prom_printf ("Strange OBP version `%s'.\n", buffer);
+	prom_halt ();
 }
diff -u --recursive --new-file v2.1.96/linux/arch/sparc64/prom/ranges.c linux/arch/sparc64/prom/ranges.c
--- v2.1.96/linux/arch/sparc64/prom/ranges.c	Thu Sep  4 12:54:48 1997
+++ linux/arch/sparc64/prom/ranges.c	Tue Apr 14 17:44:21 1998
@@ -1,4 +1,4 @@
-/* $Id: ranges.c,v 1.8 1997/08/17 22:39:45 ecd Exp $
+/* $Id: ranges.c,v 1.10 1998/03/24 05:54:29 ecd Exp $
  * ranges.c: Handle ranges in newer proms for obio/sbus.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -140,16 +140,60 @@
 		ebus->num_ebus_ranges = (success/sizeof(struct linux_prom_ebus_ranges));
 }
 
+__initfunc(void prom_ebus_intmap_init(struct linux_ebus *ebus))
+{
+	int success;
+
+	ebus->num_ebus_intmap = 0;
+	success = prom_getproperty(ebus->prom_node, "interrupt-map",
+				   (char *)ebus->ebus_intmap,
+				   sizeof(ebus->ebus_intmap));
+	if (success == -1)
+		return;
+
+	ebus->num_ebus_intmap = (success/sizeof(struct linux_prom_ebus_intmap));
+
+	success = prom_getproperty(ebus->prom_node, "interrupt-map-mask",
+				   (char *)&ebus->ebus_intmask,
+				   sizeof(ebus->ebus_intmask));
+	if (success == -1) {
+		prom_printf("%s: can't get interrupt-map-mask\n", __FUNCTION__);
+		prom_halt();
+	}
+}
+
 __initfunc(void prom_pbm_ranges_init(int pnode, struct linux_pbm_info *pbm))
 {
 	int success;
 
 	pbm->num_pbm_ranges = 0;
-	success = prom_getproperty(pbm->prom_node, "ranges",
+	success = prom_getproperty(pnode, "ranges",
 				   (char *)&pbm->pbm_ranges,
 				   sizeof(pbm->pbm_ranges));
 	if(success != -1)
 		pbm->num_pbm_ranges = (success/sizeof(struct linux_prom_pci_ranges));
+}
+
+__initfunc(void prom_pbm_intmap_init(int pnode, struct linux_pbm_info *pbm))
+{
+	int success;
+
+	pbm->num_pbm_intmap = 0;
+	success = prom_getproperty(pnode, "interrupt-map",
+				   (char *)pbm->pbm_intmap,
+				   sizeof(pbm->pbm_intmap));
+	if (success == -1)
+		return;
+
+	pbm->num_pbm_intmap = (success/sizeof(struct linux_prom_pci_intmap));
+
+	success = prom_getproperty(pnode, "interrupt-map-mask",
+				   (char *)&pbm->pbm_intmask,
+				   sizeof(pbm->pbm_intmask));
+	if (success == -1) {
+		prom_printf("%s: can't get interrupt-map-mask\n", __FUNCTION__);
+		prom_halt();
+	}
 }
 #endif
 
diff -u --recursive --new-file v2.1.96/linux/arch/sparc64/solaris/Makefile linux/arch/sparc64/solaris/Makefile
--- v2.1.96/linux/arch/sparc64/solaris/Makefile	Mon Jan 12 15:15:44 1998
+++ linux/arch/sparc64/solaris/Makefile	Tue Apr 14 17:44:21 1998
@@ -8,7 +8,7 @@
 # Note 2! The CFLAGS definition is now in the main makefile...
 
 O_TARGET := solaris.o
-O_OBJS   := entry64.o fs.o misc.o signal.o systbl.o ioctl.o ipc.o socksys.o
+O_OBJS   := entry64.o fs.o misc.o signal.o systbl.o ioctl.o ipc.o socksys.o timod.o
 ifeq ($(CONFIG_SOLARIS_EMUL),m)
 M_OBJS   := $(O_TARGET)
 CPPFLAGS = $(MODFLAGS)
diff -u --recursive --new-file v2.1.96/linux/arch/sparc64/solaris/conv.h linux/arch/sparc64/solaris/conv.h
--- v2.1.96/linux/arch/sparc64/solaris/conv.h	Mon Jan 12 15:15:44 1998
+++ linux/arch/sparc64/solaris/conv.h	Tue Apr 14 17:44:21 1998
@@ -1,10 +1,11 @@
-/* $Id: conv.h,v 1.2 1997/09/03 12:29:13 jj Exp $
+/* $Id: conv.h,v 1.3 1998/03/26 08:46:13 jj Exp $
  * conv.h: Utility macros for Solaris emulation
  *
  * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
  */
  
 /* #define DEBUG_SOLARIS */
+#define DEBUG_SOLARIS_KMALLOC
 
 #ifndef __ASSEMBLY__
 
@@ -24,5 +25,13 @@
 
 #define SYS(name) ((long)sys_call_table[__NR_##name])
 #define SUNOS(x) ((long)sunos_sys_table[x])
+
+#ifdef DEBUG_SOLARIS
+#define SOLD(s) printk("%s,%d,%s(): %s\n",__FILE__,__LINE__,__FUNCTION__,(s))
+#define SOLDD(s) printk("solaris: "); printk s
+#else
+#define SOLD(s)
+#define SOLDD(s)
+#endif
 
 #endif /* __ASSEMBLY__ */
diff -u --recursive --new-file v2.1.96/linux/arch/sparc64/solaris/entry64.S linux/arch/sparc64/solaris/entry64.S
--- v2.1.96/linux/arch/sparc64/solaris/entry64.S	Mon Jan 12 15:15:44 1998
+++ linux/arch/sparc64/solaris/entry64.S	Tue Apr 14 17:44:21 1998
@@ -1,7 +1,7 @@
-/* $Id: entry64.S,v 1.4 1997/09/09 17:13:50 jj Exp $
+/* $Id: entry64.S,v 1.5 1998/03/26 08:46:15 jj Exp $
  * entry64.S:  Solaris syscall emulation entry point.
  *
- * Copyright (C) 1996,1997 Jakub Jelinek   (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1996,1997,1998 Jakub Jelinek   (jj@sunsite.mff.cuni.cz)
  * Copyright (C) 1995,1997 David S. Miller (davem@caip.rutgers.edu)
  * Copyright (C) 1996 Miguel de Icaza      (miguel@nuclecu.unam.mx)
  */
@@ -29,8 +29,11 @@
 	mov		%i4, %o4
 	srl		%i1, 0, %o1
 	mov		%i5, %o5
-	b,pt		%xcc, 2f
+	andcc		%l3, 1, %g0
+	be,pt		%icc, 2f
 	 srl		%i2, 0, %o2
+	b,pt		%xcc, 2f
+	 add		%sp, STACK_BIAS + REGWIN_SZ, %o0
 
 solaris_sucks:
 /* Solaris is a big system which needs to be able to do all the things
@@ -59,9 +62,9 @@
 	 mov		%i4, %o4
 
 linux_syscall_for_solaris:
-	sll		%l7, 2, %l4
+	sll		%l3, 2, %l4
 	ba,pt		%xcc, 10f
-	 lduw		[%l6 + %l4], %l7
+	 lduw		[%l6 + %l4], %l3
 
 	/* Solaris system calls enter here... */
 	.align	32
@@ -78,18 +81,18 @@
 	cmp		%l0, 1
 	bne,pn		%icc, solaris_reg
 1:	 srl		%i0, 0, %o0
-	lduw		[%l7 + %l4], %l7
+	lduw		[%l7 + %l4], %l3
 	srl		%i1, 0, %o1
 	ldx		[%g6 + AOFF_task_flags], %l5
-	cmp		%l7, NR_SYSCALLS
+	cmp		%l3, NR_SYSCALLS
 	bleu,a,pn	%xcc, linux_syscall_for_solaris
 	 sethi		%hi(sys_call_table32), %l6
-	andcc		%l7, 1, %g0
+	andcc		%l3, 1, %g0
 	bne,a,pn	%icc, 10f
 	 add		%sp, STACK_BIAS + REGWIN_SZ, %o0
 10:	srl		%i2, 0, %o2
 	mov		%i5, %o5
-	andn		%l7, 3, %l7
+	andn		%l3, 3, %l7
 	andcc		%l5, 0x20, %g0				
 	bne,pn		%icc, solaris_syscall_trace		
 	 mov		%i0, %l5
@@ -110,12 +113,20 @@
 	andn		%g3, %g2, %g3
 	stx		%g3, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TSTATE]	
 	bne,pn		%icc, solaris_syscall_trace2
-	 ldx		[%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TNPC], %l1 ! pc = npc
-	add		%l1, 0x4, %l2				         !npc = npc+4
-	stx		%l1, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TPC]
-	clr		%l6
+	 ldx		[%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TNPC], %l1
+	andcc		%l1, 1, %g0
+	bne,pn		%icc, 2f
+	 clr		%l6
+	add		%l1, 0x4, %l2				         
+	stx		%l1, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TPC]	 ! pc = npc
+	call		rtrap
+	 stx		%l2, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TNPC] !npc = npc+4
+
+	/* When tnpc & 1, this comes from setcontext and we don't want to advance pc */
+2:	andn		%l1, 3, %l1
 	call		rtrap
-	 stx		%l2, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TNPC]
+	 stx		%l1, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TNPC] !npc = npc&~3
+
 1:
 	/* System call failure, set Carry condition code.
 	 * Also, get abs(errno) to return to the process.
@@ -134,15 +145,20 @@
 	mov		1, %l6
 	stx		%g3, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TSTATE]
 	bne,pn		%icc, solaris_syscall_trace2
-	 ldx		[%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TNPC], %l1 ! pc = npc
-	add		%l1, 0x4, %l2				         !npc = npc+4
-
-	stx		%l1, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TPC]
+	 ldx		[%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TNPC], %l1
+	andcc		%l1, 1, %g0
+	bne,pn		%icc, 2b
+	 add		%l1, 0x4, %l2
+	stx		%l1, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TPC]  ! pc = npc
 	call		rtrap
-	 stx		%l2, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TNPC]
+	 stx		%l2, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TNPC] !npc = npc+4 
+
 solaris_syscall_trace2:
 	call		syscall_trace
 	 add		%l1, 0x4, %l2			/* npc = npc+4 */
+	andcc		%l1, 1, %g0
+	bne,pn		%icc, 2b
+	 nop
 	stx		%l1, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TPC]
 	call		rtrap
 	 stx		%l2, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TNPC]
diff -u --recursive --new-file v2.1.96/linux/arch/sparc64/solaris/fs.c linux/arch/sparc64/solaris/fs.c
--- v2.1.96/linux/arch/sparc64/solaris/fs.c	Mon Jan 12 15:15:44 1998
+++ linux/arch/sparc64/solaris/fs.c	Tue Apr 14 17:44:21 1998
@@ -1,11 +1,13 @@
-/* $Id: fs.c,v 1.6 1997/10/13 03:54:05 davem Exp $
+/* $Id: fs.c,v 1.8 1998/03/29 10:11:02 davem Exp $
  * fs.c: fs related syscall emulation for Solaris
  *
  * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
  */
 
 #include <linux/types.h>
+#include <linux/sched.h>
 #include <linux/fs.h>
+#include <linux/file.h>
 #include <linux/stat.h>
 #include <linux/smp_lock.h>
 #include <linux/limits.h>
@@ -339,9 +341,12 @@
 	int error;
 
 	lock_kernel();
-	if (fd >= NR_OPEN || !(file = current->files->fd[fd]))
-		error = -EBADF;
-	else if (!(dentry = file->f_dentry))
+	error = -EBADF;
+	file = fget(fd);
+	if (!file)
+		goto out;
+
+	if (!(dentry = file->f_dentry))
 		error = -ENOENT;
 	else if (!(inode = dentry->d_inode))
 		error = -ENOENT;
@@ -351,6 +356,8 @@
 		error = -ENOSYS;
 	else
 		error = report_statvfs(inode, buf);
+	fput(file);
+out:
 	unlock_kernel();
 	return error;
 }
@@ -516,7 +523,7 @@
 		newattrs.ia_mode &= ~S_ISGID;
 		newattrs.ia_valid |= ATTR_MODE;
 	}
-	DQUOT_TRANSFER(inode, newattrs);
+	DQUOT_TRANSFER(dentry, newattrs);
 out:
 	return error;
 }
@@ -563,13 +570,15 @@
 	
 	lock_kernel();
 	retval = -EBADF;
-	if (fd >= NR_OPEN ||
-            !(file = current->files->fd[fd]))
+	file = fget(fd);
+	if (!file)
 		goto bad;
+
 	temp = file->f_pos;
 	if (temp != offset) {
 		retval = sys_lseek(fd, offset, 0);
-		if (retval < 0) goto bad;
+		if (retval < 0)
+			goto out_putf;
 	}
 	retval = sys_read(fd, (char *)A(buf), nbyte);
 	if (file->f_pos != temp) {
@@ -578,6 +587,9 @@
 		else
 			sys_lseek(fd, temp, 0);
 	}
+
+out_putf:
+	fput(file);
 bad:
 	unlock_kernel();
 	return retval;
@@ -595,13 +607,15 @@
 	
 	lock_kernel();
 	retval = -EBADF;
-	if (fd >= NR_OPEN ||
-            !(file = current->files->fd[fd]))
+        file = fget(fd);
+	if (!file)
 		goto bad;
+
 	temp = file->f_pos;
 	if (temp != offset) {
 		retval = sys_lseek(fd, offset, 0);
-		if (retval < 0) goto bad;
+		if (retval < 0)
+			goto out_putf;
 	}
 	retval = sys_write(fd, (char *)A(buf), nbyte);
 	if (file->f_pos != temp) {
@@ -610,6 +624,9 @@
 		else
 			sys_lseek(fd, temp, 0);
 	}
+
+out_putf:
+	fput(file);
 bad:
 	unlock_kernel();
 	return retval;
diff -u --recursive --new-file v2.1.96/linux/arch/sparc64/solaris/ioctl.c linux/arch/sparc64/solaris/ioctl.c
--- v2.1.96/linux/arch/sparc64/solaris/ioctl.c	Mon Jan 12 15:15:44 1998
+++ linux/arch/sparc64/solaris/ioctl.c	Tue Apr 14 17:44:21 1998
@@ -1,7 +1,8 @@
-/* $Id: ioctl.c,v 1.4 1997/09/18 10:38:24 rth Exp $
+/* $Id: ioctl.c,v 1.10 1998/03/29 10:11:00 davem Exp $
  * ioctl.c: Solaris ioctl emulation.
  *
  * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1997,1998 Patrik Rak (prak3264@ss1000.ms.mff.cuni.cz)
  *
  * Streams & timod emulation based on code
  * Copyright (C) 1995, 1996 Mike Jagdis (jaggy@purplet.demon.co.uk)
@@ -15,14 +16,16 @@
 #include <linux/smp_lock.h>
 #include <linux/ioctl.h>
 #include <linux/fs.h>
+#include <linux/file.h>
 #include <linux/netdevice.h>
 
 #include <asm/uaccess.h>
 #include <asm/termios.h>
 
 #include "conv.h"
+#include "socksys.h"
 
-extern char * getname32(u32 filename);
+extern char *getname32(u32 filename);
 #define putname32 putname
 
 extern asmlinkage int sys_ioctl(unsigned int fd, unsigned int cmd, 
@@ -31,6 +34,11 @@
 	u32 arg);
 asmlinkage int solaris_ioctl(unsigned int fd, unsigned int cmd, u32 arg);
 
+extern int timod_putmsg(unsigned int fd, char *ctl_buf, int ctl_len,
+			char *data_buf, int data_len, int flags);
+extern int timod_getmsg(unsigned int fd, char *ctl_buf, int ctl_maxlen, int *ctl_len,
+			char *data_buf, int data_maxlen, int *data_len, int *flags);
+
 /* termio* stuff {{{ */
 
 struct solaris_termios {
@@ -231,31 +239,303 @@
 	u32 data;
 };
 
+struct solaris_si_sockparams {
+	int sp_family;
+	int sp_type;
+	int sp_protocol;
+};
+
+struct solaris_o_si_udata {
+	int tidusize;
+	int addrsize;
+	int optsize;
+	int etsdusize;
+	int servtype;
+	int so_state;
+	int so_options;
+	int tsdusize;
+};
+
+struct solaris_si_udata {
+	int tidusize;
+	int addrsize;
+	int optsize;
+	int etsdusize;
+	int servtype;
+	int so_state;
+	int so_options;
+	int tsdusize;
+	struct solaris_si_sockparams sockparams;
+};
+
+#define SOLARIS_MODULE_TIMOD    0
+#define SOLARIS_MODULE_SOCKMOD  1
+#define SOLARIS_MODULE_MAX      2
+
+static struct module_info {
+        const char *name;
+        /* can be expanded further if needed */
+} module_table[ SOLARIS_MODULE_MAX + 1 ] = {
+        /* the ordering here must match the module numbers above! */
+        { "timod" },
+        { "sockmod" },
+        { NULL }
+};
+
+static inline int solaris_sockmod(unsigned int fd, unsigned int cmd, u32 arg)
+{
+	struct inode *ino;
+	/* I wonder which of these tests are superfluous... --patrik */
+	if (! current->files->fd[fd] ||
+	    ! current->files->fd[fd]->f_dentry ||
+	    ! (ino = current->files->fd[fd]->f_dentry->d_inode) ||
+	    ! ino->i_sock)
+		return TBADF;
+	
+	switch (cmd & 0xff) {
+	case 109: /* SI_SOCKPARAMS */
+	{
+		struct solaris_si_sockparams si;
+		if (copy_from_user (&si, (struct solaris_si_sockparams *) A(arg), sizeof(si)))
+			return (EFAULT << 8) | TSYSERR;
+
+		/* Should we modify socket ino->socket_i.ops and type? */
+		return 0;
+	}
+	case 110: /* SI_GETUDATA */
+	{
+		int etsdusize, servtype;
+		switch (ino->u.socket_i.type) {
+		case SOCK_STREAM:
+			etsdusize = 1;
+			servtype = 2;
+			break;
+		default:
+			etsdusize = -2;
+			servtype = 3;
+			break;
+		}
+		if (put_user(16384, &((struct solaris_si_udata *)A(arg))->tidusize) ||
+		    __put_user(sizeof(struct sockaddr), &((struct solaris_si_udata *)A(arg))->addrsize) ||
+		    __put_user(-1, &((struct solaris_si_udata *)A(arg))->optsize) ||
+		    __put_user(etsdusize, &((struct solaris_si_udata *)A(arg))->etsdusize) ||
+		    __put_user(servtype, &((struct solaris_si_udata *)A(arg))->servtype) ||
+		    __put_user(0, &((struct solaris_si_udata *)A(arg))->so_state) ||
+		    __put_user(0, &((struct solaris_si_udata *)A(arg))->so_options) ||
+		    __put_user(16384, &((struct solaris_si_udata *)A(arg))->tsdusize) ||
+		    __put_user(ino->u.socket_i.ops->family, &((struct solaris_si_udata *)A(arg))->sockparams.sp_family) ||
+		    __put_user(ino->u.socket_i.type, &((struct solaris_si_udata *)A(arg))->sockparams.sp_type) ||
+		    __put_user(ino->u.socket_i.ops->family, &((struct solaris_si_udata *)A(arg))->sockparams.sp_protocol))
+			return (EFAULT << 8) | TSYSERR;
+		return 0;
+	}
+	case 101: /* O_SI_GETUDATA */
+	{
+		int etsdusize, servtype;
+		switch (ino->u.socket_i.type) {
+		case SOCK_STREAM:
+			etsdusize = 1;
+			servtype = 2;
+			break;
+		default:
+			etsdusize = -2;
+			servtype = 3;
+			break;
+		}
+		if (put_user(16384, &((struct solaris_o_si_udata *)A(arg))->tidusize) ||
+		    __put_user(sizeof(struct sockaddr), &((struct solaris_o_si_udata *)A(arg))->addrsize) ||
+		    __put_user(-1, &((struct solaris_o_si_udata *)A(arg))->optsize) ||
+		    __put_user(etsdusize, &((struct solaris_o_si_udata *)A(arg))->etsdusize) ||
+		    __put_user(servtype, &((struct solaris_o_si_udata *)A(arg))->servtype) ||
+		    __put_user(0, &((struct solaris_o_si_udata *)A(arg))->so_state) ||
+		    __put_user(0, &((struct solaris_o_si_udata *)A(arg))->so_options) ||
+		    __put_user(16384, &((struct solaris_o_si_udata *)A(arg))->tsdusize))
+			return (EFAULT << 8) | TSYSERR;
+		return 0;
+	}
+	case 102: /* SI_SHUTDOWN */
+	case 103: /* SI_LISTEN */
+	case 104: /* SI_SETMYNAME */
+	case 105: /* SI_SETPEERNAME */
+	case 106: /* SI_GETINTRANSIT */
+	case 107: /* SI_TCL_LINK */
+	case 108: /* SI_TCL_UNLINK */
+	}
+	return TNOTSUPPORT;
+}
+
+static inline int solaris_timod(unsigned int fd, unsigned int cmd, u32 arg,
+                                    int len, int *len_p)
+{
+        struct file *filp;
+        struct inode *ino;
+	int ret;
+
+        filp = current->files->fd[fd];
+        if (! filp ||
+	    ! (ino = filp->f_dentry->d_inode) ||
+	    ! ino->i_sock)
+		return TBADF;
+		
+	switch (cmd & 0xff) {
+	case 141: /* TI_OPTMGMT */
+	{
+		int i;
+		u32 prim;
+		SOLD("TI_OPMGMT entry");
+		ret = timod_putmsg(fd, (char *)A(arg), len, NULL, -1, 0);
+		SOLD("timod_putmsg() returned");
+		if (ret)
+			return (-ret << 8) | TSYSERR;
+		i = MSG_HIPRI;
+		SOLD("calling timod_getmsg()");
+		ret = timod_getmsg(fd, (char *)A(arg), len, len_p, NULL, -1, NULL, &i);
+		SOLD("timod_getmsg() returned");
+		if (ret)
+			return (-ret << 8) | TSYSERR;
+		SOLD("ret ok");
+		if (get_user(prim, (u32 *)A(arg)))
+			return (EFAULT << 8) | TSYSERR;
+		SOLD("got prim");
+		if (prim == T_ERROR_ACK) {
+			u32 tmp, tmp2;
+			SOLD("prim is T_ERROR_ACK");
+			if (get_user(tmp, (u32 *)A(arg)+3) ||
+			    get_user(tmp2, (u32 *)A(arg)+2))
+				return (EFAULT << 8) | TSYSERR;
+			return (tmp2 << 8) | tmp;
+		}
+		SOLD("TI_OPMGMT return 0");
+		return 0;
+	}
+	case 142: /* TI_BIND */
+	{
+		int i;
+		u32 prim;
+		SOLD("TI_BIND entry");
+		ret = timod_putmsg(fd, (char *)A(arg), len, NULL, -1, 0);
+		SOLD("timod_putmsg() returned");
+		if (ret)
+			return (-ret << 8) | TSYSERR;
+		len = 1024; /* Solaris allows arbitrary return size */
+		i = MSG_HIPRI;
+		SOLD("calling timod_getmsg()");
+		ret = timod_getmsg(fd, (char *)A(arg), len, len_p, NULL, -1, NULL, &i);
+		SOLD("timod_getmsg() returned");
+		if (ret)
+			return (-ret << 8) | TSYSERR;
+		SOLD("ret ok");
+		if (get_user(prim, (u32 *)A(arg)))
+			return (EFAULT << 8) | TSYSERR;
+		SOLD("got prim");
+		if (prim == T_ERROR_ACK) {
+			u32 tmp, tmp2;
+			SOLD("prim is T_ERROR_ACK");
+			if (get_user(tmp, (u32 *)A(arg)+3) ||
+			    get_user(tmp2, (u32 *)A(arg)+2))
+				return (EFAULT << 8) | TSYSERR;
+			return (tmp2 << 8) | tmp;
+		}
+		SOLD("no ERROR_ACK requested");
+		if (prim != T_OK_ACK)
+			return TBADSEQ;
+		SOLD("OK_ACK requested");
+		i = MSG_HIPRI;
+		SOLD("calling timod_getmsg()");
+		ret = timod_getmsg(fd, (char *)A(arg), len, len_p, NULL, -1, NULL, &i);
+		SOLD("timod_getmsg() returned");
+		if (ret)
+			return (-ret << 8) | TSYSERR;
+		SOLD("TI_BIND return ok");
+		return 0;
+	}
+	case 140: /* TI_GETINFO */
+	case 143: /* TI_UNBIND */
+	case 144: /* TI_GETMYNAME */
+	case 145: /* TI_GETPEERNAME */
+	case 146: /* TI_SETMYNAME */
+	case 147: /* TI_SETPEERNAME */
+	}
+	return TNOTSUPPORT;
+}
+
 static inline int solaris_S(unsigned int fd, unsigned int cmd, u32 arg)
 {
 	char *p;
 	int ret;
 	mm_segment_t old_fs;
 	struct strioctl si;
-	
+	struct inode *ino;
+        struct file *filp;
+        struct sol_socket_struct *sock;
+        struct module_info *mi;
+
+        filp = current->files->fd[fd];
+        if (! filp ||
+	    ! (ino = filp->f_dentry->d_inode) ||
+	    ! ino->i_sock)
+		return -EBADF;
+        sock = filp->private_data;
+        if (! sock) {
+                printk("solaris_S: NULL private_data\n");
+                return -EBADF;
+        }
+        if (sock->magic != SOLARIS_SOCKET_MAGIC) {
+                printk("solaris_S: invalid magic\n");
+                return -EBADF;
+        }
+        
+
 	switch (cmd & 0xff) {
 	case 1: /* I_NREAD */
 		return -ENOSYS;
 	case 2: /* I_PUSH */
+        {
 		p = getname32 (arg);
 		if (IS_ERR (p))
 			return PTR_ERR(p);
+                ret = -EINVAL;
+                for (mi = module_table; mi->name; mi++) {
+                        if (strcmp(mi->name, p) == 0) {
+                                sol_module m;
+                                if (sock->modcount >= MAX_NR_STREAM_MODULES) {
+                                        ret = -ENXIO;
+                                        break;
+                                }
+                                m = (sol_module) (mi - module_table);
+                                sock->module[sock->modcount++] = m;
+                                ret = 0;
+                                break;
+                        }
+                }
 		putname32 (p);
-		return 0;
+		return ret;
+        }
 	case 3: /* I_POP */
+                if (sock->modcount <= 0) return -EINVAL;
+                sock->modcount--;
 		return 0;
+        case 4: /* I_LOOK */
+        {
+        	const char *p;
+                if (sock->modcount <= 0) return -EINVAL;
+                p = module_table[(unsigned)sock->module[sock->modcount]].name;
+                if (copy_to_user ((char *)A(arg), p, strlen(p)))
+                	return -EFAULT;
+                return 0;
+        }
 	case 5: /* I_FLUSH */
 		return 0;
 	case 8: /* I_STR */
-		if (copy_from_user (&si, (struct strioctl *)A(arg), sizeof(struct strioctl)))
+		if (copy_from_user(&si, (struct strioctl *)A(arg), sizeof(struct strioctl)))
 			return -EFAULT;
+                /* We ignore what module is actually at the top of stack. */
 		switch ((si.cmd >> 8) & 0xff) {
+		case 'I':
+                        return solaris_sockmod(fd, si.cmd, si.data);
 		case 'T':
+                        return solaris_timod(fd, si.cmd, si.data, si.len,
+                                                &((struct strioctl*)A(arg))->len);
 		default:
 			return solaris_ioctl(fd, si.cmd, si.data);
 		}
@@ -269,12 +549,25 @@
 		if (ret == current->pid) return 0x3ff;
 		else return -EINVAL;
 	case 11: /* I_FIND */
+        {
+                int i;
 		p = getname32 (arg);
 		if (IS_ERR (p))
 			return PTR_ERR(p);
-		ret = !strcmp(p, "timod");
+                ret = 0;
+                for (i = 0; i < sock->modcount; i++) {
+                        unsigned m = sock->module[i];
+                        if (strcmp(module_table[m].name, p) == 0) {
+                                ret = 1;
+                                break;
+                        } 
+                }
 		putname32 (p);
 		return ret;
+        }
+	case 19: /* I_SWROPT */
+	case 32: /* I_SETCLTIME */
+		return 0;	/* Lie */
 	}
 	return -ENOSYS;
 }
@@ -287,7 +580,8 @@
 		return 0; /* We don't support them */
 	case 1: /* SIOCGHIWAT */
 	case 3: /* SIOCGLOWAT */
-		put_user_ret (0, (u32 *)A(arg), -EFAULT);
+		if (put_user (0, (u32 *)A(arg)))
+			return -EFAULT;
 		return 0; /* Lie */
 	case 7: /* SIOCATMARK */
 		return sys_ioctl(fd, SIOCATMARK, arg);
@@ -368,8 +662,10 @@
 			ret = sys_socketcall(((cmd & 0xff) == 52) ? SYS_GETSOCKNAME : SYS_GETPEERNAME,
 					args);
 			set_fs(old_fs);
-			if (ret >= 0)
-				copy_to_user_ret((char *)A(arg), &uaddr, uaddr_len, -EFAULT);
+			if (ret >= 0) {
+				if (copy_to_user((char *)A(arg), &uaddr, uaddr_len))
+					return -EFAULT;
+			}
 			return ret;
 		}
 #if 0		
@@ -382,7 +678,8 @@
 			int i = 0;
 			
 			for (d = dev_base; d; d = d->next) i++;
-			put_user_ret (i, (int *)A(arg), -EFAULT);
+			if (put_user (i, (int *)A(arg)))
+				return -EFAULT;
 			return 0;
 		}
 	}
@@ -393,14 +690,13 @@
 
 asmlinkage int solaris_ioctl(unsigned int fd, unsigned int cmd, u32 arg)
 {
-	struct file * filp;
+	struct file *filp;
 	int error = -EBADF;
 
 	lock_kernel();
-	if(fd >= NR_OPEN) goto out;
-
-	filp = current->files->fd[fd];
-	if(!filp) goto out;
+	filp = fcheck(fd);
+	if (!filp)
+		goto out;
 
 	error = -EFAULT;
 	switch ((cmd >> 8) & 0xff) {
@@ -410,6 +706,7 @@
 	case 'r': error = solaris_r(fd, cmd, arg); break;
 	case 's': error = solaris_s(fd, cmd, arg); break;
 	case 't': error = solaris_t(fd, cmd, arg); break;
+	case 'f': error = sys_ioctl(fd, cmd, arg); break;
 	default:
 		error = -ENOSYS;
 		break;
diff -u --recursive --new-file v2.1.96/linux/arch/sparc64/solaris/misc.c linux/arch/sparc64/solaris/misc.c
--- v2.1.96/linux/arch/sparc64/solaris/misc.c	Mon Jan 12 15:15:44 1998
+++ linux/arch/sparc64/solaris/misc.c	Tue Apr 14 17:44:21 1998
@@ -1,4 +1,4 @@
-/* $Id: misc.c,v 1.6 1997/12/14 23:40:15 ecd Exp $
+/* $Id: misc.c,v 1.10 1998/04/01 05:16:06 davem Exp $
  * misc.c: Miscelaneous syscall emulation for Solaris
  *
  * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
@@ -106,7 +106,7 @@
 
 static char *platform(char *buffer)
 {
-	int i;
+	int i, len;
 	struct {
 		char *platform;
 		int id_machtype;
@@ -128,7 +128,9 @@
 	};
 
 	*buffer = 0;
-	prom_getproperty(prom_root_node, "name", buffer, 256);
+	len = prom_getproperty(prom_root_node, "name", buffer, 256);
+	if(len > 0)
+		buffer[len] = 0;
 	if (*buffer) {
 		char *p;
 
@@ -145,10 +147,13 @@
 static char *serial(char *buffer)
 {
 	int node = prom_getchild(prom_root_node);
+	int len;
 
 	node = prom_searchsiblings(node, "options");
 	*buffer = 0;
-	prom_getproperty(node, "system-board-serial#", buffer, 256);
+	len = prom_getproperty(node, "system-board-serial#", buffer, 256);
+	if(len > 0)
+		buffer[len] = 0;
 	if (!*buffer)
 		return "4512348717234";
 	else
@@ -268,6 +273,8 @@
 #define	SOLARIS_CONFIG_PHYS_PAGES		26
 #define	SOLARIS_CONFIG_AVPHYS_PAGES		27
 
+extern unsigned prom_cpu_nodes[NR_CPUS];
+
 asmlinkage int solaris_sysconf(int id)
 {
 	switch (id) {
@@ -279,9 +286,8 @@
 	case SOLARIS_CONFIG_XOPEN_VER:	return 3;
 	case SOLARIS_CONFIG_CLK_TCK:
 	case SOLARIS_CONFIG_PROF_TCK:
-		return prom_getintdefault(
-			linux_cpus[smp_processor_id()].prom_node,
-			"clock-frequency", 167000000);
+		return prom_getintdefault(prom_cpu_nodes[smp_processor_id()],
+					  "clock-frequency", 167000000);
 #ifdef __SMP__	
 	case SOLARIS_CONFIG_NPROC_CONF:	return NR_CPUS;
 	case SOLARIS_CONFIG_NPROC_ONLN:	return smp_num_cpus;
@@ -362,6 +368,14 @@
 	return -EINVAL;
 }
 
+asmlinkage int solaris_gettimeofday(u32 tim)
+{
+	int (*sys_gettimeofday)(struct timeval *, struct timezone *) =
+		(int (*)(struct timeval *, struct timezone *))SYS(gettimeofday);
+		
+	return sys_gettimeofday((struct timeval *)(u64)tim, NULL);
+}
+
 asmlinkage int do_sol_unimplemented(struct pt_regs *regs)
 {
 	printk ("Unimplemented Solaris syscall %d %08x %08x %08x %08x\n", 
@@ -401,10 +415,13 @@
 	NULL
 };
 
+extern int init_socksys(void);
+
 #ifdef MODULE
 
-MODULE_AUTHOR("Jakub Jelinek (jj@sunsite.mff.cuni.cz)");
+MODULE_AUTHOR("Jakub Jelinek (jj@ultra.linux.cz), Patrik Rak (prak3264@ss1000.ms.mff.cuni.cz)");
 MODULE_DESCRIPTION("Solaris binary emulation module");
+EXPORT_NO_SYMBOLS;
 
 #ifdef __sparc_v9__
 extern u32 tl0_solaris[8];
@@ -416,12 +433,13 @@
 
 extern u32 solaris_sparc_syscall[];
 extern u32 solaris_syscall[];
-extern int init_socksys(void);
 extern void cleanup_socksys(void);
 
 int init_module(void)
 {
 	int ret;
+
+	SOLDD(("Solaris module at %p\n", solaris_sparc_syscall));
 	register_exec_domain(&solaris_exec_domain);
 	if ((ret = init_socksys())) {
 		unregister_exec_domain(&solaris_exec_domain);
diff -u --recursive --new-file v2.1.96/linux/arch/sparc64/solaris/socksys.c linux/arch/sparc64/solaris/socksys.c
--- v2.1.96/linux/arch/sparc64/solaris/socksys.c	Mon Jan 12 15:15:44 1998
+++ linux/arch/sparc64/solaris/socksys.c	Tue Apr 14 17:44:21 1998
@@ -1,7 +1,8 @@
-/* $Id: socksys.c,v 1.2 1997/09/08 11:29:38 jj Exp $
+/* $Id: socksys.c,v 1.7 1998/03/29 10:11:04 davem Exp $
  * socksys.c: /dev/inet/ stuff for Solaris emulation.
  *
  * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1997, 1998 Patrik Rak (prak3264@ss1000.ms.mff.cuni.cz)
  * Copyright (C) 1995, 1996 Mike Jagdis (jaggy@purplet.demon.co.uk)
  */
 
@@ -12,14 +13,17 @@
 #include <linux/smp_lock.h>
 #include <linux/ioctl.h>
 #include <linux/fs.h>
+#include <linux/file.h>
 #include <linux/init.h>
 #include <linux/poll.h>
 #include <linux/file.h>
+#include <linux/malloc.h>
 
 #include <asm/uaccess.h>
 #include <asm/termios.h>
 
 #include "conv.h"
+#include "socksys.h"
 
 extern asmlinkage int sys_ioctl(unsigned int fd, unsigned int cmd, 
 	unsigned long arg);
@@ -30,6 +34,20 @@
 0, 0, 0, 0, 0, 0,
 };
 
+#ifndef DEBUG_SOLARIS_KMALLOC
+
+#define mykmalloc kmalloc
+#define mykfree kfree
+
+#else
+
+extern void * mykmalloc(size_t s, int gfp);
+extern void mykfree(void *);
+
+#endif
+
+static unsigned int (*sock_poll)(struct file *, poll_table *);
+
 static struct file_operations socksys_file_ops = {
 	NULL,		/* lseek */
 	NULL,		/* read */
@@ -48,6 +66,7 @@
 	struct dentry *dentry;
 	int (*sys_socket)(int,int,int) =
 		(int (*)(int,int,int))SUNOS(97);
+        struct sol_socket_struct * sock;
 	
 	family = ((MINOR(inode->i_rdev) >> 4) & 0xf);
 	switch (family) {
@@ -68,30 +87,78 @@
 		protocol = 0;
 		break;
 	}
+
 	fd = sys_socket(family, type, protocol);
-	if (fd < 0) return fd;
+	if (fd < 0)
+		return fd;
+	/*
+	 * N.B. The following operations are not legal!
+	 * Try instead:
+	 * d_delete(filp->f_dentry), then d_instantiate with sock inode
+	 */
 	dentry = filp->f_dentry;
-	filp->f_dentry = current->files->fd[fd]->f_dentry;
+	filp->f_dentry = dget(fcheck(fd)->f_dentry);
 	filp->f_dentry->d_inode->i_rdev = inode->i_rdev;
 	filp->f_dentry->d_inode->i_flock = inode->i_flock;
 	filp->f_dentry->d_inode->u.socket_i.file = filp;
 	filp->f_op = &socksys_file_ops;
+        sock = (struct sol_socket_struct*) 
+        	mykmalloc(sizeof(struct sol_socket_struct), GFP_KERNEL);
+        if (!sock) return -ENOMEM;
+	SOLDD(("sock=%016lx(%016lx)\n", sock, filp));
+        sock->magic = SOLARIS_SOCKET_MAGIC;
+        sock->modcount = 0;
+        sock->state = TS_UNBND;
+        sock->offset = 0;
+        sock->pfirst = sock->plast = NULL;
+        filp->private_data = sock;
+	SOLDD(("filp->private_data %016lx\n", filp->private_data));
+
+	sys_close(fd);
 	dput(dentry);
-	FD_CLR(fd, &current->files->close_on_exec);
-	FD_CLR(fd, &current->files->open_fds);
-	put_filp(current->files->fd[fd]);
-	current->files->fd[fd] = NULL;
 	return 0;
 }
 
 static int socksys_release(struct inode * inode, struct file * filp)
 {
+        struct sol_socket_struct * sock;
+        struct T_primsg *it;
+
+	/* XXX: check this */
+	sock = (struct sol_socket_struct *)filp->private_data;
+	SOLDD(("sock release %016lx(%016lx)\n", sock, filp));
+	it = sock->pfirst;
+	while (it) {
+		struct T_primsg *next = it->next;
+		
+		SOLDD(("socksys_release %016lx->%016lx\n", it, next));
+		mykfree((char*)it);
+		it = next;
+	}
+	filp->private_data = NULL;
+	SOLDD(("socksys_release %016lx\n", sock));
+	mykfree((char*)sock);
 	return 0;
 }
 
 static unsigned int socksys_poll(struct file * filp, poll_table * wait)
 {
-	return 0;
+	struct inode *ino;
+	unsigned int mask = 0;
+
+	ino=filp->f_dentry->d_inode;
+	if (ino && ino->i_sock) {
+		struct sol_socket_struct *sock;
+		sock = (struct sol_socket_struct*)filp->private_data;
+		if (sock && sock->pfirst) {
+			mask |= POLLIN | POLLRDNORM;
+			if (sock->pfirst->pri == MSG_HIPRI)
+				mask |= POLLPRI;
+		}
+	}
+	if (sock_poll)
+		mask |= (*sock_poll)(filp, wait);
+	return mask;
 }
 	
 static struct file_operations socksys_fops = {
@@ -110,6 +177,7 @@
 init_socksys(void))
 {
 	int ret;
+	struct file * file;
 	int (*sys_socket)(int,int,int) =
 		(int (*)(int,int,int))SUNOS(97);
 	int (*sys_close)(unsigned int) = 
@@ -125,8 +193,11 @@
 		printk ("Couldn't create socket\n");
 		return ret;
 	}
-	socksys_file_ops = *current->files->fd[ret]->f_op;
+	file = fcheck(ret);
+	/* N.B. Is this valid? Suppose the f_ops are in a module ... */
+	socksys_file_ops = *file->f_op;
 	sys_close(ret);
+	sock_poll = socksys_file_ops.poll;
 	socksys_file_ops.poll = socksys_poll;
 	socksys_file_ops.release = socksys_release;
 	return 0;
diff -u --recursive --new-file v2.1.96/linux/arch/sparc64/solaris/socksys.h linux/arch/sparc64/solaris/socksys.h
--- v2.1.96/linux/arch/sparc64/solaris/socksys.h	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/solaris/socksys.h	Tue Apr 14 17:44:21 1998
@@ -0,0 +1,208 @@
+/* $Id: socksys.h,v 1.2 1998/03/26 08:46:07 jj Exp $
+ * socksys.h: Definitions for STREAMS modules emulation code.
+ *
+ * Copyright (C) 1998 Patrik Rak (prak3264@ss1000.ms.mff.cuni.cz)
+ */
+
+#define MSG_HIPRI	0x01
+#define MSG_ANY		0x02
+#define MSG_BAND	0x04
+
+#define MORECTL		1
+#define MOREDATA	2
+
+#define	TBADADDR		1
+#define	TBADOPT			2
+#define	TACCES			3
+#define TBADF			4
+#define TNOADDR			5
+#define TOUTSTATE	        6
+#define TBADSEQ		        7
+#define TSYSERR			8
+#define TLOOK		        9
+#define TBADDATA	       10
+#define TBUFOVFLW	       11
+#define TFLOW		       12
+#define	TNODATA		       13
+#define TNODIS		       14
+#define TNOUDERR	       15
+#define TBADFLAG	       16
+#define TNOREL		       17
+#define TNOTSUPPORT	       18
+#define TSTATECHNG	       19
+
+#define T_CONN_REQ      0
+#define T_CONN_RES      1
+#define T_DISCON_REQ    2
+#define T_DATA_REQ      3
+#define T_EXDATA_REQ    4
+#define T_INFO_REQ      5
+#define T_BIND_REQ      6
+#define T_UNBIND_REQ    7
+#define T_UNITDATA_REQ  8
+#define T_OPTMGMT_REQ   9
+#define T_ORDREL_REQ    10
+
+#define T_CONN_IND      11
+#define T_CONN_CON      12
+#define T_DISCON_IND    13
+#define T_DATA_IND      14
+#define T_EXDATA_IND    15
+#define T_INFO_ACK      16
+#define T_BIND_ACK      17
+#define T_ERROR_ACK     18
+#define T_OK_ACK        19
+#define T_UNITDATA_IND  20
+#define T_UDERROR_IND   21
+#define T_OPTMGMT_ACK   22
+#define T_ORDREL_IND    23
+
+#define T_NEGOTIATE	0x0004
+#define T_FAILURE	0x0040
+
+#define TS_UNBND	0	/* unbound */
+#define	TS_WACK_BREQ	1	/* waiting for T_BIND_REQ ack  */
+#define TS_WACK_UREQ	2	/* waiting for T_UNBIND_REQ ack */
+#define TS_IDLE		3	/* idle */
+#define TS_WACK_OPTREQ	4	/* waiting for T_OPTMGMT_REQ ack */
+#define TS_WACK_CREQ	5	/* waiting for T_CONN_REQ ack */
+#define TS_WCON_CREQ	6	/* waiting for T_CONN_REQ confirmation */
+#define	TS_WRES_CIND	7	/* waiting for T_CONN_IND */
+#define TS_WACK_CRES	8	/* waiting for T_CONN_RES ack */
+#define TS_DATA_XFER	9	/* data transfer */
+#define TS_WIND_ORDREL	10	/* releasing read but not write */
+#define TS_WREQ_ORDREL	11      /* wait to release write but not read */
+#define TS_WACK_DREQ6	12	/* waiting for T_DISCON_REQ ack */
+#define TS_WACK_DREQ7	13	/* waiting for T_DISCON_REQ ack */
+#define TS_WACK_DREQ9	14	/* waiting for T_DISCON_REQ ack */
+#define TS_WACK_DREQ10	15	/* waiting for T_DISCON_REQ ack */
+#define TS_WACK_DREQ11	16	/* waiting for T_DISCON_REQ ack */
+#define TS_NOSTATES	17
+
+struct T_conn_req {
+	s32 PRIM_type; 
+	s32 DEST_length;
+	s32 DEST_offset;
+	s32 OPT_length;
+	s32 OPT_offset;
+};
+
+struct T_bind_req {
+	s32 PRIM_type;
+	s32 ADDR_length;
+	s32 ADDR_offset;
+	u32 CONIND_number;
+};
+
+struct T_unitdata_req {
+	s32 PRIM_type; 
+	s32 DEST_length;
+	s32 DEST_offset;
+	s32 OPT_length;
+	s32 OPT_offset;
+};
+
+struct T_optmgmt_req {
+	s32 PRIM_type; 
+	s32 OPT_length;
+	s32 OPT_offset;
+	s32 MGMT_flags;
+};
+
+struct T_bind_ack {
+	s32 PRIM_type;
+	s32 ADDR_length;
+	s32 ADDR_offset;
+	u32 CONIND_number;
+};
+
+struct T_error_ack {
+	s32 PRIM_type;
+	s32 ERROR_prim;
+	s32 TLI_error;
+	s32 UNIX_error;
+};
+
+struct T_ok_ack {
+	s32 PRIM_type;
+	s32 CORRECT_prim;
+};
+
+struct T_conn_ind {
+	s32 PRIM_type;
+	s32 SRC_length;
+	s32 SRC_offset;
+	s32 OPT_length;
+	s32 OPT_offset;
+	s32 SEQ_number;
+};
+
+struct T_conn_con {
+	s32 PRIM_type;
+	s32 RES_length;
+	s32 RES_offset;
+	s32 OPT_length;
+	s32 OPT_offset;
+};
+
+struct T_discon_ind {
+	s32 PRIM_type;
+	s32 DISCON_reason;
+	s32 SEQ_number;
+};
+
+struct T_unitdata_ind {
+	s32 PRIM_type;
+	s32 SRC_length;
+	s32 SRC_offset;
+	s32 OPT_length;
+	s32 OPT_offset;
+};
+
+struct T_optmgmt_ack {
+	s32 PRIM_type; 
+	s32 OPT_length;
+	s32 OPT_offset;
+	s32 MGMT_flags;
+};
+
+struct opthdr {
+	s32 level;
+	s32 name;
+	s32 len;
+	char value[0];	
+};
+
+struct T_primsg {
+	struct T_primsg *next;
+	unsigned char pri;
+	unsigned char band;
+	int length;
+	s32 type;
+};
+
+struct strbuf {
+	s32 maxlen;
+	s32 len;
+	u32 buf;
+} ;
+
+/* Constants used by STREAMS modules emulation code */
+
+typedef char sol_module;
+
+#define MAX_NR_STREAM_MODULES   16
+
+/* Private data structure assigned to sockets. */
+
+struct sol_socket_struct {
+        int magic;
+        int modcount;
+        sol_module module[MAX_NR_STREAM_MODULES];
+        long state;
+        int offset;
+        struct T_primsg *pfirst, *plast;
+};
+
+#define SOLARIS_SOCKET_MAGIC    0xADDED
+
diff -u --recursive --new-file v2.1.96/linux/arch/sparc64/solaris/systbl.S linux/arch/sparc64/solaris/systbl.S
--- v2.1.96/linux/arch/sparc64/solaris/systbl.S	Thu Sep  4 12:54:48 1997
+++ linux/arch/sparc64/solaris/systbl.S	Tue Apr 14 17:44:21 1998
@@ -1,4 +1,4 @@
-/* $Id: systbl.S,v 1.5 1997/09/04 15:46:24 jj Exp $
+/* $Id: systbl.S,v 1.6 1998/03/26 08:46:08 jj Exp $
  * systbl.S: System call entry point table for Solaris compatibility.
  *
  * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
@@ -14,11 +14,9 @@
 #define REGS(name)	name+1
 
 /* Hack till all be implemented */
-#define solaris_getmsg		solaris_unimplemented
 #define solaris_getpmsg		solaris_unimplemented
 #define solaris_hrtsys		solaris_unimplemented
 #define solaris_msgsys		solaris_unimplemented
-#define solaris_putmsg		solaris_unimplemented
 #define solaris_putpmsg		solaris_unimplemented
 #define solaris_semsys		solaris_unimplemented
 
@@ -115,8 +113,8 @@
 	.word solaris_unimplemented	/* libattach		82	*/
 	.word solaris_unimplemented	/* libdetach		83	*/
 	.word CHAIN(sysfs)		/* sysfs	dxx	84	*/
-	.word REGS(solaris_getmsg)	/* getmsg	dxxx	85	*/
-	.word REGS(solaris_putmsg)	/* putmsg	dxxd	86	*/
+	.word solaris_getmsg		/* getmsg	dxxx	85	*/
+	.word solaris_putmsg		/* putmsg	dxxd	86	*/
 	.word CHAIN(poll)		/* poll		xdd	87	*/
 	.word solaris_lstat		/* lstat	sp	88	*/
 	.word CHAIN(symlink)		/* symlink	ss	89	*/
@@ -186,7 +184,7 @@
 	.word solaris_unimplemented	/* fchroot	d	153	*/
 	.word solaris_unimplemented	/* lvlproc	dx	154	*/
 	.word solaris_unimplemented	/* ?			155	*/
-	.word CHAIN(gettimeofday)	/* gettimeofday	xx	156	*/
+	.word solaris_gettimeofday	/* gettimeofday	x	156	*/
 	.word CHAIN(getitimer)		/* getitimer	dx	157	*/
 	.word CHAIN(setitimer)		/* setitimer	dxx	158	*/
 	.word solaris_unimplemented	/* lwp-xxx		159	*/
diff -u --recursive --new-file v2.1.96/linux/arch/sparc64/solaris/timod.c linux/arch/sparc64/solaris/timod.c
--- v2.1.96/linux/arch/sparc64/solaris/timod.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/solaris/timod.c	Tue Apr 14 17:44:21 1998
@@ -0,0 +1,979 @@
+/* $Id: timod.c,v 1.1 1998/03/26 08:46:18 jj Exp $
+ * timod.c: timod emulation.
+ *
+ * Copyright (C) 1998 Patrik Rak (prak3264@ss1000.ms.mff.cuni.cz)
+ *
+ * Streams & timod emulation based on code
+ * Copyright (C) 1995, 1996 Mike Jagdis (jaggy@purplet.demon.co.uk)
+ *
+ */
+ 
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/ioctl.h>
+#include <linux/fs.h>
+#include <linux/netdevice.h>
+#include <linux/poll.h>
+
+#include <asm/uaccess.h>
+#include <asm/termios.h>
+
+#include "conv.h"
+#include "socksys.h"
+
+extern char *getname32(u32 filename);
+#define putname32 putname
+
+extern asmlinkage int sys_ioctl(unsigned int fd, unsigned int cmd, 
+	unsigned long arg);
+extern asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd,
+	u32 arg);
+asmlinkage int solaris_ioctl(unsigned int fd, unsigned int cmd, u32 arg);
+
+#ifdef __SMP__
+spinlock_t timod_pagelock = SPIN_LOCK_UNLOCKED;
+#endif
+static char * page = NULL ;
+
+#ifndef DEBUG_SOLARIS_KMALLOC
+
+#define mykmalloc kmalloc
+#define mykfree kfree
+
+#else
+
+void * mykmalloc(size_t s, int gfp)
+{
+	static char * page;
+	static size_t free = 0;
+	void * r;
+	s = ((s + 63) & ~63);
+	if( s > PAGE_SIZE ) {
+		SOLD("too big size, calling real kmalloc");
+		return kmalloc(s, gfp);
+	}
+	if( s > free ) {
+		/* we are wasting memory, but we don't care */
+		page = (char *)__get_free_page(gfp);
+		free = PAGE_SIZE;
+	}
+	r = page;
+	page += s;
+	free -= s;
+	return r;
+}
+
+void mykfree(void *p)
+{
+}
+
+#endif
+
+#ifndef DEBUG_SOLARIS
+
+#define BUF_SIZE	PAGE_SIZE
+#define PUT_MAGIC(a,m)
+#define CHECK_MAGIC(a,m)
+#define BUF_OFFSET	0
+#define MKCTL_TRAILER	0
+
+#else
+
+#define BUF_SIZE	(PAGE_SIZE-2*sizeof(u64))
+#define BUFPAGE_MAGIC	0xBADC0DEDDEADBABEL
+#define MKCTL_MAGIC	0xDEADBABEBADC0DEDL
+#define PUT_MAGIC(a,m)	do{(*(u64*)(a))=(m);}while(0)
+#define CHECK_MAGIC(a,m)	do{if((*(u64*)(a))!=(m))printk("%s,%u,%s(): magic %08x at %p corrupted!\n",\
+				__FILE__,__LINE__,__FUNCTION__,(m),(a));}while(0)
+#define BUF_OFFSET	sizeof(u64)
+#define MKCTL_TRAILER	sizeof(u64)
+
+#endif
+
+static char *getpage( void )
+{
+	char *r;
+	SOLD("getting page");
+	spin_lock(&timod_pagelock);
+	if (page) {
+		r = page;
+		page = NULL;
+		spin_unlock(&timod_pagelock);
+		SOLD("got cached");
+		return r + BUF_OFFSET;
+	}
+	spin_unlock(&timod_pagelock);
+	SOLD("getting new");
+	r = (char *)__get_free_page(GFP_KERNEL);
+	PUT_MAGIC(r,BUFPAGE_MAGIC);
+	PUT_MAGIC(r+PAGE_SIZE-sizeof(u64),BUFPAGE_MAGIC);
+	return r + BUF_OFFSET;
+}
+
+static void putpage(char *p)
+{
+	SOLD("putting page");
+	p = p - BUF_OFFSET;
+	CHECK_MAGIC(p,BUFPAGE_MAGIC);
+	CHECK_MAGIC(p+PAGE_SIZE-sizeof(u64),BUFPAGE_MAGIC);
+	spin_lock(&timod_pagelock);
+	if (page) {
+		spin_unlock(&timod_pagelock);
+		free_page((unsigned long)p);
+		SOLD("freed it");
+	} else {
+		page = p;
+		spin_unlock(&timod_pagelock);
+		SOLD("cached it");
+	}
+}
+
+static struct T_primsg *timod_mkctl(int size)
+{
+	struct T_primsg *it;
+
+	SOLD("creating primsg");
+	it = (struct T_primsg *)mykmalloc(size+sizeof(*it)-sizeof(s32)+2*MKCTL_TRAILER, GFP_KERNEL);
+	if (it) {
+		SOLD("got it");
+		it->pri = MSG_HIPRI;
+		it->length = size;
+		PUT_MAGIC((char*)((u64)(((char *)&it->type)+size+7)&~7),MKCTL_MAGIC);
+	}
+	return it;
+}
+
+static void timod_wake_socket(unsigned int fd)
+{
+	struct socket *sock;
+
+	SOLD("wakeing socket");
+	sock = &current->files->fd[fd]->f_dentry->d_inode->u.socket_i;
+	wake_up_interruptible(&sock->wait);
+	if (sock->fasync_list && !(sock->flags & SO_WAITDATA))
+		kill_fasync(sock->fasync_list, SIGIO);
+	SOLD("done");
+}
+
+static void timod_queue(unsigned int fd, struct T_primsg *it)
+{
+	struct sol_socket_struct *sock;
+
+	SOLD("queuing primsg");
+	sock = (struct sol_socket_struct *)current->files->fd[fd]->private_data;
+	it->next = sock->pfirst;
+	sock->pfirst = it;
+	if (!sock->plast)
+		sock->plast = it;
+	timod_wake_socket(fd);
+	SOLD("done");
+}
+
+static void timod_queue_end(unsigned int fd, struct T_primsg *it)
+{
+	struct sol_socket_struct *sock;
+
+	SOLD("queuing primsg at end");
+	sock = (struct sol_socket_struct *)current->files->fd[fd]->private_data;
+	it->next = NULL;
+	if (sock->plast)
+		sock->plast->next = it;
+	else
+		sock->pfirst = it;
+	sock->plast = it;
+	SOLD("done");
+}
+
+static void timod_error(unsigned int fd, int prim, int terr, int uerr)
+{
+	struct T_primsg *it;
+	
+	SOLD("making error");
+	it = timod_mkctl(sizeof(struct T_error_ack));
+	if (it) {
+		struct T_error_ack *err = (struct T_error_ack *)&it->type;
+		
+		SOLD("got it");
+		err->PRIM_type = T_ERROR_ACK;
+		err->ERROR_prim = prim;
+		err->TLI_error = terr;
+		err->UNIX_error = uerr; /* FIXME: convert this */
+		timod_queue(fd, it);
+	}
+	SOLD("done");
+}
+
+static void timod_ok(unsigned int fd, int prim)
+{
+	struct T_primsg *it;
+	struct T_ok_ack *ok;
+	
+	SOLD("creating ok ack");
+	it = timod_mkctl(sizeof(*ok));
+	if (it) {
+		SOLD("got it");
+		ok = (struct T_ok_ack *)&it->type;
+		ok->PRIM_type = T_OK_ACK;
+		ok->CORRECT_prim = prim;
+		timod_queue(fd, it);
+	}
+	SOLD("done");
+}
+
+static int timod_optmgmt(unsigned int fd, int flag, char *opt_buf, int opt_len, int do_ret)
+{
+	int error, failed;
+	int ret_space, ret_len;
+	long args[5];
+	char *ret_pos,*ret_buf;
+	int (*sys_socketcall)(int, unsigned long *) =
+		(int (*)(int, unsigned long *))SYS(socketcall);
+	mm_segment_t old_fs = get_fs();
+
+	SOLD("entry");
+	SOLDD(("fd %u flg %u buf %p len %u doret %u",fd,flag,opt_buf,opt_len,do_ret));
+	if (!do_ret && (!opt_buf || opt_len <= 0))
+		return 0;
+	SOLD("getting page");
+	ret_pos = ret_buf = getpage();
+	ret_space = BUF_SIZE;
+	ret_len = 0;
+	
+	error = failed = 0;
+	SOLD("looping");
+	while(opt_len >= sizeof(struct opthdr)) {
+		struct opthdr *opt;
+		int orig_opt_len; 
+		SOLD("loop start");
+		opt = (struct opthdr *)ret_pos; 
+		if (ret_space < sizeof(struct opthdr)) {
+			failed = TSYSERR;
+			break;
+		}
+		SOLD("getting opthdr");
+		if (copy_from_user(opt, opt_buf, sizeof(struct opthdr)) ||
+			opt->len > opt_len) {
+			failed = TBADOPT;
+			break;
+		}
+		SOLD("got opthdr");
+		if (flag == T_NEGOTIATE) {
+			char *buf;
+			
+			SOLD("handling T_NEGOTIATE");
+			buf = ret_pos + sizeof(struct opthdr);
+			if (ret_space < opt->len + sizeof(struct opthdr) ||
+				copy_from_user(buf, opt_buf+sizeof(struct opthdr), opt->len)) {
+				failed = TSYSERR;
+				break;
+			}
+			SOLD("got optdata");
+			args[0] = fd;
+			args[1] = opt->level;
+			args[2] = opt->name;
+			args[3] = (long)buf;
+			args[4] = opt->len;
+			SOLD("calling SETSOCKOPT");
+			set_fs(KERNEL_DS);
+			error = sys_socketcall(SYS_SETSOCKOPT, args);
+			set_fs(old_fs);
+			if (error) {
+				failed = TBADOPT;
+				break;
+			}
+			SOLD("SETSOCKOPT ok");
+		}
+		orig_opt_len = opt->len;
+		opt->len = ret_space - sizeof(struct opthdr);
+		if (opt->len < 0) {
+			failed = TSYSERR;
+			break;
+		}
+		args[0] = fd;
+		args[1] = opt->level;
+		args[2] = opt->name;
+		args[3] = (long)(ret_pos+sizeof(struct opthdr));
+		args[4] = (long)&opt->len;
+		SOLD("calling GETSOCKOPT");
+		set_fs(KERNEL_DS);
+		error = sys_socketcall(SYS_GETSOCKOPT, args);
+		set_fs(old_fs);;
+		if (error) {
+			failed = TBADOPT;
+			break;
+		}
+		SOLD("GETSOCKOPT ok");
+		ret_space -= sizeof(struct opthdr) + opt->len;
+		ret_len += sizeof(struct opthdr) + opt->len;
+		ret_pos += sizeof(struct opthdr) + opt->len;
+		opt_len -= sizeof(struct opthdr) + orig_opt_len;
+		opt_buf += sizeof(struct opthdr) + orig_opt_len;
+		SOLD("loop end");
+	}
+	SOLD("loop done");
+	if (do_ret) {
+		SOLD("generating ret msg");
+		if (failed)
+			timod_error(fd, T_OPTMGMT_REQ, failed, -error);
+		else {
+			struct T_primsg *it;
+			it = timod_mkctl(sizeof(struct T_optmgmt_ack) + ret_len);
+			if (it) {
+				struct T_optmgmt_ack *ack =
+					(struct T_optmgmt_ack *)&it->type;
+				SOLD("got primsg");
+				ack->PRIM_type = T_OPTMGMT_ACK;
+				ack->OPT_length = ret_len;
+				ack->OPT_offset = sizeof(struct T_optmgmt_ack);
+				ack->MGMT_flags = (failed ? T_FAILURE : flag);
+				memcpy(((char*)ack)+sizeof(struct T_optmgmt_ack),
+					ret_buf, ret_len);
+				timod_queue(fd, it);
+			}
+		}
+	}
+	SOLDD(("put_page %p\n", ret_buf));
+	putpage(ret_buf);
+	SOLD("done");	
+	return 0;
+}
+
+int timod_putmsg(unsigned int fd, char *ctl_buf, int ctl_len,
+			char *data_buf, int data_len, int flags)
+{
+	int ret, error, terror;
+	char *buf;
+	struct file *filp;
+	struct inode *ino;
+	struct sol_socket_struct *sock;
+	mm_segment_t old_fs = get_fs();
+	long args[6];
+	int (*sys_socketcall)(int, unsigned long *) =
+		(int (*)(int, unsigned long *))SYS(socketcall);
+	int (*sys_sendto)(int, void *, size_t, unsigned, struct sockaddr *, int) =
+		(int (*)(int, void *, size_t, unsigned, struct sockaddr *, int))SYS(sendto);
+	filp = current->files->fd[fd];
+	ino = filp->f_dentry->d_inode;
+	sock = (struct sol_socket_struct *)filp->private_data;
+	SOLD("entry");
+	if (get_user(ret, (int *)A(ctl_buf)))
+		return -EFAULT;
+	switch (ret) {
+	case T_BIND_REQ:
+	{
+		struct T_bind_req req;
+		
+		SOLDD(("bind %016lx(%016lx)\n", sock, filp));
+		SOLD("T_BIND_REQ");
+		if (sock->state != TS_UNBND) {
+			timod_error(fd, T_BIND_REQ, TOUTSTATE, 0);
+			return 0;
+		}
+		SOLD("state ok");
+		if (copy_from_user(&req, ctl_buf, sizeof(req))) {
+			timod_error(fd, T_BIND_REQ, TSYSERR, EFAULT);
+			return 0;
+		}
+		SOLD("got ctl req");
+		if (req.ADDR_offset && req.ADDR_length) {
+			if (req.ADDR_length > BUF_SIZE) {
+				timod_error(fd, T_BIND_REQ, TSYSERR, EFAULT);
+				return 0;
+			}
+			SOLD("req size ok");
+			buf = getpage();
+			if (copy_from_user(buf, ctl_buf + req.ADDR_offset, req.ADDR_length)) {
+				timod_error(fd, T_BIND_REQ, TSYSERR, EFAULT);
+				putpage(buf);
+				return 0;
+			}
+			SOLD("got ctl data");
+			args[0] = fd;
+			args[1] = (long)buf;
+			args[2] = req.ADDR_length;
+			SOLD("calling BIND");
+			set_fs(KERNEL_DS);
+			error = sys_socketcall(SYS_BIND, args);
+			set_fs(old_fs);
+			putpage(buf);
+			SOLD("BIND returned");
+		} else 
+			error = 0;
+		if (!error) {
+			struct T_primsg *it;
+			if (req.CONIND_number) {
+	  			args[0] = fd;
+  				args[1] = req.CONIND_number;
+  				SOLD("calling LISTEN");
+  				set_fs(KERNEL_DS);
+	  			error = sys_socketcall(SYS_LISTEN, args);
+  				set_fs(old_fs);
+  				SOLD("LISTEN done");
+  			}
+			it = timod_mkctl(sizeof(struct T_bind_ack)+sizeof(struct sockaddr));
+			if (it) {
+				struct T_bind_ack *ack;
+
+				ack = (struct T_bind_ack *)&it->type;
+				ack->PRIM_type = T_BIND_ACK;
+				ack->ADDR_offset = sizeof(*ack);
+				ack->ADDR_length = sizeof(struct sockaddr);
+				ack->CONIND_number = req.CONIND_number;
+				args[0] = fd;
+				args[1] = (long)(ack+sizeof(*ack));
+				args[2] = (long)&ack->ADDR_length;
+				set_fs(KERNEL_DS);
+				sys_socketcall(SYS_GETSOCKNAME,args);
+				set_fs(old_fs);
+				sock->state = TS_IDLE;
+				timod_ok(fd, T_BIND_REQ);
+				timod_queue_end(fd, it);
+				SOLD("BIND done");
+				return 0;
+			}
+		}
+		SOLD("some error");
+		switch (error) {
+			case -EINVAL:
+				terror = TOUTSTATE;
+				error = 0;
+				break;
+			case -EACCES:
+				terror = TACCES;
+				error = 0;
+				break;
+			case -EADDRNOTAVAIL:
+			case -EADDRINUSE:
+				terror = TNOADDR;
+				error = 0;
+				break;
+			default:
+				terror = TSYSERR;
+				break;
+		}
+		timod_error(fd, T_BIND_REQ, terror, -error);
+		SOLD("BIND done");
+		return 0;
+	}
+	case T_CONN_REQ:
+	{
+		struct T_conn_req req;
+		unsigned short oldflags;
+		struct T_primsg *it;
+		SOLD("T_CONN_REQ");
+		if (sock->state != TS_UNBND && sock->state != TS_IDLE) {
+			timod_error(fd, T_CONN_REQ, TOUTSTATE, 0);
+			return 0;
+		}
+		SOLD("state ok");
+		if (copy_from_user(&req, ctl_buf, sizeof(req))) {
+			timod_error(fd, T_CONN_REQ, TSYSERR, EFAULT);
+			return 0;
+		}
+		SOLD("got ctl req");
+		if (ctl_len > BUF_SIZE) {
+			timod_error(fd, T_CONN_REQ, TSYSERR, EFAULT);
+			return 0;
+		}
+		SOLD("req size ok");
+		buf = getpage();
+		if (copy_from_user(buf, ctl_buf, ctl_len)) {
+			timod_error(fd, T_CONN_REQ, TSYSERR, EFAULT);
+			putpage(buf);
+			return 0;
+		}
+#ifdef DEBUG_SOLARIS		
+		{
+			char * ptr = buf;
+			int len = ctl_len;
+			printk("returned data (%d bytes): ",len);
+			while( len-- ) {
+				if (!(len & 7))
+					printk(" ");
+				printk("%02x",(unsigned char)*ptr++);
+			}
+			printk("\n");
+		}
+#endif
+		SOLD("got ctl data");
+		args[0] = fd;
+		args[1] = (long)buf+req.DEST_offset;
+		args[2] = req.DEST_length;
+		oldflags = filp->f_flags;
+		filp->f_flags &= ~O_NONBLOCK;
+		SOLD("calling CONNECT");
+		set_fs(KERNEL_DS);
+		error = sys_socketcall(SYS_CONNECT, args);
+		set_fs(old_fs);
+		filp->f_flags = oldflags;
+		SOLD("CONNECT done");
+		if (!error) {
+			struct T_conn_con *con;
+			SOLD("no error");
+			it = timod_mkctl(ctl_len);
+			if (!it) {
+				putpage(buf);
+				return -ENOMEM;
+			}
+			con = (struct T_conn_con *)&it->type;
+#ifdef DEBUG_SOLARIS			
+			{
+				char * ptr = buf;
+				int len = ctl_len;
+				printk("returned data (%d bytes): ",len);
+				while( len-- ) {
+					if (!(len & 7))
+						printk(" ");
+					printk("%02x",(unsigned char)*ptr++);
+				}
+				printk("\n");
+			}
+#endif
+			memcpy(con, buf, ctl_len);
+			SOLD("copied ctl_buf");
+			con->PRIM_type = T_CONN_CON;
+			sock->state = TS_DATA_XFER;
+		} else {
+			struct T_discon_ind *dis;
+			SOLD("some error");
+			it = timod_mkctl(sizeof(*dis));
+			if (!it) {
+				putpage(buf);
+				return -ENOMEM;
+			}
+			SOLD("got primsg");
+			dis = (struct T_discon_ind *)&it->type;
+			dis->PRIM_type = T_DISCON_IND;
+			dis->DISCON_reason = -error;	/* FIXME: convert this as in iABI_errors() */
+			dis->SEQ_number = 0;
+		}
+		putpage(buf);
+		timod_ok(fd, T_CONN_REQ);
+		it->pri = 0;
+		timod_queue_end(fd, it);
+		SOLD("CONNECT done");
+		return 0;
+	}
+	case T_OPTMGMT_REQ:
+	{
+		struct T_optmgmt_req req;
+		SOLD("OPTMGMT_REQ");
+		if (copy_from_user(&req, ctl_buf, sizeof(req)))
+			return -EFAULT;
+		SOLD("got req");
+		return timod_optmgmt(fd, req.MGMT_flags,
+				req.OPT_offset > 0 ? ctl_buf + req.OPT_offset : NULL,
+				req.OPT_length, 1);
+	}
+	case T_UNITDATA_REQ:
+	{
+		struct T_unitdata_req req;
+		
+		int err;
+		SOLD("T_UNITDATA_REQ");
+		if (sock->state != TS_IDLE && sock->state != TS_DATA_XFER) {
+			timod_error(fd, T_CONN_REQ, TOUTSTATE, 0);
+			return 0;
+		}
+		SOLD("state ok");
+		if (copy_from_user(&req, ctl_buf, sizeof(req))) {
+			timod_error(fd, T_CONN_REQ, TSYSERR, EFAULT);
+			return 0;
+		}
+		SOLD("got ctl req");
+#ifdef DEBUG_SOLARIS		
+		{
+			char * ptr = ctl_buf+req.DEST_offset;
+			int len = req.DEST_length;
+			printk("socket address (%d bytes): ",len);
+			while( len-- ) {
+				char c;
+				if (get_user(c,ptr))
+					printk("??");
+				else
+					printk("%02x",(unsigned char)c);
+				ptr++;
+			}
+			printk("\n");
+		}
+#endif		
+		err = sys_sendto(fd, data_buf, data_len, 0, req.DEST_length > 0 ? (struct sockaddr*)(ctl_buf+req.DEST_offset) : NULL, req.DEST_length);
+		if (err == data_len)
+			return 0;
+		if(err >= 0) {
+			printk("timod: sendto failed to send all the data\n");
+			return 0;
+		}
+		timod_error(fd, T_CONN_REQ, TSYSERR, -err);
+		return 0;
+	}
+	default:
+		printk("timod_putmsg: unsuported command %u.\n", ret);
+		break;
+	}
+	return -EINVAL;
+}
+
+/* copied directly from fs/select.c */
+
+static void free_wait(poll_table * p)
+{
+	struct poll_table_entry * entry = p->entry + p->nr;
+
+	SOLD("entry");
+	while (p->nr > 0) {
+		p->nr--;
+		entry--;
+		remove_wait_queue(entry->wait_address,&entry->wait);
+	}
+	SOLD("done");
+}
+
+
+int timod_getmsg(unsigned int fd, char *ctl_buf, int ctl_maxlen, s32 *ctl_len,
+			char *data_buf, int data_maxlen, s32 *data_len, int *flags_p)
+{
+	int error;
+	int oldflags;
+	struct file *filp;
+	struct inode *ino;
+	struct sol_socket_struct *sock;
+	struct T_unitdata_ind udi;
+	mm_segment_t old_fs = get_fs();
+	long args[6];
+	char *tmpbuf;
+	int tmplen;
+	int (*sys_socketcall)(int, unsigned long *) =
+		(int (*)(int, unsigned long *))SYS(socketcall);
+	int (*sys_recvfrom)(int, void *, size_t, unsigned, struct sockaddr *, int *);
+	
+	SOLD("entry");
+	SOLDD(("%u %p %d %p %p %d %p %d\n", fd, ctl_buf, ctl_maxlen, ctl_len, data_buf, data_maxlen, data_len, *flags_p));
+	filp = current->files->fd[fd];
+	ino = filp->f_dentry->d_inode;
+	sock = (struct sol_socket_struct *)filp->private_data;
+	SOLDD(("%p %p\n", sock->pfirst, sock->pfirst ? sock->pfirst->next : NULL));
+	if ( ctl_maxlen > 0 && !sock->pfirst && ino->u.socket_i.type == SOCK_STREAM
+		&& sock->state == TS_IDLE) {
+		SOLD("calling LISTEN");
+		args[0] = fd;
+		args[1] = -1;
+		set_fs(KERNEL_DS);
+		sys_socketcall(SYS_LISTEN, args);
+		set_fs(old_fs);
+		SOLD("LISTEN done");
+	}
+	if (!(filp->f_flags & O_NONBLOCK)) {
+		poll_table wait_table, *wait;
+		struct poll_table_entry *entry;
+		SOLD("getting poll_table");
+		entry = (struct poll_table_entry *)__get_free_page(GFP_KERNEL);
+		if (!entry)
+			return -ENOMEM;
+		SOLD("got one");
+		wait_table.nr = 0;
+		wait_table.entry = entry;
+		wait = &wait_table;
+		for(;;) {
+			SOLD("loop");
+			current->state = TASK_INTERRUPTIBLE;
+			/* ! ( l<0 || ( l>=0 && ( ! pfirst || (flags == HIPRI && pri != HIPRI) ) ) ) */ 
+			/* ( ! l<0 && ! ( l>=0 && ( ! pfirst || (flags == HIPRI && pri != HIPRI) ) ) ) */ 
+			/* ( l>=0 && ( ! l>=0 || ! ( ! pfirst || (flags == HIPRI && pri != HIPRI) ) ) ) */ 
+			/* ( l>=0 && ( l<0 || ( pfirst && ! (flags == HIPRI && pri != HIPRI) ) ) ) */ 
+			/* ( l>=0 && ( l<0 || ( pfirst && (flags != HIPRI || pri == HIPRI) ) ) ) */ 
+			/* ( l>=0 && ( pfirst && (flags != HIPRI || pri == HIPRI) ) ) */ 
+			if (ctl_maxlen >= 0 && sock->pfirst && (*flags_p != MSG_HIPRI || sock->pfirst->pri == MSG_HIPRI))
+				break;
+			SOLD("cond 1 passed");
+			if (
+			#if 1
+				*flags_p != MSG_HIPRI &&
+			#endif
+				((filp->f_op->poll(filp, wait) & POLLIN) ||
+				(filp->f_op->poll(filp, NULL) & POLLIN) ||
+				signal_pending(current))
+			) {
+				break;
+			}
+			if( *flags_p == MSG_HIPRI ) {
+				SOLD("avoiding lockup");
+				break ;
+			}
+			SOLD("scheduling");
+			schedule();
+		}
+		SOLD("loop done");
+		current->state = TASK_RUNNING;
+		free_wait(&wait_table);
+		free_page((unsigned long)entry);
+		if (signal_pending(current)) {
+			SOLD("signal pending");
+			return -EINTR;
+		}
+	}
+	if (ctl_maxlen >= 0 && sock->pfirst) {
+		struct T_primsg *it = sock->pfirst;
+#ifndef min
+#define min(a,b) ((a)<(b)?(a):(b))
+#endif
+		int l = min(ctl_maxlen, it->length);
+		CHECK_MAGIC((char*)((u64)(((char *)&it->type)+sock->offset+it->length+7)&~7),MKCTL_MAGIC);
+		SOLD("purting ctl data");
+		if(copy_to_user(ctl_buf,
+			(char*)&it->type + sock->offset, l))
+			return -EFAULT;
+		SOLD("pur it");
+		if(put_user(l, ctl_len))
+			return -EFAULT;
+		SOLD("set ctl_len");
+		*flags_p = it->pri;
+		it->length -= l;
+		if (it->length) {
+			SOLD("more ctl");
+			sock->offset += l;
+			return MORECTL;
+		} else {
+			SOLD("removing message");
+			sock->pfirst = it->next;
+			if (!sock->pfirst)
+				sock->plast = NULL;
+			SOLDD(("getmsg kfree %016lx->%016lx\n", it, sock->pfirst));
+			mykfree(it);
+			sock->offset = 0;
+			SOLD("ctl done");
+			return 0;
+		}
+	}
+	*flags_p = 0;
+	if (ctl_maxlen >= 0) {
+		SOLD("ACCEPT perhaps?");
+		if (ino->u.socket_i.type == SOCK_STREAM && sock->state == TS_IDLE) {
+			struct T_conn_ind ind;
+			char *buf = getpage();
+			int len = BUF_SIZE;
+
+			SOLD("trying ACCEPT");
+			if (put_user(ctl_maxlen - sizeof(ind), ctl_len))
+				return -EFAULT;
+			args[0] = fd;
+			args[1] = (long)buf;
+			args[2] = (long)&len;
+			oldflags = filp->f_flags;
+			filp->f_flags |= O_NONBLOCK;
+			SOLD("calling ACCEPT");
+			set_fs(KERNEL_DS);
+			error = sys_socketcall(SYS_ACCEPT, args);
+			set_fs(old_fs);
+			filp->f_flags = oldflags;
+			if (error < 0) {
+				SOLD("some error");
+				putpage(buf);
+				return error;
+			}
+			if (error) {
+				SOLD("connect");
+				putpage(buf);
+				if (sizeof(ind) > ctl_maxlen) {
+					SOLD("generating CONN_IND");
+					ind.PRIM_type = T_CONN_IND;
+					ind.SRC_length = len;
+					ind.SRC_offset = sizeof(ind);
+					ind.OPT_length = ind.OPT_offset = 0;
+					ind.SEQ_number = error;
+					if(copy_to_user(ctl_buf, &ind, sizeof(ind))||
+					   put_user(sizeof(ind)+ind.SRC_length,ctl_len))
+						return -EFAULT;
+					SOLD("CONN_IND created");
+				}
+				if (data_maxlen >= 0)
+					put_user(0, data_len);
+				SOLD("CONN_IND done");
+				return 0;
+			}
+			if (len>ctl_maxlen) {
+				SOLD("data don't fit");
+				putpage(buf);
+				return -EFAULT;		/* XXX - is this ok ? */
+			}
+			if(copy_to_user(ctl_buf,buf,len) || put_user(len,ctl_len)){
+				SOLD("can't copy data");
+				putpage(buf);
+				return -EFAULT;
+			}
+			SOLD("ACCEPT done");
+			putpage(buf);
+		}
+	}
+	SOLD("checking data req");
+	if (data_maxlen <= 0) {
+		if (data_maxlen == 0)
+			put_user(0, data_len);
+		if (ctl_maxlen >= 0)
+			put_user(0, ctl_len);
+		return -EAGAIN;
+	}
+	SOLD("wants data");
+	if (ctl_maxlen > sizeof(udi) && sock->state == TS_IDLE) {
+		SOLD("udi fits");
+		tmpbuf = ctl_buf + sizeof(udi);
+		tmplen = ctl_maxlen - sizeof(udi);
+	} else {
+		SOLD("udi does not fit");
+		tmpbuf = NULL;
+		tmplen = 0;
+	}
+	if (put_user(tmplen, ctl_len))
+		return -EFAULT;
+	SOLD("set ctl_len");
+	oldflags = filp->f_flags;
+	filp->f_flags |= O_NONBLOCK;
+	SOLD("calling recvfrom");
+	sys_recvfrom = (int (*)(int, void *, size_t, unsigned, struct sockaddr *, int *))SYS(recvfrom);
+	error = sys_recvfrom(fd, data_buf, min(0,data_maxlen), 0, (struct sockaddr*)tmpbuf, ctl_len);
+	filp->f_flags = oldflags;
+	if (error < 0)
+		return error;
+	SOLD("error >= 0" ) ;
+	if (error && ctl_maxlen > sizeof(udi) && sock->state == TS_IDLE) {
+		SOLD("generating udi");
+		udi.PRIM_type = T_UNITDATA_IND;
+		get_user(udi.SRC_length, ctl_len);
+		udi.SRC_offset = sizeof(udi);
+		udi.OPT_length = udi.OPT_offset = 0;
+		copy_to_user(ctl_buf, &udi, sizeof(udi));
+		put_user(sizeof(udi)+udi.SRC_length, ctl_len);
+		SOLD("udi done");
+	} else
+		put_user(0, ctl_len);
+	put_user(error, data_len);
+	SOLD("done");
+	return 0;
+}
+
+asmlinkage int solaris_getmsg(unsigned int fd, u32 arg1, u32 arg2, u32 arg3)
+{
+	struct file *filp;
+	struct inode *ino;
+	struct strbuf *ctlptr, *datptr;
+	struct strbuf ctl, dat;
+	int *flgptr;
+	int flags;
+	int error = -EBADF;
+
+	SOLD("entry");
+	lock_kernel();
+	if(fd >= NR_OPEN) goto out;
+
+	filp = current->files->fd[fd];
+	if(!filp) goto out;
+
+	ino = filp->f_dentry->d_inode;
+	if (!ino) goto out;
+
+	if (!ino->i_sock)
+		goto out;
+
+	ctlptr = (struct strbuf *)A(arg1);
+	datptr = (struct strbuf *)A(arg2);
+	flgptr = (int *)A(arg3);
+
+	error = -EFAULT;
+
+	if (ctlptr) {
+		if (copy_from_user(&ctl,ctlptr,sizeof(struct strbuf)) || 
+		    put_user(-1,&ctlptr->len))
+			goto out;
+	} else
+		ctl.maxlen = -1;
+
+	if (datptr) {
+		if (copy_from_user(&dat,datptr,sizeof(struct strbuf)) || 
+		    put_user(-1,&datptr->len))
+			goto out;
+	} else
+		dat.maxlen = -1;
+
+	if (get_user(flags,flgptr))
+		goto out;
+
+	switch (flags) {
+	case 0:
+	case MSG_HIPRI:
+	case MSG_ANY:
+	case MSG_BAND:
+		break;
+	default:
+		error = -EINVAL;
+		goto out;
+	}
+
+	error = timod_getmsg(fd,(char*)A(ctl.buf),ctl.maxlen,&ctlptr->len,
+				(char*)A(dat.buf),dat.maxlen,&datptr->len,&flags);
+
+	if (!error && put_user(flags,flgptr))
+		error = -EFAULT;
+out:
+	unlock_kernel();
+	SOLD("done");
+	return error;
+}
+
+asmlinkage int solaris_putmsg(unsigned int fd, u32 arg1, u32 arg2, u32 arg3)
+{
+	struct file *filp;
+	struct inode *ino;
+	struct strbuf *ctlptr, *datptr;
+	struct strbuf ctl, dat;
+	int flags = (int) arg3;
+	int error = -EBADF;
+
+	SOLD("entry");
+	lock_kernel();
+	if(fd >= NR_OPEN) goto out;
+
+	filp = current->files->fd[fd];
+	if(!filp) goto out;
+
+	ino = filp->f_dentry->d_inode;
+	if (!ino) goto out;
+
+	if (!ino->i_sock &&
+		(MAJOR(ino->i_rdev) != 30 || MINOR(ino->i_rdev) != 1))
+		goto out;
+
+	ctlptr = (struct strbuf *)A(arg1);
+	datptr = (struct strbuf *)A(arg2);
+
+	error = -EFAULT;
+
+	if (ctlptr) {
+		if (copy_from_user(&ctl,ctlptr,sizeof(ctl)))
+			goto out;
+		if (ctl.len < 0 && flags) {
+			error = -EINVAL;
+			goto out;
+		}
+	} else {
+		ctl.len = 0;
+		ctl.buf = 0;
+	}
+
+	if (datptr) {
+		if (copy_from_user(&dat,datptr,sizeof(dat)))
+			goto out;
+	} else {
+		dat.len = 0;
+		dat.buf = 0;
+	}
+
+	error = timod_putmsg(fd,(char*)A(ctl.buf),ctl.len,
+				(char*)A(dat.buf),dat.len,flags);
+out:
+	unlock_kernel();
+	SOLD("done");
+	return error;
+}
diff -u --recursive --new-file v2.1.96/linux/arch/sparc64/vmlinux.lds linux/arch/sparc64/vmlinux.lds
--- v2.1.96/linux/arch/sparc64/vmlinux.lds	Sat Aug 16 09:51:09 1997
+++ linux/arch/sparc64/vmlinux.lds	Tue Apr 14 17:44:21 1998
@@ -52,12 +52,9 @@
    . += 8192;
    empty_bad_pte_table = .;
    . += 8192;
-   empty_bad_page = .;
-   . += 8192;
    . += 0x40;
    empty_null_pmd_table = .;
    . += 8192;
-   . += 0x40;
    empty_null_pte_table = .;
    . += 8192;
   }
diff -u --recursive --new-file v2.1.96/linux/drivers/block/ide-dma.c linux/drivers/block/ide-dma.c
--- v2.1.96/linux/drivers/block/ide-dma.c	Mon Apr  6 17:40:59 1998
+++ linux/drivers/block/ide-dma.c	Fri Apr 17 21:58:48 1998
@@ -333,7 +333,7 @@
  */
 __initfunc(unsigned long ide_get_or_set_dma_base (struct pci_dev *dev, ide_hwif_t *hwif, int extra, const char *name))
 {
-	unsigned long new, dma_base = 0;
+	unsigned long dma_base = 0;
 
 	if (hwif->mate && hwif->mate->dma_base) {
 		dma_base = hwif->mate->dma_base - (hwif->channel ? 0 : 8);
@@ -341,19 +341,7 @@
 		dma_base = dev->base_address[4] & PCI_BASE_ADDRESS_IO_MASK;
 		if (!dma_base || dma_base == PCI_BASE_ADDRESS_IO_MASK) {
 			printk("%s: dma_base is invalid (0x%04lx, BIOS problem), please report to <mj@ucw.cz>\n", name, dma_base);
-			new = ide_find_free_region(16 + extra);
-			hwif->no_autodma = 1;	/* default DMA off if we had to configure it here */
-			if (new) {
-				printk("%s: setting dma_base to 0x%04lx\n", name, new);
-				new |= PCI_BASE_ADDRESS_SPACE_IO;
-				(void) pci_write_config_dword(dev, PCI_BASE_ADDRESS_4, new);
-				dma_base = 0;
-				(void) pci_read_config_dword(dev, PCI_BASE_ADDRESS_4, (unsigned int *) &dma_base);
-				if (dma_base != new) {
-					printk("%s: Operation failed, DMA disabled\n", name);
-					dma_base = 0;
-				}
-			}
+			dma_base = 0;
 		}
 	}
 	if (dma_base) {
diff -u --recursive --new-file v2.1.96/linux/drivers/block/ide-pci.c linux/drivers/block/ide-pci.c
--- v2.1.96/linux/drivers/block/ide-pci.c	Mon Apr  6 17:40:59 1998
+++ linux/drivers/block/ide-pci.c	Fri Apr 17 21:58:48 1998
@@ -116,31 +116,6 @@
 	{IDE_PCI_DEVID_NULL, "PCI_IDE",	NULL,		{{0x00,0x00,0x00}, {0x00,0x00,0x00}} }};
 
 /*
- * Search for an (apparently) unused block of I/O space
- * of "size" bytes in length.  Ideally we ought to do a pass
- * through pcicfg space to eliminate ports already allocated
- * by the BIOS, to avoid conflicts later in the init cycle,
- * but we don't.	FIXME
- */
-unsigned long ide_find_free_region (unsigned short size) /* __init */
-{
-	static unsigned short base = 0x5800;	/* it works for me */
-	unsigned short i;
-
-	for (; base > 0; base -= 0x200) {
-		if (!check_region(base,size)) {
-			for (i = 0; i < size; i++) {
-				if (inb(base+i) != 0xff)
-					goto next;
-			}
-			return base;	/* success */
-		}
-	next:
-	}
-	return 0;	/* failure */
-}
-
-/*
  * Match a PCI IDE port against an entry in ide_hwifs[],
  * based on io_base port if possible.
  */
@@ -197,7 +172,6 @@
 
 __initfunc(static int ide_setup_pci_baseregs (struct pci_dev *dev, const char *name))
 {
-	unsigned int base, readback;
 	byte reg, progif = 0;
 
 	/*
@@ -218,16 +192,11 @@
 	/*
 	 * Setup base registers for IDE command/control spaces for each interface:
 	 */
-	if (!(base = ide_find_free_region(32)))
-		return 1;
-	for (reg = 0; reg < 4; reg++, base += 8) {
-		(void) pci_write_config_dword(dev, PCI_BASE_ADDRESS_0 + reg, base | PCI_BASE_ADDRESS_SPACE_IO);
-		if (pci_read_config_dword(dev, PCI_BASE_ADDRESS_0 + reg, &readback) ||
-		    readback != (base | PCI_BASE_ADDRESS_SPACE_IO)) {
-			printk("%s: readback failed for basereg 0x%02x: wrote 0x%04x, read 0x%x04\n", name, reg, base, readback);
+	for (reg = 0; reg < 4; reg++)
+		if (!dev->base_address[reg]) {
+			printk("%s: Missing I/O address #%d, please report to <mj@ucw.cz>\n", name, reg);
 			return 1;
 		}
-	}
 	return 0;
 }
 
diff -u --recursive --new-file v2.1.96/linux/drivers/block/ide-proc.c linux/drivers/block/ide-proc.c
--- v2.1.96/linux/drivers/block/ide-proc.c	Wed Apr  8 19:36:25 1998
+++ linux/drivers/block/ide-proc.c	Fri Apr 17 21:58:48 1998
@@ -226,7 +226,7 @@
 						restore_flags(flags);
 						printk("proc_ide_write_config: error writing %s at bus %02x dev %02x reg 0x%x value 0x%x\n",
 							msg, dev->bus->number, dev->devfn, reg, val);
-						printk("proc_ide_write_config: %s\n", pcibios_strerror(rc));
+						printk("proc_ide_write_config: error %d\n", rc);
 						return -EIO;
 					}
 #endif	/* CONFIG_BLK_DEV_IDEPCI */
@@ -271,7 +271,7 @@
 		if (rc) {
 			printk("proc_ide_read_config: error reading bus %02x dev %02x reg 0x%02x\n",
 				dev->bus->number, dev->devfn, reg);
-			printk("proc_ide_read_config: %s\n", pcibios_strerror(rc));
+			printk("proc_ide_read_config: error %d\n", rc);
 			return -EIO;
 			out += sprintf(out, "??%c", (++reg & 0xf) ? ' ' : '\n');
 		} else
diff -u --recursive --new-file v2.1.96/linux/drivers/char/Config.in linux/drivers/char/Config.in
--- v2.1.96/linux/drivers/char/Config.in	Wed Apr  8 19:36:25 1998
+++ linux/drivers/char/Config.in	Fri Apr 17 22:04:44 1998
@@ -16,6 +16,7 @@
 if [ "$CONFIG_SERIAL_EXTENDED" = "y" ]; then
    bool '   Support more than 4 serial ports' CONFIG_SERIAL_MANY_PORTS
    bool '   Support for sharing serial interrupts' CONFIG_SERIAL_SHARE_IRQ
+   bool '   Autodetect IRQ on standard ports (unsafe)' CONFIG_SERIAL_DETECT_IRQ
    bool '   Support special multiport boards' CONFIG_SERIAL_MULTIPORT
    bool '   Support the Bell Technologies HUB6 card' CONFIG_HUB6
 fi
diff -u --recursive --new-file v2.1.96/linux/drivers/char/lp.c linux/drivers/char/lp.c
--- v2.1.96/linux/drivers/char/lp.c	Tue Apr 14 14:29:20 1998
+++ linux/drivers/char/lp.c	Fri Apr 17 21:57:16 1998
@@ -20,20 +20,19 @@
 
 /* This driver should, in theory, work with any parallel port that has an
  * appropriate low-level driver; all I/O is done through the parport
- * abstraction layer.  There is a performance penalty for this, but parallel
- * ports are comparitively low-speed devices anyway.
+ * abstraction layer.
  *
  * If this driver is built into the kernel, you can configure it using the
  * kernel command-line.  For example:
  *
- *      lp=parport1,none,parport2	(bind lp0 to parport1, disable lp1 and
+ *	lp=parport1,none,parport2	(bind lp0 to parport1, disable lp1 and
  *					 bind lp2 to parport2)
  *
  *	lp=auto				(assign lp devices to all ports that
  *				         have printers attached, as determined
  *					 by the IEEE-1284 autoprobe)
  * 
- *      lp=reset			(reset the printer during 
+ *	lp=reset			(reset the printer during 
  *					 initialisation)
  *
  *	lp=off				(disable the printer driver entirely)
@@ -41,13 +40,11 @@
  * If the driver is loaded as a module, similar functionality is available
  * using module parameters.  The equivalent of the above commands would be:
  *
- *	# insmod lp.o parport=1,-1,2	(use -1 for disabled ports, since
- *					 module parameters do not allow you
- *					 to mix textual and numeric values)
+ *	# insmod lp.o parport=1,none,2
  *
- *      # insmod lp.o autoprobe=1
+ *	# insmod lp.o parport=auto
  *
- *	# insmod lp.0 reset=1
+ *	# insmod lp.o reset=1
  */
 
 /* COMPATIBILITY WITH OLD KERNELS
@@ -652,13 +649,12 @@
 
 #ifdef MODULE
 
-static int parport[LP_NO] = { [0 ... LP_NO-1] = LP_PARPORT_UNSPEC };
+static int parport_nr[LP_NO] = { [0 ... LP_NO-1] = LP_PARPORT_UNSPEC };
+static char *parport[LP_NO] = { NULL,  };
 static int reset = 0;
-static int autoprobe = 0;
 
-MODULE_PARM(parport, "1-" __MODULE_STRING(LP_NO) "i");
+MODULE_PARM(parport, "1-" __MODULE_STRING(LP_NO) "s");
 MODULE_PARM(reset, "i");
-MODULE_PARM(autoprobe, "i");
 
 #else
 
@@ -720,7 +716,7 @@
 	unsigned int i;
 	struct parport *port;
 
-	switch (parport[0])
+	switch (parport_nr[0])
 	{
 	case LP_PARPORT_OFF:
 		return 0;
@@ -729,7 +725,7 @@
 	case LP_PARPORT_AUTO:
 	        for (port = parport_enumerate(); port; port = port->next) {
 
-			if (parport[0] == LP_PARPORT_AUTO &&
+			if (parport_nr[0] == LP_PARPORT_AUTO &&
 			    port->probe_info.class != PARPORT_CLASS_PRINTER)
 				continue;
 
@@ -739,12 +735,11 @@
 		}
 		break;
 
-	case LP_PARPORT_NONE:
 	default:
 		for (i = 0; i < LP_NO; i++) {
-			if (parport[i] >= 0) {
+			if (parport_nr[i] >= 0) {
 				char buffer[16];
-				sprintf(buffer, "parport%d", parport[i]);
+				sprintf(buffer, "parport%d", parport_nr[i]);
 				for (port = parport_enumerate(); port; 
 				     port = port->next) {
 					if (!strcmp(port->name, buffer)) {
@@ -773,8 +768,28 @@
 #ifdef MODULE
 int init_module(void)
 {
-	if (autoprobe)
-		parport[0] = LP_PARPORT_AUTO;
+	if (parport[0]) {
+		/* The user gave some parameters.  Let's see what they were.  */
+		if (!strncmp(parport[0], "auto", 4))
+			parport_nr[0] = LP_PARPORT_AUTO;
+		else {
+			int n;
+			for (n = 0; n < LP_NO && parport[n]; n++) {
+				if (!strncmp(parport[n], "none", 4))
+					parport_nr[n] = LP_PARPORT_NONE;
+				else {
+					char *ep;
+					unsigned long r = simple_strtoul(parport[n], &ep, 0);
+					if (ep != parport[n]) 
+						parport_nr[n] = r;
+					else {
+						printk(KERN_ERR "lp: bad port specifier `%s'\n", parport[n]);
+						return -ENODEV;
+					}
+				}
+			}
+		}
+	}
 
 	return lp_init();
 }
diff -u --recursive --new-file v2.1.96/linux/drivers/char/mem.c linux/drivers/char/mem.c
--- v2.1.96/linux/drivers/char/mem.c	Wed Apr  1 20:11:48 1998
+++ linux/drivers/char/mem.c	Fri Apr 17 22:05:40 1998
@@ -544,7 +544,7 @@
 #ifdef CONFIG_JOYSTICK
 	/*
 	 *	Some joysticks only appear when the soundcard they are
-	 *	connected too is confgured. Keep the sound/joystick ordering.
+	 *	connected to is configured. Keep the sound/joystick ordering.
 	 */
 	js_init();
 #endif	
diff -u --recursive --new-file v2.1.96/linux/drivers/char/serial.c linux/drivers/char/serial.c
--- v2.1.96/linux/drivers/char/serial.c	Tue Apr 14 14:29:20 1998
+++ linux/drivers/char/serial.c	Fri Apr 17 22:04:44 1998
@@ -25,50 +25,18 @@
  *  8/97: Fix bug in rs_set_termios with RTS
  *        Stanislav V. Voronyi <stas@uanet.kharkov.ua>
  *
+ *  3/98: Change the IRQ detection, use of probe_irq_o*(),
+ *	  supress TIOCSERGWILD and TIOCSERSWILD
+ *	  Etienne Lorrain <etienne.lorrain@ibm.net>
+ *
+ *  4/98: Added changes to support the ARM architecture proposed by
+ * 	  Russell King
+ *
  * This module exports the following rs232 io functions:
  *
  *	int rs_init(void);
  */
 
-#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/interrupt.h>
-#include <linux/tty.h>
-#include <linux/tty_flip.h>
-#include <linux/serial.h>
-#include <linux/serial_reg.h>
-#include <linux/major.h>
-#include <linux/string.h>
-#include <linux/fcntl.h>
-#include <linux/ptrace.h>
-#include <linux/ioport.h>
-#include <linux/mm.h>
-#include <linux/malloc.h>
-#include <linux/init.h>
-#ifdef CONFIG_SERIAL_CONSOLE
-#include <linux/console.h>
-#endif
-
-#include <asm/system.h>
-#include <asm/io.h>
-#include <asm/uaccess.h>
-#include <asm/bitops.h>
-
-static char *serial_name = "Serial driver";
-static char *serial_version = "4.24";
-
-static DECLARE_TASK_QUEUE(tq_serial);
-
-static struct tty_driver serial_driver, callout_driver;
-static int serial_refcount;
-
-/* number of characters left in xmit buffer before we ask for more */
-#define WAKEUP_CHARS 256
-
 /*
  * Serial driver configuration section.  Here are the various options:
  *
@@ -86,6 +54,9 @@
  * CONFIG_SERIAL_SHARE_IRQ
  * 		Enables support for multiple serial ports on one IRQ
  *
+ * CONFIG_SERIAL_DETECT_IRQ
+ *		Enable the autodetection of IRQ on standart ports
+ *
  * SERIAL_PARANOIA_CHECK
  * 		Check the magic number for the async_structure where
  * 		ever possible.
@@ -100,6 +71,7 @@
 
 #define CONFIG_SERIAL_MANY_PORTS
 #define CONFIG_SERIAL_SHARE_IRQ
+#define CONFIG_SERIAL_DETECT_IRQ
 #define CONFIG_SERIAL_MULTIPORT
 #define CONFIG_HUB6
 #endif
@@ -133,7 +105,7 @@
 
 #define IRQ_T(info) ((info->flags & ASYNC_SHARE_IRQ) ? SA_SHIRQ : SA_INTERRUPT)
 
-#define _INLINE_ inline
+#define SERIAL_INLINE
   
 #if defined(MODULE) && defined(SERIAL_DEBUG_MCOUNT)
 #define DBG_CNT(s) printk("(%s): [%x] refc=%d, serc=%d, ttyc=%d -> %s\n", \
@@ -143,22 +115,70 @@
 #endif
 
 /*
+ * End of serial driver configuration section.
+ */
+
+#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/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial.h>
+#include <linux/serial_reg.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#ifdef CONFIG_SERIAL_CONSOLE
+#include <linux/console.h>
+#endif
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+#include <asm/bitops.h>
+#include <asm/serial.h>
+
+#ifdef SERIAL_INLINE
+#define _INLINE_ inline
+#endif
+	
+static char *serial_name = "Serial driver";
+static char *serial_version = "4.25";
+
+static DECLARE_TASK_QUEUE(tq_serial);
+
+static struct tty_driver serial_driver, callout_driver;
+static int serial_refcount;
+
+/* number of characters left in xmit buffer before we ask for more */
+#define WAKEUP_CHARS 256
+
+/*
  * IRQ_timeout		- How long the timeout should be for each IRQ
  * 				should be after the IRQ has been active.
  */
 
-static struct async_struct *IRQ_ports[16];
+static struct async_struct *IRQ_ports[NR_IRQS];
 #ifdef CONFIG_SERIAL_MULTIPORT
-static struct rs_multiport_struct rs_multiport[16];
+static struct rs_multiport_struct rs_multiport[NR_IRQS];
 #endif
-static int IRQ_timeout[16];
-static volatile int rs_irq_triggered;
-static volatile int rs_triggered;
-static int rs_wild_int_mask;
+static int IRQ_timeout[NR_IRQS];
 #ifdef CONFIG_SERIAL_CONSOLE
 static struct console sercons;
 #endif
 
+static unsigned detect_uart_irq (struct serial_state * state);
 static void autoconfig(struct serial_state * info);
 static void change_speed(struct async_struct *info);
 static void rs_wait_until_sent(struct tty_struct *tty, int timeout);
@@ -180,109 +200,9 @@
 	{ "TI16750", 64, UART_CLEAR_FIFO | UART_USE_FIFO},
 	{ 0, 0}
 };
-	
-/*
- * This assumes you have a 1.8432 MHz clock for your UART.
- *
- * It'd be nice if someone built a serial card with a 24.576 MHz
- * clock, since the 16550A is capable of handling a top speed of 1.5
- * megabits/second; but this requires the faster clock.
- */
-#define BASE_BAUD ( 1843200 / 16 )
-
-/* Standard COM flags (except for COM4, because of the 8514 problem) */
-#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST )
-#define STD_COM4_FLAGS ASYNC_BOOT_AUTOCONF
-
-
-#ifdef CONFIG_SERIAL_MANY_PORTS
-#define FOURPORT_FLAGS ASYNC_FOURPORT
-#define ACCENT_FLAGS 0
-#define BOCA_FLAGS 0
-#define HUB6_FLAGS 0
-#endif
-	
-/*
- * The following define the access methods for the HUB6 card. All
- * access is through two ports for all 24 possible chips. The card is
- * selected through the high 2 bits, the port on that card with the
- * "middle" 3 bits, and the register on that port with the bottom
- * 3 bits.
- *
- * While the access port and interrupt is configurable, the default
- * port locations are 0x302 for the port control register, and 0x303
- * for the data read/write register. Normally, the interrupt is at irq3
- * but can be anything from 3 to 7 inclusive. Note that using 3 will
- * require disabling com2.
- */
-
-#define C_P(card,port) (((card)<<6|(port)<<3) + 1)
 
 static struct serial_state rs_table[] = {
-	/* UART CLK   PORT IRQ     FLAGS        */
-	{ 0, BASE_BAUD, 0x3F8, 4, STD_COM_FLAGS },	/* ttyS0 */
-	{ 0, BASE_BAUD, 0x2F8, 3, STD_COM_FLAGS },	/* ttyS1 */
-	{ 0, BASE_BAUD, 0x3E8, 4, STD_COM_FLAGS },	/* ttyS2 */
-	{ 0, BASE_BAUD, 0x2E8, 3, STD_COM4_FLAGS },	/* ttyS3 */
-#ifdef CONFIG_SERIAL_MANY_PORTS
-	{ 0, BASE_BAUD, 0x1A0, 9, FOURPORT_FLAGS }, 	/* ttyS4 */
-	{ 0, BASE_BAUD, 0x1A8, 9, FOURPORT_FLAGS },	/* ttyS5 */
-	{ 0, BASE_BAUD, 0x1B0, 9, FOURPORT_FLAGS },	/* ttyS6 */
-	{ 0, BASE_BAUD, 0x1B8, 9, FOURPORT_FLAGS },	/* ttyS7 */
-
-	{ 0, BASE_BAUD, 0x2A0, 5, FOURPORT_FLAGS },	/* ttyS8 */
-	{ 0, BASE_BAUD, 0x2A8, 5, FOURPORT_FLAGS },	/* ttyS9 */
-	{ 0, BASE_BAUD, 0x2B0, 5, FOURPORT_FLAGS },	/* ttyS10 */
-	{ 0, BASE_BAUD, 0x2B8, 5, FOURPORT_FLAGS },	/* ttyS11 */
-	
-	{ 0, BASE_BAUD, 0x330, 4, ACCENT_FLAGS },	/* ttyS12 */
-	{ 0, BASE_BAUD, 0x338, 4, ACCENT_FLAGS },	/* ttyS13 */
-	{ 0, BASE_BAUD, 0x000, 0, 0 },	/* ttyS14 (spare; user configurable) */
-	{ 0, BASE_BAUD, 0x000, 0, 0 },	/* ttyS15 (spare; user configurable) */
-
-	{ 0, BASE_BAUD, 0x100, 12, BOCA_FLAGS },	/* ttyS16 */
-	{ 0, BASE_BAUD, 0x108, 12, BOCA_FLAGS },	/* ttyS17 */
-	{ 0, BASE_BAUD, 0x110, 12, BOCA_FLAGS },	/* ttyS18 */
-	{ 0, BASE_BAUD, 0x118, 12, BOCA_FLAGS },	/* ttyS19 */
-	{ 0, BASE_BAUD, 0x120, 12, BOCA_FLAGS },	/* ttyS20 */
-	{ 0, BASE_BAUD, 0x128, 12, BOCA_FLAGS },	/* ttyS21 */
-	{ 0, BASE_BAUD, 0x130, 12, BOCA_FLAGS },	/* ttyS22 */
-	{ 0, BASE_BAUD, 0x138, 12, BOCA_FLAGS },	/* ttyS23 */
-	{ 0, BASE_BAUD, 0x140, 12, BOCA_FLAGS },	/* ttyS24 */
-	{ 0, BASE_BAUD, 0x148, 12, BOCA_FLAGS },	/* ttyS25 */
-	{ 0, BASE_BAUD, 0x150, 12, BOCA_FLAGS },	/* ttyS26 */
-	{ 0, BASE_BAUD, 0x158, 12, BOCA_FLAGS },	/* ttyS27 */
-	{ 0, BASE_BAUD, 0x160, 12, BOCA_FLAGS },	/* ttyS28 */
-	{ 0, BASE_BAUD, 0x168, 12, BOCA_FLAGS },	/* ttyS29 */
-	{ 0, BASE_BAUD, 0x170, 12, BOCA_FLAGS },	/* ttyS30 */
-	{ 0, BASE_BAUD, 0x178, 12, BOCA_FLAGS },	/* ttyS31 */
-
-/* You can have up to four HUB6's in the system, but I've only
- * included two cards here for a total of twelve ports.
- */
-#ifdef CONFIG_HUB6
-	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,0) },	/* ttyS32 */
-	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,1) },	/* ttyS33 */
-	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,2) },	/* ttyS34 */
-	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,3) },	/* ttyS35 */
-	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,4) },	/* ttyS36 */
-	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,5) },	/* ttyS37 */
-	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,0) },	/* ttyS38 */
-	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,1) },	/* ttyS39 */
-	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,2) },	/* ttyS40 */
-	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,3) },	/* ttyS41 */
-	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,4) },	/* ttyS42 */
-	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,5) },	/* ttyS43 */
-#endif
-#endif /* CONFIG_SERIAL_MANY_PORTS */
-#ifdef CONFIG_MCA
-	{ 0, BASE_BAUD, 0x3220, 3, STD_COM_FLAGS },
-	{ 0, BASE_BAUD, 0x3228, 3, STD_COM_FLAGS },
-	{ 0, BASE_BAUD, 0x4220, 3, STD_COM_FLAGS },
-	{ 0, BASE_BAUD, 0x4228, 3, STD_COM_FLAGS },
-	{ 0, BASE_BAUD, 0x5220, 3, STD_COM_FLAGS },
-	{ 0, BASE_BAUD, 0x5228, 3, STD_COM_FLAGS },
-#endif
+	SERIAL_PORT_DFNS	/* Defined in serial.h */
 };
 
 #define NR_PORTS	(sizeof(rs_table)/sizeof(struct serial_state))
@@ -304,7 +224,7 @@
  * 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 unsigned char *tmp_buf;
 static struct semaphore tmp_buf_sem = MUTEX;
 
 static inline int serial_paranoia_check(struct async_struct *info,
@@ -443,17 +363,6 @@
  */
 
 /*
- * This is the serial driver's interrupt routine while we are probing
- * for submarines.
- */
-static void rs_probe(int irq, void *dev_id, struct pt_regs * regs)
-{
-	rs_irq_triggered = irq;
-	rs_triggered |= 1 << irq;
-	return;
-}
-
-/*
  * This routine is used by the interrupt handler to schedule
  * processing in the software interrupt portion of the driver.
  */
@@ -923,7 +832,7 @@
 	unsigned int	i;
 
 	if ((jiffies - last_strobe) >= RS_STROBE_TIME) {
-		for (i=1; i < 16; i++) {
+		for (i=1; i < NR_IRQS; i++) {
 			info = IRQ_ports[i];
 			if (!info)
 				continue;
@@ -975,38 +884,6 @@
  */
 
 /*
- * Grab all interrupts in preparation for doing an automatic irq
- * detection.  dontgrab is a mask of irq's _not_ to grab.  Returns a
- * mask of irq's which were grabbed and should therefore be freed
- * using free_all_interrupts().
- */
-static int grab_all_interrupts(int dontgrab)
-{
-	int 			irq_lines = 0;
-	int			i, mask;
-	
-	for (i = 0, mask = 1; i < 16; i++, mask <<= 1) {
-		if (!(mask & dontgrab) && !request_irq(i, rs_probe, SA_INTERRUPT, "serial probe", NULL)) {
-			irq_lines |= mask;
-		}
-	}
-	return irq_lines;
-}
-
-/*
- * Release all interrupts grabbed by grab_all_interrupts
- */
-static void free_all_interrupts(int irq_lines)
-{
-	int	i;
-	
-	for (i = 0; i < 16; i++) {
-		if (irq_lines & (1 << i))
-			free_irq(i, NULL);
-	}
-}
-
-/*
  * This routine figures out the correct timeout for a particular IRQ.
  * It uses the smallest timeout of all of the serial ports in a
  * particular interrupt chain.  Now only used for IRQ 0....
@@ -1541,44 +1418,61 @@
 
 	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) {
+		down(&tmp_buf_sem);
+		while (1) {
+			c = MIN(count,
+				MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
+				    SERIAL_XMIT_SIZE - info->xmit_head));
+			if (c <= 0)
+				break;
 
-		if (from_user) {
 			c -= copy_from_user(tmp_buf, buf, c);
 			if (!c) {
 				if (!ret)
 					ret = -EFAULT;
 				break;
 			}
+			cli();
 			c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
 				       SERIAL_XMIT_SIZE - info->xmit_head));
 			memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c);
-		} else
+			info->xmit_head = ((info->xmit_head + c) &
+					   (SERIAL_XMIT_SIZE-1));
+			info->xmit_cnt += c;
+			restore_flags(flags);
+			buf += c;
+			count -= c;
+			ret += c;
+		}
+		up(&tmp_buf_sem);
+	} else {
+		while (1) {
+			cli();		
+			c = MIN(count,
+				MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
+				    SERIAL_XMIT_SIZE - info->xmit_head));
+			if (c <= 0) {
+				restore_flags(flags);
+				break;
+			}
 			memcpy(info->xmit_buf + info->xmit_head, buf, c);
-		info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE-1);
-		info->xmit_cnt += c;
-		restore_flags(flags);
-		buf += c;
-		count -= c;
-		ret += c;
+			info->xmit_head = ((info->xmit_head + c) &
+					   (SERIAL_XMIT_SIZE-1));
+			info->xmit_cnt += c;
+			restore_flags(flags);
+			buf += c;
+			count -= c;
+			ret += c;
+		}
 	}
-	if (from_user)
-		up(&tmp_buf_sem);
 	if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped &&
 	    !(info->IER & UART_IER_THRI)) {
 		info->IER |= UART_IER_THRI;
 		serial_out(info, UART_IER, info->IER);
 	}
-	restore_flags(flags);
 	return ret;
 }
 
@@ -1759,10 +1653,9 @@
 		goto check_and_exit;
 	}
 
-	if (new_serial.irq == 2)
-		new_serial.irq = 9;
+	new_serial.irq = irq_cannonicalize(new_serial.irq);
 
-	if ((new_serial.irq > 15) || (new_serial.port > 0xffff) ||
+	if ((new_serial.irq >= NR_IRQS) || (new_serial.port > 0xffff) ||
 	    (new_serial.type < PORT_UNKNOWN) || (new_serial.type > PORT_MAX)) {
 		return -EINVAL;
 	}
@@ -1793,6 +1686,7 @@
 	state->type = new_serial.type;
 	state->close_delay = new_serial.close_delay * HZ/100;
 	state->closing_wait = new_serial.closing_wait * HZ/100;
+	info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
 	info->xmit_fifo_size = state->xmit_fifo_size =
 		new_serial.xmit_fifo_size;
 
@@ -1951,9 +1845,9 @@
 	
 	shutdown(info);
 
-	cli();
 	autoconfig(info->state);
-	sti();
+	if ((info->state->flags & ASYNC_AUTO_IRQ) && (info->state->port != 0))
+		info->state->irq = detect_uart_irq(info->state);
 
 	retval = startup(info);
 	if (retval)
@@ -1984,50 +1878,6 @@
 	restore_flags(flags);
 }
 
-/*
- * This routine returns a bitfield of "wild interrupts".  Basically,
- * any unclaimed interrupts which is flapping around.
- */
-static int check_wild_interrupts(int doprint)
-{
-	int	i, mask;
-	int	wild_interrupts = 0;
-	int	irq_lines;
-	unsigned long timeout;
-	unsigned long flags;
-	
-	/* Turn on interrupts (they may be off) */
-	save_flags(flags); sti();
-
-	irq_lines = grab_all_interrupts(0);
-	
-	/*
-	 * Delay for 0.1 seconds -- we use a busy loop since this may 
-	 * occur during the bootup sequence
-	 */
-	timeout = jiffies+HZ/10;
-	while (timeout >= jiffies)
-		;
-	
-	rs_triggered = 0;	/* Reset after letting things settle */
-
-	timeout = jiffies+HZ/10;
-	while (timeout >= jiffies)
-		;
-	
-	for (i = 0, mask = 1; i < 16; i++, mask <<= 1) {
-		if ((rs_triggered & (1 << i)) &&
-		    (irq_lines & (1 << i))) {
-			wild_interrupts |= mask;
-			if (doprint)
-				printk("Wild interrupt?  (IRQ %d)\n", i);
-		}
-	}
-	free_all_interrupts(irq_lines);
-	restore_flags(flags);
-	return wild_interrupts;
-}
-
 #ifdef CONFIG_SERIAL_MULTIPORT
 static int get_multiport_struct(struct async_struct * info,
 				struct serial_multiport_struct *retinfo)
@@ -2155,8 +2005,7 @@
 		return -ENODEV;
 
 	if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
-	    (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGWILD)  &&
-	    (cmd != TIOCSERSWILD) && (cmd != TIOCSERGSTRUCT) &&
+	    (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) &&
 	    (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) {
 		if (tty->flags & (1 << TTY_IO_ERROR))
 		    return -EIO;
@@ -2178,23 +2027,9 @@
 		case TIOCSERCONFIG:
 			return do_autoconfig(info);
 
-		case TIOCSERGWILD:
-			return put_user(rs_wild_int_mask,
-					(unsigned int *) arg);
 		case TIOCSERGETLSR: /* Get line status register */
 			return get_lsr_info(info, (unsigned int *) arg);
 
-		case TIOCSERSWILD:
-			if (!suser())
-				return -EPERM;
-			error = get_user(rs_wild_int_mask,
-					 (unsigned int *) arg);
-			if (error)
-				return error;
-			if (rs_wild_int_mask < 0)
-				rs_wild_int_mask = check_wild_interrupts(0);
-			return 0;
-
 		case TIOCSERGSTRUCT:
 			if (copy_to_user((struct async_struct *) arg,
 					 info, sizeof(struct async_struct)))
@@ -2216,7 +2051,7 @@
  		 *   (use |'ed TIOCM_RNG/DSR/CD/CTS for masking)
 		 * Caller should use TIOCGICOUNT to see which one it was
 		 */
-		 case TIOCMIWAIT:
+		case TIOCMIWAIT:
 			cli();
 			/* note the counters on entry */
 			cprev = info->state->icount;
@@ -2707,6 +2542,7 @@
 #endif
 	tty->driver_data = info;
 	info->tty = tty;
+	info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
 
 	if (!tmp_buf) {
 		page = get_free_page(GFP_KERNEL);
@@ -2778,7 +2614,7 @@
  * /proc fs routines....
  */
 
-static int inline line_info(char *buf, struct serial_state *state)
+static inline int line_info(char *buf, struct serial_state *state)
 {
 	struct async_struct *info = state->info, scr_info;
 	char	stat_buf[30], control, status;
@@ -2906,7 +2742,10 @@
 #endif
 #ifdef CONFIG_SERIAL_SHARE_IRQ
 	printk(" SHARE_IRQ");
+#endif
 #define SERIAL_OPT
+#ifdef CONFIG_SERIAL_DETECT_IRQ
+	printk(" DETECT_IRQ");
 #endif
 #ifdef SERIAL_OPT
 	printk(" enabled\n");
@@ -2917,105 +2756,54 @@
 }
 
 /*
- * This routine is called by do_auto_irq(); it attempts to determine
- * which interrupt a serial port is configured to use.  It is not
- * fool-proof, but it works a large part of the time.
- */
-static int get_auto_irq(struct async_struct *info)
-{
-        
-	unsigned char save_MCR, save_IER;
-	unsigned long timeout;
-#ifdef CONFIG_SERIAL_MANY_PORTS
-	unsigned char save_ICP=0;
-	unsigned short ICP=0, port = info->port;
-#endif
+ * This routine detect the IRQ of a serial port by clearing OUT2 when
+ * no UART interrupt are requested (IER = 0) (*GPL*). This seems to work at
+ * each time, as long as no other device permanently request the IRQ.
+ * If no IRQ is detected, or multiple IRQ appear, this function returns 0.
+ * The variable "state" and the field "state->port" should not be null.
+ */
+static unsigned detect_uart_irq (struct serial_state * state)
+{
+	int irq;
+	unsigned long irqs;
+	unsigned char save_mcr;
+	struct async_struct scr_info; /* serial_{in,out} because HUB6 */
 
-	
-	/*
-	 * Enable interrupts and see who answers
-	 */
-	rs_irq_triggered = 0;
-	cli();
-	save_IER = serial_inp(info, UART_IER);
-	save_MCR = serial_inp(info, UART_MCR);
 #ifdef CONFIG_SERIAL_MANY_PORTS
-	if (info->flags & ASYNC_FOURPORT)  {
-		serial_outp(info, UART_MCR, UART_MCR_DTR | UART_MCR_RTS);
-		serial_outp(info, UART_IER, 0x0f);	/* enable all intrs */
-		ICP = (port & 0xFE0) | 0x01F;
+	unsigned char save_ICP=0; /* no warning */
+	unsigned short ICP=0;
+
+	if (state->flags & ASYNC_FOURPORT)  {
+		ICP = (state->port & 0xFE0) | 0x01F;
 		save_ICP = inb_p(ICP);
 		outb_p(0x80, ICP);
 		(void) inb_p(ICP);
-	} else 
-#endif
-        {
-		serial_outp(info, UART_MCR,
-			    UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2);
-		serial_outp(info, UART_IER, 0x0f);	/* enable all intrs */
-	}
-	sti();
-	/*
-	 * Next, clear the interrupt registers.
-	 */
-	(void)serial_inp(info, UART_LSR);
-	(void)serial_inp(info, UART_RX);
-	(void)serial_inp(info, UART_IIR);
-	(void)serial_inp(info, UART_MSR);
-	
-	timeout = jiffies+ ((2*HZ)/100);
-	while (timeout >= jiffies) {
-		if (rs_irq_triggered)
-			break;
 	}
-	/*
-	 * Now check to see if we got any business, and clean up.
-	 */
-	cli();
-	serial_outp(info, UART_IER, save_IER);
-	serial_outp(info, UART_MCR, save_MCR);
-#ifdef CONFIG_SERIAL_MANY_PORTS
-	if (info->flags & ASYNC_FOURPORT)
-		outb_p(save_ICP, ICP);
 #endif
-	sti();
-	return(rs_irq_triggered);
-}
+	scr_info.magic = SERIAL_MAGIC;
+	scr_info.port = state->port;
+	scr_info.flags = state->flags;
+#ifdef CONFIG_HUB6
+	scr_info.hub6 = state->hub6;
+#endif
 
-/*
- * Calls get_auto_irq() multiple times, to make sure we don't get
- * faked out by random interrupts
- */
-static int do_auto_irq(struct async_struct * info)
-{
-	unsigned 		port = info->port;
-	int 			irq_lines = 0;
-	int			irq_try_1 = 0, irq_try_2 = 0;
-	int			retries;
-	unsigned long flags;
+	/* forget possible initially masked and pending IRQ */
+	probe_irq_off(probe_irq_on());
+	save_mcr = serial_inp(&scr_info, UART_MCR);
 
-	if (!port)
-		return 0;
+	serial_outp(&scr_info, UART_MCR, UART_MCR_OUT1 | UART_MCR_OUT2);
+	irqs = probe_irq_on();
+	serial_outp(&scr_info, UART_MCR, 0);
+	udelay (1);
+	irq = probe_irq_off(irqs);
 
-	/* Turn on interrupts (they may be off) */
-	save_flags(flags); sti();
+	serial_outp(&scr_info, UART_MCR, save_mcr);
 
-	irq_lines = grab_all_interrupts(rs_wild_int_mask);
-	
-	for (retries = 0; retries < 5; retries++) {
-		if (!irq_try_1)
-			irq_try_1 = get_auto_irq(info);
-		if (!irq_try_2)
-			irq_try_2 = get_auto_irq(info);
-		if (irq_try_1 && irq_try_2) {
-			if (irq_try_1 == irq_try_2)
-				break;
-			irq_try_1 = irq_try_2 = 0;
-		}
-	}
-	restore_flags(flags);
-	free_all_interrupts(irq_lines);
-	return (irq_try_1 == irq_try_2) ? irq_try_1 : 0;
+#ifdef CONFIG_SERIAL_MANY_PORTS
+	if (state->flags & ASYNC_FOURPORT)
+		outb_p(save_ICP, ICP);
+#endif
+	return (irq > 0)? irq : 0;
 }
 
 /*
@@ -3041,6 +2829,9 @@
 	info->magic = SERIAL_MAGIC;
 	info->port = state->port;
 	info->flags = state->flags;
+#ifdef CONFIG_HUB6
+	info->hub6 = state->hub6;
+#endif
 
 	save_flags(flags); cli();
 	
@@ -3086,13 +2877,6 @@
 		}
 	} 
 	
-	/*
-	 * If the AUTO_IRQ flag is set, try to do the automatic IRQ
-	 * detection.
-	 */
-	if (state->flags & ASYNC_AUTO_IRQ)
-		state->irq = do_auto_irq(info);
-		
 	scratch2 = serial_in(info, UART_LCR);
 	serial_outp(info, UART_LCR, 0xBF); /* set up for StarTech test */
 	serial_outp(info, UART_EFR, 0);	/* EFR is the same as FCR */
@@ -3174,6 +2958,7 @@
 	serial_outp(info, UART_FCR, (UART_FCR_CLEAR_RCVR |
 				     UART_FCR_CLEAR_XMIT));
 	(void)serial_in(info, UART_RX);
+	serial_outp(info, UART_IER, 0);
 	
 	restore_flags(flags);
 }
@@ -3191,15 +2976,21 @@
 {
 	int i;
 	struct serial_state * state;
-	
+	extern void atomwide_serial_init (void);
+	extern void dualsp_serial_init (void);
+
+#ifdef CONFIG_ATOMWIDE_SERIAL
+	atomwide_serial_init ();
+#endif
+#ifdef CONFIG_DUALSP_SERIAL
+	dualsp_serial_init ();
+#endif
+
 	init_bh(SERIAL_BH, do_serial_bh);
 	timer_table[RS_TIMER].fn = rs_timer;
 	timer_table[RS_TIMER].expires = 0;
-#ifdef CONFIG_AUTO_IRQ
-	rs_wild_int_mask = check_wild_interrupts(1);
-#endif
 
-	for (i = 0; i < 16; i++) {
+	for (i = 0; i < NR_IRQS; i++) {
 		IRQ_ports[i] = 0;
 		IRQ_timeout[i] = 0;
 #ifdef CONFIG_SERIAL_MULTIPORT
@@ -3291,17 +3082,26 @@
 		state->icount.rx = state->icount.tx = 0;
 		state->icount.frame = state->icount.parity = 0;
 		state->icount.overrun = state->icount.brk = 0;
-		if (state->irq == 2)
-			state->irq = 9;
-		if (state->type == PORT_UNKNOWN) {
-			if (!(state->flags & ASYNC_BOOT_AUTOCONF))
-				continue;
-			if (check_region(state->port,8))
-				continue;
-			autoconfig(state);
-			if (state->type == PORT_UNKNOWN)
-				continue;
+		state->irq = irq_cannonicalize(state->irq);
+		if (check_region(state->port,8)) {
+			state->type = PORT_UNKNOWN;
+			continue;
 		}
+		if (   (state->type == PORT_UNKNOWN)
+		    && (state->flags & ASYNC_BOOT_AUTOCONF))
+			autoconfig(state);
+	}
+	/*
+	 * Detect the IRQ only once every port is initialised,
+	 * because some 16450 do not reset to 0 the MCR register.
+	 */
+	for (i = 0, state = rs_table; i < NR_PORTS; i++,state++) {
+		if (state->type == PORT_UNKNOWN)
+			continue;
+		if (   (state->flags & ASYNC_BOOT_AUTOCONF)
+		    && (state->flags & ASYNC_AUTO_IRQ)
+		    && (state->port != 0))
+			state->irq = detect_uart_irq(state);
 		printk(KERN_INFO "ttyS%02d%s at 0x%04x (irq = %d) is a %s\n",
 		       state->line,
 		       (state->flags & ASYNC_FOURPORT) ? " FourPort" : "",
@@ -3354,10 +3154,14 @@
 		printk("register_serial(): autoconfig failed\n");
 		return -1;
 	}
+	restore_flags(flags);
+
+	if ((state->flags & ASYNC_AUTO_IRQ) && (state->port != 0))
+		state->irq = detect_uart_irq(state);
+
 	printk(KERN_INFO "tty%02d at 0x%04x (irq = %d) is a %s\n",
 	       state->line, state->port, state->irq,
 	       uart_config[state->type].name);
-	restore_flags(flags);
 	return state->line;
 }
 
@@ -3597,7 +3401,7 @@
 	 *	Divisor, bytesize and parity
 	 */
 	ser = rs_table + co->index;
-	quot = BASE_BAUD / baud;
+	quot = ser->baud_base / baud;
 	cval = cflag & (CSIZE | CSTOPB);
 #if defined(__powerpc__) || defined(__alpha__)
 	cval >>= 8;
@@ -3613,13 +3417,12 @@
 	 *	Disable UART interrupts, set DTR and RTS high
 	 *	and set speed.
 	 */
-	outb(cval, ser->port + UART_LCR);  /* don't assume that DLAB is clear */
-	outb(0, ser->port + UART_IER);
-	outb(UART_MCR_DTR | UART_MCR_RTS, ser->port + UART_MCR);
 	outb(cval | UART_LCR_DLAB, ser->port + UART_LCR);	/* set DLAB */
 	outb(quot & 0xff, ser->port + UART_DLL);	/* LS of divisor */
 	outb(quot >> 8, ser->port + UART_DLM);		/* MS of divisor */
 	outb(cval, ser->port + UART_LCR);		/* reset DLAB */
+	outb(0, ser->port + UART_IER);
+	outb(UART_MCR_DTR | UART_MCR_RTS, ser->port + UART_MCR);
 
 	/*
 	 *	If we read 0xff from the LSR, there is no UART here.
diff -u --recursive --new-file v2.1.96/linux/drivers/char/tty_io.c linux/drivers/char/tty_io.c
--- v2.1.96/linux/drivers/char/tty_io.c	Mon Apr  6 17:40:59 1998
+++ linux/drivers/char/tty_io.c	Fri Apr 17 22:04:44 1998
@@ -131,12 +131,13 @@
 /*
  * This routine returns the name of tty.
  */
+#define TTY_NUMBER(tty) (MINOR((tty)->device) - (tty)->driver.minor_start + \
+			 (tty)->driver.name_base)
+	
 char *tty_name(struct tty_struct *tty, char *buf)
 {
 	if (tty)
-		sprintf(buf, "%s%d", tty->driver.name,
-			MINOR(tty->device) - tty->driver.minor_start +
-			tty->driver.name_base);
+		sprintf(buf, "%s%d", tty->driver.name, TTY_NUMBER(tty));
 	else
 		strcpy(buf, "NULL tty");
 	return buf;
@@ -1287,9 +1288,17 @@
 		tty->pgrp = current->pgrp;
 	}
 	if ((tty->driver.type == TTY_DRIVER_TYPE_SERIAL) &&
-	    (tty->driver.subtype == SERIAL_TYPE_CALLOUT)) {
-		printk(KERN_INFO "Warning, %s opened, is a deprecated tty "
-		       "callout device\n", tty_name(tty, buf));
+	    (tty->driver.subtype == SERIAL_TYPE_CALLOUT) &&
+	    (tty->count == 1)) {
+		static int nr_warns = 0;
+		if (nr_warns < 5) {
+			printk(KERN_WARNING "tty_io.c: "
+				"process %d (%s) used obsolete /dev/%s - " 
+				"update software to use /dev/ttyS%d\n",
+				current->pid, current->comm, 
+				tty_name(tty, buf), TTY_NUMBER(tty));
+			nr_warns++;
+		}
 	}
 	return 0;
 }
diff -u --recursive --new-file v2.1.96/linux/drivers/char/vt.c linux/drivers/char/vt.c
--- v2.1.96/linux/drivers/char/vt.c	Sun Nov 30 10:59:02 1997
+++ linux/drivers/char/vt.c	Tue Apr 14 17:44:21 1998
@@ -1183,20 +1183,14 @@
 #ifdef CONFIG_SUN_CONSOLE
 	if (old_vc_mode != vt_cons[new_console]->vc_mode)
 	{
-		extern void set_cursor(int currcons);
-		extern void hide_cursor(void);
-
 	 	if (old_vc_mode == KD_GRAPHICS)
 		{
-			extern void sun_clear_margin(void);
-			extern void render_screen(void);
-			
-			sun_clear_margin();
-			render_screen();
-			set_cursor(fg_console);
+			suncons_ops.clear_margin();
+			suncons_ops.render_screen();
+			suncons_ops.set_cursor(fg_console);
 		}
 		else
-			hide_cursor();
+			suncons_ops.hide_cursor();
 	}
 #endif		
       /*
diff -u --recursive --new-file v2.1.96/linux/drivers/fc4/fc.c linux/drivers/fc4/fc.c
--- v2.1.96/linux/drivers/fc4/fc.c	Mon Jan 12 15:19:36 1998
+++ linux/drivers/fc4/fc.c	Tue Apr 14 17:44:21 1998
@@ -1,7 +1,7 @@
 /* fc.c: Generic Fibre Channel and FC4 SCSI driver.
  *
- * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
- * Copyright (C) 1997 Jiri Hanika (geo@ff.cuni.cz)
+ * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1997,1998 Jiri Hanika (geo@ff.cuni.cz)
  *
  * Sources:
  *	Fibre Channel Physical & Signaling Interface (FC-PH), dpANS, 1994
@@ -51,9 +51,9 @@
 	return mmu_get_scsi_one (buf, len, fc->dev->my_bus);
 }
 
-static inline void fc_sync_dma_exit(void *buf, long size, fc_channel *fc)
+static inline void fc_sync_dma_exit(dma_handle dmh, long size, fc_channel *fc)
 {
-	mmu_release_scsi_one ((u32)(long)buf, size, fc->dev->my_bus);
+	mmu_release_scsi_one (dmh, size, fc->dev->my_bus);
 }
 
 static inline void fc_sync_dma_entry_sg(struct scatterlist *list, int count, fc_channel *fc)
@@ -73,28 +73,28 @@
 #define FC_SCMND(SCpnt) ((fc_channel *)(SCpnt->host->hostdata[0]))
 #define SC_FCMND(fcmnd) ((Scsi_Cmnd *)((long)fcmnd - (long)&(((Scsi_Cmnd *)0)->SCp)))
 
-static void fcp_scsi_insert_queue (fc_channel *fc, int no, fcp_cmnd *fcmd)
+static void fcp_scsi_insert_queue (fc_channel *fc, fcp_cmnd *fcmd)
 {
-	if (!fc->scsi_que[no]) {
-		fc->scsi_que[no] = fcmd;
+	if (!fc->scsi_que) {
+		fc->scsi_que = fcmd;
 		fcmd->next = fcmd;
 		fcmd->prev = fcmd;
 	} else {
-		fc->scsi_que[no]->prev->next = fcmd;
-		fcmd->prev = fc->scsi_que[no]->prev;
-		fc->scsi_que[no]->prev = fcmd;
-		fcmd->next = fc->scsi_que[no];
+		fc->scsi_que->prev->next = fcmd;
+		fcmd->prev = fc->scsi_que->prev;
+		fc->scsi_que->prev = fcmd;
+		fcmd->next = fc->scsi_que;
 	}
 }
 
-static void fcp_scsi_remove_queue (fc_channel *fc, int no, fcp_cmnd *fcmd)
+static void fcp_scsi_remove_queue (fc_channel *fc, fcp_cmnd *fcmd)
 {
 	if (fcmd == fcmd->next) {
-		fc->scsi_que[no] = NULL;
+		fc->scsi_que = NULL;
 		return;
 	}
-	if (fcmd == fc->scsi_que[no])
-		fc->scsi_que[no] = fcmd->next;
+	if (fcmd == fc->scsi_que)
+		fc->scsi_que = fcmd->next;
 	fcmd->prev->next = fcmd->next;
 	fcmd->next->prev = fcmd->prev;
 }
@@ -151,7 +151,7 @@
 			fc->state = FC_STATE_FPORT_OK;
 			fcmd = l->fcmds + i;
 			plogi = l->logi + 3 * i;
-			fc_sync_dma_exit (plogi, 3 * sizeof(logi), fc);
+			fc_sync_dma_exit (fcmd->cmd, 3 * sizeof(logi), fc);
 			plogi->code = LS_PLOGI;
 			memcpy (&plogi->nport_wwn, &fc->wwn_nport, sizeof(fc_wwn));
 			memcpy (&plogi->node_wwn, &fc->wwn_node, sizeof(fc_wwn));
@@ -159,7 +159,6 @@
 			memcpy (&plogi->class1, fc->class_svcs, 3*sizeof(svc_parm));
 			fch = &fcmd->fch;
 			fcmd->token += l->count;
-			fcmd->rsp += sizeof(logi);
 			FILL_FCHDR_RCTL_DID(fch, R_CTL_ELS_REQ, fc->did);
 			FILL_FCHDR_SID(fch, fc->sid);
 #ifdef FCDEBUG
@@ -172,7 +171,8 @@
 				printk ("\n");
 			}
 #endif			
-			fc_sync_dma_entry (plogi, 3 * sizeof(logi), fc);
+			fcmd->cmd = fc_sync_dma_entry (plogi, 3 * sizeof(logi), fc);
+			fcmd->rsp = fcmd->cmd + 2 * sizeof(logi);
 			if (fc->hw_enque (fc, fcmd))
 				printk ("FC: Cannot enque PLOGI packet on %s\n", fc->name);
 			break;
@@ -194,7 +194,7 @@
 		switch (status) {
 		case FC_STATUS_OK:
 			plogi = l->logi + 3 * i;
-			fc_sync_dma_exit (plogi, 3 * sizeof(logi), fc);
+			fc_sync_dma_exit (l->fcmds[i].cmd, 3 * sizeof(logi), fc);
 			if (!fc->wwn_dest.lo && !fc->wwn_dest.hi) {
 				memcpy (&fc->wwn_dest, &plogi[1].node_wwn, sizeof(fc_wwn)); 
 				FCD(("Dest WWN %08x%08x\n", *(u32 *)&fc->wwn_dest, fc->wwn_dest.lo))
@@ -212,7 +212,7 @@
 			break;
 		case FC_STATUS_ERR_OFFLINE:
 			fc->state = FC_STATE_OFFLINE;
-			fc_sync_dma_exit (l->logi + 3 * i, 3 * sizeof(logi), fc);
+			fc_sync_dma_exit (l->fcmds[i].cmd, 3 * sizeof(logi), fc);
 			printk ("%s: FC is offline\n", fc->name);
 			if (atomic_dec_and_test (&l->todo))
 				up(&l->sem);
@@ -228,26 +228,41 @@
 void fcp_register(fc_channel *fc, u8 type, int unregister)
 {
 	int size, i;
+	int slots = (fc->can_queue * 3) >> 1;
+
+	FCND(("Going to %sregister\n", unregister ? "un" : ""))
 
 	if (type == TYPE_SCSI_FCP) {
 		if (!unregister) {
 			fc->scsi_cmd_pool = 
-				(fcp_cmd *) fc_dma_alloc (fc->can_queue * (sizeof (fcp_cmd) + fc->rsp_size), 
+				(fcp_cmd *) fc_dma_alloc (slots * (sizeof (fcp_cmd) + fc->rsp_size), 
 							  "FCP SCSI cmd & rsp queues", &fc->dma_scsi_cmd);
-			fc->scsi_rsp_pool = (char *)(fc->scsi_cmd_pool + fc->can_queue);
-			fc->dma_scsi_rsp = fc->dma_scsi_cmd + fc->can_queue * sizeof (fcp_cmd);
-			fc->scsi_bitmap_end = ((fc->can_queue + 63) & ~63);
+			fc->scsi_rsp_pool = (char *)(fc->scsi_cmd_pool + slots);
+			fc->dma_scsi_rsp = fc->dma_scsi_cmd + slots * sizeof (fcp_cmd);
+			fc->scsi_bitmap_end = (slots + 63) & ~63;
 			size = fc->scsi_bitmap_end / 8;
 			fc->scsi_bitmap = kmalloc (size, GFP_KERNEL);
 			memset (fc->scsi_bitmap, 0, size);
+			set_bit (0, fc->scsi_bitmap);
 			for (i = fc->can_queue; i < fc->scsi_bitmap_end; i++)
 				set_bit (i, fc->scsi_bitmap);
 			fc->scsi_free = fc->can_queue;
-			fc->token_tab = (fcp_cmnd **)kmalloc(fc->can_queue * sizeof(fcp_cmnd*), GFP_KERNEL);
+			fc->token_tab = (fcp_cmnd **)kmalloc(slots * sizeof(fcp_cmnd*), GFP_KERNEL);
+			fc->abort_count = 0;
 		} else {
 			fc->scsi_name[0] = 0;
 			kfree (fc->scsi_bitmap);
 			kfree (fc->token_tab);
+			FCND(("Unregistering\n"));
+			if (fc->rst_pkt) {
+				if (fc->rst_pkt->eh_state == SCSI_STATE_UNUSED)
+					kfree(fc->rst_pkt);
+				else {
+					/* Can't happen. Some memory would be lost. */
+					printk("FC: Reset in progress. Now?!");
+				}
+			}
+			FCND(("Unregistered\n"));
 		}
 	} else
 		printk ("FC: %segistering unknown type %02x\n", unregister ? "Unr" : "R", type);
@@ -273,6 +288,7 @@
 		return;
 
 	rsp_status = rsp->fcp_status;
+	FCD(("rsp_status %08x status %08x\n", rsp_status, status))
 	switch (status) {
 	case FC_STATUS_OK:
 		host_status=DID_OK;
@@ -305,11 +321,11 @@
 			if (SCpnt->use_sg)
 				fc_sync_dma_exit_sg((struct scatterlist *)SCpnt->buffer, SCpnt->use_sg, fc);
 			else
-				fc_sync_dma_exit(SCpnt->request_buffer, SCpnt->request_bufflen, fc);
+				fc_sync_dma_exit(fcmd->data, SCpnt->request_bufflen, fc);
 		}
 		break;
 	default:
-		host_status=DID_ERROR;
+		host_status=DID_ERROR; /* FIXME */
 		FCD(("Wrong FC status %d for token %d\n", status, token))
 		break;
 	}
@@ -319,16 +335,21 @@
 	}	
 	
 	SCpnt->result = (host_status << 16) | (rsp_status & 0xff);
+#ifdef FCDEBUG	
+	if (host_status || SCpnt->result || rsp_status) printk("FC: host_status %d, packet status %d\n",
+			host_status, SCpnt->result);
+#endif
 	SCpnt->done = fcmd->done;
 	fcmd->done=NULL;
 	clear_bit(token, fc->scsi_bitmap);
 	fc->scsi_free++;
+	FCD(("Calling scsi_done with %08lx\n", SCpnt->result))
 	SCpnt->scsi_done(SCpnt);
 }
 
 void fcp_receive_solicited(fc_channel *fc, int proto, int token, int status, fc_hdr *fch)
 {
-	FCD(("Receive solicited %d %d %d\n", proto, token, status))
+	FCD(("receive_solicited %d %d %d\n", proto, token, status))
 	switch (proto) {
 	case TYPE_SCSI_FCP:
 		fcp_scsi_receive(fc, token, status, fch); break;
@@ -337,7 +358,7 @@
 			ls *l = (ls *)fc->ls;
 			int i = (token >= l->count) ? token - l->count : token;
 
-			/* Make us sure */
+			/* Let's be sure */
 			if ((unsigned)i < l->count && l->fcmds[i].fc == fc) {
 				fcp_login_done(fc, token, status);
 				break;
@@ -417,6 +438,7 @@
 		fcmd = l->fcmds + i;
 		fc->login = fcmd;
 		fc->ls = (void *)l;
+		fc->rst_pkt = NULL;	/* kmalloc when first used */
 		fch = &fcmd->fch;
 		FILL_FCHDR_RCTL_DID(fch, R_CTL_ELS_REQ, FS_FABRIC_F_PORT);
 		FILL_FCHDR_SID(fch, 0);
@@ -448,7 +470,7 @@
 				} else {
 					fc->state = FC_STATE_OFFLINE;
 					enable_irq(fc->irq);
-					fc_sync_dma_exit (l->logi + 3 * i, 3 * sizeof(logi), fc);
+					fc_sync_dma_exit (l->fcmds[i].cmd, 3 * sizeof(logi), fc);
 					if (atomic_dec_and_test (&l->todo))
 						goto all_done;
 				}
@@ -472,7 +494,7 @@
 		switch (fc->state) {
 		case FC_STATE_ONLINE: break;
 		case FC_STATE_OFFLINE: break;
-		default: fc_sync_dma_exit (l->logi + i, 3 * sizeof(logi), fc);
+		default: fc_sync_dma_exit (l->fcmds[i].cmd, 3 * sizeof(logi), fc);
 			break;
 		}
 		fc->ls = NULL;
@@ -535,24 +557,54 @@
 {
 	fc_channel *fc;
 	int count=0;
+	int ret;
 	
 	for (fc = fcchain; fc; fc = fc->next) {
 		fc->fcp_register = fcp_register;
-		fc->fcp_state_change = fcp_state_change;
 		count++;
 	}
 
-	fcp_initialize (fcchain, count);
+	ret = fcp_initialize (fcchain, count);
+
+	if (!ret) {	
+		if (!fc_channels)
+			fc_channels = fcchain;
+		else {
+			for (fc = fc_channels; fc->next; fc = fc->next);
+			fc->next = fcchain;
+		}
+	}
+	return ret;
+}
+
+void fcp_release(fc_channel *fcchain, int count)  /* count must > 0 */
+{
+	fc_channel *fc;
+	fc_channel *fcx;
+
+	for (fc = fcchain; --count && fc->next; fc = fc->next);
+	if (count) {
+		printk("FC: nothing to release\n");
+		return;
+	}
 	
-	if (!fc_channels)
-		fc_channels = fcchain;
+	if (fc_channels == fcchain)
+		fc_channels = fc->next;
 	else {
-		for (fc = fc_channels; fc->next; fc = fc->next);
-		fc->next = fcchain;
+		for (fcx = fc_channels; fcx->next != fcchain; fcx = fcx->next);
+		fcx->next = fc->next;
 	}
-	return 0;
+	fc->next = NULL;
+
+	/*
+	 *  We've just grabbed fcchain out of the fc_channel list
+	 *  and zero-terminated it, while destroying the count.
+	 *
+	 *  Freeing the fc's is the low level driver's responsibility.
+	 */
 }
 
+
 static void fcp_scsi_done (Scsi_Cmnd *SCpnt)
 {
 	if (FCP_CMND(SCpnt)->done)
@@ -561,16 +613,15 @@
 
 static int fcp_scsi_queue_it(fc_channel *fc, Scsi_Cmnd *SCpnt, fcp_cmnd *fcmd, int prepare)
 {
+	long i;
+	fcp_cmd *cmd;
+	u32 fcp_cntl;
 	if (prepare) {
-		long i;
-		fcp_cmd *cmd;
-		u32 fcp_cntl;
-
 		i = find_first_zero_bit (fc->scsi_bitmap, fc->scsi_bitmap_end);
 		set_bit (i, fc->scsi_bitmap);
 		fcmd->token = i;
 		cmd = fc->scsi_cmd_pool + i;
-		FCD(("Chose token %ld %08lx\n", i, (long)cmd))
+
 		if (fc->encode_addr (SCpnt, cmd->fcp_addr)) {
 			/* Invalid channel/id/lun and couldn't map it into fcp_addr */
 			clear_bit (i, fc->scsi_bitmap);
@@ -620,14 +671,14 @@
 		FCD(("XXX: %04x.%04x.%04x.%04x - %08x%08x%08x\n", cmd->fcp_addr[0], cmd->fcp_addr[1], cmd->fcp_addr[2], cmd->fcp_addr[3], *(u32 *)SCpnt->cmnd, *(u32 *)(SCpnt->cmnd+4), *(u32 *)(SCpnt->cmnd+8)))
 	}
 	FCD(("Trying to enque %08x\n", (int)fcmd))
-	if (!fc->scsi_que[1]) {
+	if (!fc->scsi_que) {
 		if (!fc->hw_enque (fc, fcmd)) {
 			FCD(("hw_enque succeeded for %08x\n", (int)fcmd))
 			return 0;
 		}
 	}
 	FCD(("Putting into que1 %08x\n", (int)fcmd))
-	fcp_scsi_insert_queue (fc, 1, fcmd);
+	fcp_scsi_insert_queue (fc, fcmd);
 	return 0;
 }
 
@@ -643,8 +694,10 @@
 		SCpnt->scsi_done = done;
 		fcmd->proto = TYPE_SCSI_FCP;
 		if (!fc->scsi_free) {
-			FCD(("Putting into que0\n"))
-			fcp_scsi_insert_queue (fc, 0, fcmd);
+			FCD(("FC: !scsi_free, putting cmd on ML queue\n"))
+#if (FCP_SCSI_USE_NEW_EH_CODE == 0)
+			printk("fcp_scsi_queue_command: queue full, losing cmd, bad\n");
+#endif
 			return 1;
 		}
 		return fcp_scsi_queue_it(fc, SCpnt, fcmd, 1);
@@ -656,24 +709,166 @@
 {
 	fcp_cmnd *fcmd;
 	FCD(("Queue empty\n"))
-	while ((fcmd = fc->scsi_que[1])) {
-		/* The hw tell us we can try again queue some packet */
+	while ((fcmd = fc->scsi_que)) {
+		/* The hw told us we can try again queue some packet */
 		if (fc->hw_enque (fc, fcmd))
 			return;
-		fcp_scsi_remove_queue (fc, 1, fcmd);
+		fcp_scsi_remove_queue (fc, fcmd);
 	}
 }
 
+int fcp_old_abort(Scsi_Cmnd *SCpnt)
+{
+	printk("FC: Abort not implemented\n");
+	return 1;
+}
+
 int fcp_scsi_abort(Scsi_Cmnd *SCpnt)
 {
-	printk ("FC: AIEEE, abort!\n");
-	return 0;
+	/* Internal bookkeeping only. Lose 1 token_tab slot. */
+	fcp_cmnd *fcmd = FCP_CMND(SCpnt);
+	fc_channel *fc = FC_SCMND(SCpnt);
+	
+	/*
+	 * We react to abort requests by simply forgetting
+	 * about the command and pretending everything's sweet.
+	 * This may or may not be silly. We can't, however,
+	 * immediately reuse the command's token_tab slot,
+	 * as its result may arrive later and we cannot
+	 * check whether it is the aborted one, can't we?
+	 *
+	 * Therefore, after the first few aborts are done,
+	 * we tell the scsi error handler to do something clever.
+	 * It will eventually call host reset, refreshing
+	 * token_tab for us.
+	 *
+	 * There is a theoretical chance that we sometimes allow
+	 * more than can_queue packets to the jungle this way,
+	 * but the worst outcome possible is a series of
+	 * more aborts and eventually the dev_reset catharsis.
+	 */
+
+	if (++fc->abort_count < (fc->can_queue >> 1)) {
+		SCpnt->result = DID_ABORT;
+		fcmd->done(SCpnt);
+		printk("FC: soft abort\n");
+		return SUCCESS;
+	} else {
+		printk("FC: hard abort refused\n");
+		return FAILED;
+	}
 }
 
-int fcp_scsi_reset(Scsi_Cmnd *SCpnt, unsigned int reset_flags)
+void fcp_scsi_reset_done(Scsi_Cmnd *SCpnt)
 {
-	printk ("FC: AIEEE, reset!\n");
-	return 0;
+	fc_channel *fc = FC_SCMND(SCpnt);
+
+	fc->rst_pkt->eh_state = SCSI_STATE_FINISHED;
+	up(fc->rst_pkt->host->eh_action);
+}
+
+#define FCP_RESET_TIMEOUT (2*HZ)
+
+int fcp_scsi_dev_reset(Scsi_Cmnd *SCpnt)
+{
+	fcp_cmd *cmd;
+	fcp_cmnd *fcmd;
+	fc_channel *fc = FC_SCMND(SCpnt);
+        struct semaphore sem = MUTEX_LOCKED;
+
+	if (!fc->rst_pkt) {
+		fc->rst_pkt = (Scsi_Cmnd *) kmalloc(sizeof(SCpnt), GFP_KERNEL);
+		if (!fc->rst_pkt) return FAILED;
+		
+		fcmd = FCP_CMND(fc->rst_pkt);
+
+
+		fcmd->token = 0;
+		cmd = fc->scsi_cmd_pool + 0;
+		FCD(("Preparing rst packet\n"))
+		if (fc->encode_addr (SCpnt, /*?*/cmd->fcp_addr))
+		fc->rst_pkt->channel = SCpnt->channel;
+		fc->rst_pkt->target = SCpnt->target;
+		fc->rst_pkt->lun = 0;
+		fc->rst_pkt->cmd_len = 0;
+		
+		fc->token_tab[0] = fcmd;
+
+		cmd->fcp_cntl = FCP_CNTL_QTYPE_ORDERED | FCP_CNTL_RESET;
+		fcmd->data = (dma_handle)NULL;
+		fcmd->proto = TYPE_SCSI_FCP;
+
+		memcpy (cmd->fcp_cdb, SCpnt->cmnd, SCpnt->cmd_len);
+		memset (cmd->fcp_cdb+SCpnt->cmd_len, 0, sizeof(cmd->fcp_cdb)-SCpnt->cmd_len);
+		FCD(("XXX: %04x.%04x.%04x.%04x - %08x%08x%08x\n", cmd->fcp_addr[0], cmd->fcp_addr[1], cmd->fcp_addr[2], cmd->fcp_addr[3], *(u32 *)SCpnt->cmnd, *(u32 *)(SCpnt->cmnd+4), *(u32 *)(SCpnt->cmnd+8)))
+	} else {
+		fcmd = FCP_CMND(fc->rst_pkt);
+		if (fc->rst_pkt->eh_state == SCSI_STATE_QUEUED)
+			return FAILED; /* or SUCCESS. Only these */
+	}
+	fc->rst_pkt->done = NULL;
+
+
+        fc->rst_pkt->eh_state = SCSI_STATE_QUEUED;
+
+	fc->rst_pkt->eh_timeout.data = (unsigned long) fc->rst_pkt;
+	fc->rst_pkt->eh_timeout.expires = jiffies + FCP_RESET_TIMEOUT;
+	fc->rst_pkt->eh_timeout.function = (void (*)(unsigned long))fcp_scsi_reset_done;
+
+        add_timer(&fc->rst_pkt->eh_timeout);
+
+	/*
+	 * Set up the semaphore so we wait for the command to complete.
+	 */
+
+	fc->rst_pkt->host->eh_action = &sem;
+	fc->rst_pkt->request.rq_status = RQ_SCSI_BUSY;
+
+	fc->rst_pkt->done = fcp_scsi_reset_done;
+	fcp_scsi_queue_it(fc, fc->rst_pkt, fcmd, 0);
+	
+	down(&sem);
+
+	fc->rst_pkt->host->eh_action = NULL;
+	del_timer(&fc->rst_pkt->eh_timeout);
+
+	/*
+	 * See if timeout.  If so, tell the host to forget about it.
+	 * In other words, we don't want a callback any more.
+	 */
+	if (fc->rst_pkt->eh_state == SCSI_STATE_TIMEOUT ) {
+		fc->rst_pkt->eh_state = SCSI_STATE_UNUSED;
+		return FAILED;
+	}
+	fc->rst_pkt->eh_state = SCSI_STATE_UNUSED;
+	return SUCCESS;
+}
+
+int fcp_scsi_bus_reset(Scsi_Cmnd *SCpnt)
+{
+	printk ("FC: bus reset!\n");
+	return FAILED;
+}
+
+int fcp_scsi_host_reset(Scsi_Cmnd *SCpnt)
+{
+	fc_channel *fc = FC_SCMND(SCpnt);
+	fcp_cmnd *fcmd = FCP_CMND(SCpnt);
+	int i;
+
+	printk ("FC: host reset\n");
+
+	for (i=0; i < fc->can_queue; i++) {
+		if (fc->token_tab[i] && SCpnt->result != DID_ABORT) {
+			SCpnt->result = DID_RESET;
+			fcmd->done(SCpnt);
+			fc->token_tab[i] = NULL;
+		}
+	}
+	fc->reset(fc);
+	fc->abort_count = 0;
+	if (fcp_initialize(fc, 1)) return SUCCESS;
+	else return FAILED;
 }
 
 #ifdef MODULE
diff -u --recursive --new-file v2.1.96/linux/drivers/fc4/fc_syms.c linux/drivers/fc4/fc_syms.c
--- v2.1.96/linux/drivers/fc4/fc_syms.c	Mon Jan 12 15:19:36 1998
+++ linux/drivers/fc4/fc_syms.c	Tue Apr 14 17:44:21 1998
@@ -16,13 +16,18 @@
 #include "fcp_scsi.h"
 
 EXPORT_SYMBOL(fcp_init);
+EXPORT_SYMBOL(fcp_release);
 EXPORT_SYMBOL(fcp_queue_empty);
 EXPORT_SYMBOL(fcp_receive_solicited);
 EXPORT_SYMBOL(fc_channels);
+EXPORT_SYMBOL(fcp_state_change);
 
 /* SCSI stuff */
 EXPORT_SYMBOL(fcp_scsi_queuecommand);
+EXPORT_SYMBOL(fcp_old_abort);
 EXPORT_SYMBOL(fcp_scsi_abort);
-EXPORT_SYMBOL(fcp_scsi_reset);
+EXPORT_SYMBOL(fcp_scsi_dev_reset);
+EXPORT_SYMBOL(fcp_scsi_bus_reset);
+EXPORT_SYMBOL(fcp_scsi_host_reset);
 
 #endif /* CONFIG_MODULES */
diff -u --recursive --new-file v2.1.96/linux/drivers/fc4/fcp_scsi.h linux/drivers/fc4/fcp_scsi.h
--- v2.1.96/linux/drivers/fc4/fcp_scsi.h	Mon Jan 12 15:19:36 1998
+++ linux/drivers/fc4/fcp_scsi.h	Tue Apr 14 17:44:21 1998
@@ -1,6 +1,7 @@
 /* fcp_scsi.h: Generic SCSI on top of FC4 - interface defines.
  *
  * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1998 Jiri Hanika (geo@ff.cuni.cz)
  */
 
 #ifndef _FCP_SCSI_H
@@ -22,6 +23,9 @@
 #error Need to port FC layer to your architecture
 #endif
 
+/* 0 or 1 */
+#define	FCP_SCSI_USE_NEW_EH_CODE	0
+
 #define FC_CLASS_OUTBOUND	0x01
 #define FC_CLASS_INBOUND	0x02
 #define FC_CLASS_SIMPLE		0x03
@@ -62,7 +66,6 @@
 	int			did;
 	char			name[16];
 	void			(*fcp_register)(struct _fc_channel *, u8, int);
-	void			(*fcp_state_change)(struct _fc_channel *, int);
 	void			(*reset)(struct _fc_channel *);
 	int			(*hw_enque)(struct _fc_channel *, fcp_cmnd *);
 	fc_wwn			wwn_node;
@@ -73,8 +76,10 @@
 #ifdef __sparc__	
 	struct linux_sbus_device *dev;
 #endif
+	struct module		*module;
 	/* FCP SCSI stuff */
-	int			can_queue;
+	short			can_queue;
+	short			abort_count;
 	int			rsp_size;
 	fcp_cmd			*scsi_cmd_pool;
 	char			*scsi_rsp_pool;
@@ -83,12 +88,13 @@
 	long			scsi_bitmap_end;
 	int			scsi_free;
 	int			(*encode_addr)(Scsi_Cmnd *cmnd, u16 *addr);
-	fcp_cmnd		*scsi_que[2];
+	fcp_cmnd		*scsi_que;
 	char			scsi_name[4];
 	fcp_cmnd		**token_tab;
 	int			channels;
 	int			targets;
 	long			*ages;
+	Scsi_Cmnd		*rst_pkt;
 	/* LOGIN stuff */
 	fcp_cmnd		*login;
 	void			*ls;
@@ -124,7 +130,9 @@
 
 void fcp_queue_empty(fc_channel *);
 int fcp_init(fc_channel *);
+void fcp_release(fc_channel *fc_chain, int count);
 void fcp_receive_solicited(fc_channel *, int, int, int, fc_hdr *);
+void fcp_state_change(fc_channel *, int);
 
 #define for_each_fc_channel(fc)				\
 	for (fc = fc_channels; fc; fc = fc->next)
@@ -134,7 +142,10 @@
 		if (fc->state == FC_STATE_ONLINE)
 
 int fcp_scsi_queuecommand(Scsi_Cmnd *, void (* done)(Scsi_Cmnd *));
+int fcp_old_abort(Scsi_Cmnd *);
 int fcp_scsi_abort(Scsi_Cmnd *);
-int fcp_scsi_reset(Scsi_Cmnd *, unsigned int);
+int fcp_scsi_dev_reset(Scsi_Cmnd *);
+int fcp_scsi_bus_reset(Scsi_Cmnd *);
+int fcp_scsi_host_reset(Scsi_Cmnd *);
 
 #endif /* !(_FCP_SCSI_H) */
diff -u --recursive --new-file v2.1.96/linux/drivers/fc4/soc.c linux/drivers/fc4/soc.c
--- v2.1.96/linux/drivers/fc4/soc.c	Mon Jan 12 15:19:36 1998
+++ linux/drivers/fc4/soc.c	Tue Apr 14 17:44:21 1998
@@ -1,15 +1,26 @@
 /* soc.c: Sparc SUNW,soc (Serial Optical Channel) Fibre Channel Sbus adapter support.
  *
  * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
- * Copyright (C) 1997 Jiri Hanika (geo@ff.cuni.cz)
+ * Copyright (C) 1997,1998 Jiri Hanika (geo@ff.cuni.cz)
  *
  * Sources:
  *	Fibre Channel Physical & Signaling Interface (FC-PH), dpANS, 1994
  *	dpANS Fibre Channel Protocol for SCSI (X3.269-199X), Rev. 012, 1995
+ *
+ * Supported hardware:
+ *      Tested on SOC sbus card bought with SS1000 in Linux running on SS5 and Ultra1. 
+ *      Should run on on-board SOC/SOC+ cards of Ex000 servers as well, but it is not
+ *      tested (let us know if you succeed).
+ *      For SOC sbus cards, you have to make sure your FCode is 1.52 or later.
+ *      If you have older FCode, you should try to upgrade or get SOC microcode from Sun
+ *      (the microcode is present in Solaris soc driver as well). In that case you need
+ *      to #define HAVE_SOC_UCODE and format the microcode into soc_asm.c. For the exact
+ *      format mail me and I will tell you. I cannot offer you the actual microcode though,
+ *      unless Sun confirms they don't mind.
  */
 
 static char *version =
-        "soc.c:v1.0 24/Nov/97 Jakub Jelinek (jj@sunsite.mff.cuni.cz), Jiri Hanika (geo@ff.cuni.cz)\n";
+        "soc.c:v1.2 27/Feb/98 Jakub Jelinek (jj@sunsite.mff.cuni.cz), Jiri Hanika (geo@ff.cuni.cz)\n";
 
 #include <linux/module.h>
 #include <linux/kernel.h>
@@ -68,6 +79,7 @@
 	s->regs->sae = 0; s->regs->cfg = s->cfg;
 	s->regs->cmd = SOC_CMD_RSP_QALL; 
 	SOC_SETIMASK(s, SOC_IMASK_RSP_QALL | SOC_IMASK_SAE);
+	SOD(("imask %08lx %08lx\n", s->imask, s->regs->imask));
 }
 
 static void soc_reset(fc_channel *fc)
@@ -148,6 +160,7 @@
 static void inline soc_request (struct soc *s, u32 cmd)
 {
 	SOC_SETIMASK(s, s->imask & ~(cmd & SOC_CMD_REQ_QALL));
+	SOD(("imask %08lx %08lx\n", s->imask, s->regs->imask));
 
 	SOD(("Queues available %08x OUT %X %X\n", cmd, xram_get_8((xram_p)&s->req[0].hw_cq->out), xram_get_8((xram_p)&s->req[0].hw_cq->out)))
 	if (s->port[s->curr_port].fc.state != FC_STATE_OFFLINE) {
@@ -179,6 +192,7 @@
 	while (sw_cq->in != sw_cq->out) {
 		/* ...real work per entry here... */
 		hwrsp = (soc_rsp *)sw_cq->pool + sw_cq->out;
+
 		hwrspc = NULL;
 		flags = hwrsp->shdr.flags;
 		count = xram_get_8 ((xram_p)&hwrsp->count);
@@ -218,10 +232,12 @@
 			status = xram_get_32low ((xram_p)&hwrsp->status);
 			switch (status) {
 			case SOC_ONLINE:
-				fc->fcp_state_change(fc, FC_STATE_ONLINE);
+				SOD(("State change to ONLINE\n"));
+				fcp_state_change(fc, FC_STATE_ONLINE);
 				break;
 			case SOC_OFFLINE:
-				fc->fcp_state_change(fc, FC_STATE_OFFLINE);
+				SOD(("State change to OFFLINE\n"));
+				fcp_state_change(fc, FC_STATE_OFFLINE);
 				break;
 			default:
 				printk ("%s: Unknown STATUS no %d\n", fc->name, status);
@@ -316,8 +332,10 @@
 	else
 		qno = 0;
 	SOD(("Putting a FCP packet type %d into hw queue %d\n", fcmd->proto, qno))
-	if (s->imask & (SOC_IMASK_REQ_Q0 << qno))
+	if (s->imask & (SOC_IMASK_REQ_Q0 << qno)) {
+		SOD(("EIO %08x\n", s->imask))
 		return -EIO;
+	}
 	sw_cq = s->req + qno;
 	cq_next_in = (sw_cq->in + 1) & sw_cq->last;
 	
@@ -325,7 +343,9 @@
 		    && cq_next_in == (sw_cq->out = xram_get_8((xram_p)&sw_cq->hw_cq->out))) {
 		SOD(("%d IN %d OUT %d LAST %d\n", qno, sw_cq->in, sw_cq->out, sw_cq->last))
 		SOC_SETIMASK(s, s->imask | (SOC_IMASK_REQ_Q0 << qno));
-		return -EBUSY;  // If queue full, just say NO
+		SOD(("imask %08lx %08lx\n", s->imask, s->regs->imask));
+		/* If queue is full, just say NO */
+		return -EBUSY;
 	}
 	
 	request = sw_cq->pool + sw_cq->in;
@@ -478,9 +498,7 @@
 
 static inline void soc_init(struct linux_sbus_device *sdev, int no)
 {
-#ifdef __sparc_v9__
         struct devid_cookie dcookie;
-#endif
 	unsigned char tmp[60];
 	int propl;
 	struct soc *s;
@@ -497,7 +515,14 @@
 	SOD(("socs %08lx soc_intr %08lx soc_hw_enque %08x\n", (long)socs, (long)soc_intr, (long)soc_hw_enque))	
 	if (version_printed++ == 0)
 		printk (version);
-		
+#ifdef MODULE
+	s->port[0].fc.module = &__this_module;
+	s->port[1].fc.module = &__this_module;
+#else
+	s->port[0].fc.module = NULL;
+	s->port[1].fc.module = NULL;
+#endif
+	                                	
 	s->next = socs;
 	socs = s;
 	s->port[0].fc.dev = sdev;
@@ -574,9 +599,22 @@
 	irq = sdev->irqs[0].pri;
 
 #ifndef __sparc_v9__	
-	if (request_irq (irq, soc_intr, SA_SHIRQ, "SOC", (void *)s)) {
-		soc_printk ("Cannot order irq %d to go\n", irq);
-		return;
+	if (sparc_cpu_model != sun4d) {
+		if (request_irq (irq, soc_intr, SA_SHIRQ, "SOC", (void *)s)) {
+			soc_printk ("Cannot order irq %d to go\n", irq);
+			socs = s->next;
+			return;
+		}
+	} else {
+	        dcookie.real_dev_id = s;
+	        dcookie.bus_cookie = sdev;
+	        if (request_irq(irq, soc_intr, (SA_SHIRQ | SA_DCOOKIE), "SOC", &dcookie)) {
+			soc_printk ("Cannot order irq %d to go\n", irq);
+			socs = s->next;
+			return;
+		}
+		SOD(("IRQ %d %x\n", irq, dcookie.ret_ino))
+		irq = dcookie.ret_ino;
 	}
 #else
 	dcookie.real_dev_id = s;
@@ -585,6 +623,7 @@
 	dcookie.bus_cookie = sdev->my_bus;
 	if (request_irq (irq, soc_intr, (SA_SHIRQ | SA_SBUS | SA_DCOOKIE), "SOC", &dcookie)) {
 		soc_printk ("Cannot order irq %d to go\n", irq);
+		socs = s->next;
 		return;
 	}
 	irq = dcookie.ret_ino;
@@ -693,10 +732,12 @@
 	struct linux_sbus_device *sdev;
 	
 	for_each_soc(s) {
-		/* FIXME: Tell FC layer we say good bye */
 		irq = s->port[0].fc.irq;
 		disable_irq (irq);
 		free_irq (irq, s);
+
+		fcp_release(&(s->port[0].fc), 2);
+
 		sdev = s->port[0].fc.dev;
 		if (sdev->num_registers == 1)
 			sparc_free_io ((char *)s->xram, sdev->reg_addrs [0].reg_size);
diff -u --recursive --new-file v2.1.96/linux/drivers/macintosh/Makefile linux/drivers/macintosh/Makefile
--- v2.1.96/linux/drivers/macintosh/Makefile	Mon Jan 12 15:18:13 1998
+++ linux/drivers/macintosh/Makefile	Tue Apr 14 17:33:59 1998
@@ -14,7 +14,10 @@
 
 L_TARGET := macintosh.a
 M_OBJS   :=
-L_OBJS   := via-cuda.o adb.o nvram.o macio-adb.o
+
+ifndef CONFIG_MBX
+L_OBJS   := via-cuda.o adb.o nvram.o macio-adb.o via-pmu.o mediabay.o
+endif
 
 ifeq ($(CONFIG_SERIAL)$(CONFIG_PMAC),yy)
   LX_OBJS += macserial.o
@@ -25,7 +28,7 @@
 endif
 
 ifdef CONFIG_MAC_KEYBOARD
-L_OBJS += mac_keyb.o mackeymap.o
+L_OBJS += mac_keyb.o
 endif
 
 ifdef CONFIG_VT
@@ -54,5 +57,7 @@
 
 include $(TOPDIR)/Rules.make
 
-mackeymap.c: mackeymap.map
-	loadkeys --mktable mackeymap.map > mackeymap.c
+# Integrated in mac_keyb.c
+# mackeymap.map is retained for historical reasons
+#mackeymap.c: mackeymap.map
+#	loadkeys --mktable mackeymap.map > mackeymap.c
diff -u --recursive --new-file v2.1.96/linux/drivers/macintosh/adb.c linux/drivers/macintosh/adb.c
--- v2.1.96/linux/drivers/macintosh/adb.c	Mon Jan 12 15:18:13 1998
+++ linux/drivers/macintosh/adb.c	Tue Apr 14 17:33:59 1998
@@ -10,18 +10,23 @@
 #include <linux/malloc.h>
 #include <linux/fs.h>
 #include <linux/mm.h>
+#include <linux/sched.h>
 #include <asm/prom.h>
 #include <asm/adb.h>
 #include <asm/cuda.h>
+#include <asm/pmu.h>
 #include <asm/uaccess.h>
 #include <asm/hydra.h>
 
 enum adb_hw adb_hardware;
 int (*adb_send_request)(struct adb_request *req, int sync);
 int (*adb_autopoll)(int on);
+static void adb_scan_bus(void);
 
 static struct adb_handler {
 	void (*handler)(unsigned char *, int, struct pt_regs *, int);
+	int original_address;
+	int handler_id;
 } adb_handler[16];
 
 static int adb_nodev(void)
@@ -29,17 +34,123 @@
 	return -1;
 }
 
+#if 0
+static void printADBreply(struct adb_request *req)
+{
+        int i;
+
+        printk("adb reply (%d)", req->reply_len);
+        for(i = 0; i < req->reply_len; i++)
+                printk(" %x", req->reply[i]);
+        printk("\n");
+
+}
+#endif
+
+static void adb_scan_bus(void)
+{
+	int i, highFree=0, noMovement;
+	struct adb_request req;
+	
+	/* reset ADB bus */
+	/*adb_request(&req, NULL, ADBREQ_SYNC, 1, 0);*/
+
+	/* assumes adb_handler[] is all zeroes at this point */
+	for (i = 1; i < 16; i++) {
+		/* see if there is anything at address i */
+		adb_request(&req, NULL, ADBREQ_SYNC | ADBREQ_REPLY, 1,
+                            (i << 4) | 0xf);
+		if (req.reply_len > 1)
+			/* one or more devices at this address */
+			adb_handler[i].original_address = i;
+		else if (i > highFree)
+			highFree = i;
+	}
+
+	/* Note we reset noMovement to 0 each time we move a device */
+	for (noMovement = 1; noMovement < 2 && highFree > 0; noMovement++) {
+		for (i = 1; i < 16; i++) {
+			if (adb_handler[i].original_address == 0)
+				continue;
+			/*
+			 * Send a "talk register 3" command to address i
+			 * to provoke a collision if there is more than
+			 * one device at this address.
+			 */
+			adb_request(&req, NULL, ADBREQ_SYNC | ADBREQ_REPLY, 1,
+				    (i << 4) | 0xf);
+			/*
+			 * Move the device(s) which didn't detect a
+			 * collision to address `highFree'.  Hopefully
+			 * this only moves one device.
+			 */
+			adb_request(&req, NULL, ADBREQ_SYNC, 3,
+				    (i<< 4) | 0xb, (highFree | 0x60), 0xfe);
+			/*
+			 * Test whether there are any device(s) left
+			 * at address i.
+			 */
+			adb_request(&req, NULL, ADBREQ_SYNC | ADBREQ_REPLY, 1,
+				    (i << 4) | 0xf);
+			if (req.reply_len > 1) {
+				/*
+				 * There are still one or more devices
+				 * left at address i.  Register the one(s)
+				 * we moved to `highFree', and find a new
+				 * value for highFree.
+				 */
+				adb_handler[highFree].original_address =
+					adb_handler[i].original_address;
+				while (highFree > 0 &&
+				       adb_handler[highFree].original_address)
+					highFree--;
+				if (highFree <= 0)
+					break;
+
+				noMovement = 0;
+			}
+			else {
+				/*
+				 * No devices left at address i; move the
+				 * one(s) we moved to `highFree' back to i.
+				 */
+				adb_request(&req, NULL, ADBREQ_SYNC, 3,
+					    (highFree << 4) | 0xb,
+					    (i | 0x60), 0xfe);
+			}
+		}	
+	}
+
+	/* Now fill in the handler_id field of the adb_handler entries. */
+	printk(KERN_DEBUG "adb devices:");
+	for (i = 1; i < 16; i++) {
+		if (adb_handler[i].original_address == 0)
+			continue;
+		adb_request(&req, NULL, ADBREQ_SYNC | ADBREQ_REPLY, 1,
+			    (i << 4) | 0xf);
+		adb_handler[i].handler_id = req.reply[2];
+		printk(" [%d]: %d %x", i, adb_handler[i].original_address,
+		       adb_handler[i].handler_id);
+	}
+	printk("\n");
+}
+
 void adb_init(void)
 {
 	adb_hardware = ADB_NONE;
 	adb_send_request = (void *) adb_nodev;
 	adb_autopoll = (void *) adb_nodev;
+	if ( (_machine != _MACH_chrp) && (_machine != _MACH_Pmac) )
+		return;		
 	via_cuda_init();
+	via_pmu_init();
 	macio_adb_init();
 	if (adb_hardware == ADB_NONE)
 		printk(KERN_WARNING "Warning: no ADB interface detected\n");
-	else
+	else {
+		adb_scan_bus();
 		adb_autopoll(1);
+	}
 }
 
 int
@@ -67,12 +178,25 @@
 /* Ultimately this should return the number of devices with
    the given default id. */
 int
-adb_register(int default_id,
+adb_register(int default_id, int handler_id, struct adb_ids *ids,
 	     void (*handler)(unsigned char *, int, struct pt_regs *, int))
 {
-	if (adb_handler[default_id].handler != 0)
-		panic("Two handlers for ADB device %d\n", default_id);
-	adb_handler[default_id].handler = handler;
+	int i;
+
+	ids->nids = 0;
+	for (i = 1; i < 16; i++) {
+		if ((adb_handler[i].original_address == default_id) ||
+		    (adb_handler[i].handler_id == handler_id)) {
+			if (adb_handler[i].handler != 0) {
+				printk(KERN_ERR
+				       "Two handlers for ADB device %d\n",
+				       default_id);
+				return 0;
+			}
+			adb_handler[i].handler = handler;
+			ids->id[ids->nids++] = i;
+		}
+	}
 	return 1;
 }
 
@@ -103,44 +227,40 @@
 extern void adbdev_init(void);
 
 struct adbdev_state {
-	struct adb_request req;
+	spinlock_t	lock;
+	atomic_t	n_pending;
+	struct adb_request *completed;
+	struct wait_queue *wait_queue;
+	int		inuse;
 };
 
-static struct wait_queue *adb_wait;
-
-static int adb_wait_reply(struct adbdev_state *state, struct file *file)
-{
-	int ret = 0;
-	struct wait_queue wait = { current, NULL };
-
-	add_wait_queue(&adb_wait, &wait);
-	current->state = TASK_INTERRUPTIBLE;
-
-	while (!state->req.complete) {
-		if (file->f_flags & O_NONBLOCK) {
-			ret = -EAGAIN;
-			break;
-		}
-		if (signal_pending(current)) {
-			ret = -ERESTARTSYS;
-			break;
-		}
-		schedule();
-	}
-
-	current->state = TASK_RUNNING;
-	remove_wait_queue(&adb_wait, &wait);
-
-	return ret;
-}
-
 static void adb_write_done(struct adb_request *req)
 {
+	struct adbdev_state *state = (struct adbdev_state *) req->arg;
+	unsigned long flags;
+
 	if (!req->complete) {
 		req->reply_len = 0;
 		req->complete = 1;
 	}
-	wake_up_interruptible(&adb_wait);
+	spin_lock_irqsave(&state->lock, flags);
+	atomic_dec(&state->n_pending);
+	if (!state->inuse) {
+		kfree(req);
+		if (atomic_read(&state->n_pending) == 0) {
+			spin_unlock_irqrestore(&state->lock, flags);
+			kfree(state);
+			return;
+		}
+	} else {
+		struct adb_request **ap = &state->completed;
+		while (*ap != NULL)
+			ap = &(*ap)->next;
+		req->next = NULL;
+		*ap = req;
+		wake_up_interruptible(&state->wait_queue);
+	}
+	spin_unlock_irqrestore(&state->lock, flags);
 }
 
 static int adb_open(struct inode *inode, struct file *file)
@@ -153,20 +273,31 @@
 	if (state == 0)
 		return -ENOMEM;
 	file->private_data = state;
-	state->req.reply_expected = 0;
+	spin_lock_init(&state->lock);
+	atomic_set(&state->n_pending, 0);
+	state->completed = NULL;
+	state->wait_queue = NULL;
+	state->inuse = 1;
+
 	return 0;
 }
 
 static int adb_release(struct inode *inode, struct file *file)
 {
 	struct adbdev_state *state = file->private_data;
+	unsigned long flags;
 
 	if (state) {
 		file->private_data = NULL;
-		if (state->req.reply_expected && !state->req.complete)
-			if (adb_wait_reply(state, file))
-				return 0;
-		kfree(state);
+		spin_lock_irqsave(&state->lock, flags);
+		if (atomic_read(&state->n_pending) == 0
+		    && state->completed == NULL) {
+			spin_unlock_irqrestore(&state->lock, flags);
+			kfree(state);
+		} else {
+			state->inuse = 0;
+			spin_unlock_irqrestore(&state->lock, flags);
+		}
 	}
 	return 0;
 }
@@ -181,27 +312,57 @@
 {
 	int ret;
 	struct adbdev_state *state = file->private_data;
+	struct adb_request *req;
+	struct wait_queue wait = { current, NULL };
+	unsigned long flags;
 
 	if (count < 2)
 		return -EINVAL;
-	if (count > sizeof(state->req.reply))
-		count = sizeof(state->req.reply);
+	if (count > sizeof(req->reply))
+		count = sizeof(req->reply);
 	ret = verify_area(VERIFY_WRITE, buf, count);
 	if (ret)
 		return ret;
 
-	if (!state->req.reply_expected)
-		return 0;
+	req = NULL;
+	add_wait_queue(&state->wait_queue, &wait);
+	current->state = TASK_INTERRUPTIBLE;
+
+	for (;;) {
+		spin_lock_irqsave(&state->lock, flags);
+		req = state->completed;
+		if (req != NULL)
+			state->completed = req->next;
+		else if (atomic_read(&state->n_pending) == 0)
+			ret = -EIO;
+		spin_unlock_irqrestore(&state->lock, flags);
+		if (req != NULL || ret != 0)
+			break;
+		
+		if (file->f_flags & O_NONBLOCK) {
+			ret = -EAGAIN;
+			break;
+		}
+		if (signal_pending(current)) {
+			ret = -ERESTARTSYS;
+			break;
+		}
+		schedule();
+	}
+
+	current->state = TASK_RUNNING;
+	remove_wait_queue(&state->wait_queue, &wait);
 
-	ret = adb_wait_reply(state, file);
 	if (ret)
 		return ret;
 
-	state->req.reply_expected = 0;
-	ret = state->req.reply_len;
-	if (copy_to_user(buf, state->req.reply, ret))
-		return -EFAULT;
+	ret = req->reply_len;
+	if (ret > count)
+		ret = count;
+	if (ret > 0 && copy_to_user(buf, req->reply, ret))
+		ret = -EFAULT;
 
+	kfree(req);
 	return ret;
 }
 
@@ -210,46 +371,64 @@
 {
 	int ret, i;
 	struct adbdev_state *state = file->private_data;
+	struct adb_request *req;
 
-	if (count < 2 || count > sizeof(state->req.data))
+	if (count < 2 || count > sizeof(req->data))
 		return -EINVAL;
 	ret = verify_area(VERIFY_READ, buf, count);
 	if (ret)
 		return ret;
 
-	if (state->req.reply_expected && !state->req.complete) {
-		/* A previous request is still being processed.
-		   Wait for it to finish. */
-		ret = adb_wait_reply(state, file);
-		if (ret)
-			return ret;
-	}
+	req = (struct adb_request *) kmalloc(sizeof(struct adb_request),
+					     GFP_KERNEL);
+	if (req == NULL)
+		return -ENOMEM;
 
-	state->req.nbytes = count;
-	state->req.done = adb_write_done;
-	state->req.complete = 0;
-	if (copy_from_user(state->req.data, buf, count))
-		return -EFAULT;
+	req->nbytes = count;
+	req->done = adb_write_done;
+	req->arg = (void *) state;
+	req->complete = 0;
+
+	ret = -EFAULT;
+	if (copy_from_user(req->data, buf, count))
+		goto out;
 
+	atomic_inc(&state->n_pending);
 	switch (adb_hardware) {
 	case ADB_NONE:
-		return -ENXIO;
+		ret = -ENXIO;
+		break;
 	case ADB_VIACUDA:
-		state->req.reply_expected = 1;
-		cuda_send_request(&state->req);
+		req->reply_expected = 1;
+		ret = cuda_send_request(req);
 		break;
+	case ADB_VIAPMU:
+		if (req->data[0] != ADB_PACKET) {
+			ret = pmu_send_request(req);
+			break;
+		}
+		/* else fall through */
 	default:
-		if (state->req.data[0] != ADB_PACKET)
-			return -EINVAL;
-		for (i = 1; i < state->req.nbytes; ++i)
-			state->req.data[i] = state->req.data[i+1];
-		state->req.reply_expected =
-			((state->req.data[0] & 0xc) == 0xc);
-		adb_send_request(&state->req, 0);
+		ret = -EINVAL;
+		if (req->data[0] != ADB_PACKET)
+			break;
+		for (i = 0; i < req->nbytes-1; ++i)
+			req->data[i] = req->data[i+1];
+		req->nbytes--;
+		req->reply_expected = ((req->data[0] & 0xc) == 0xc);
+		ret = adb_send_request(req, 0);
 		break;
 	}
 
+	if (ret != 0) {
+		atomic_dec(&state->n_pending);
+		goto out;
+	}
 	return count;
+
+out:
+	kfree(req);
+	return ret;
 }
 
 static struct file_operations adb_fops = {
@@ -266,6 +445,8 @@
 
 void adbdev_init()
 {
+	if ( (_machine != _MACH_chrp) && (_machine != _MACH_Pmac) )
+		return;		
 	if (register_chrdev(ADB_MAJOR, "adb", &adb_fops))
 		printk(KERN_ERR "adb: unable to get major %d\n", ADB_MAJOR);
 }
diff -u --recursive --new-file v2.1.96/linux/drivers/macintosh/ati-gx.h linux/drivers/macintosh/ati-gx.h
--- v2.1.96/linux/drivers/macintosh/ati-gx.h	Mon Jan 12 15:18:13 1998
+++ linux/drivers/macintosh/ati-gx.h	Tue Apr 14 17:33:59 1998
@@ -1,64 +1,37 @@
-#if 0		/* not filled inaty_gt_reg_init yet */
-/* Register values for 1280x1024, 75Hz mode (20) */
+/* Register values for 1280x1024, 75Hz (WAS 60) mode (20) */
 static struct aty_regvals aty_gx_reg_init_20 = {
-	{ 0x10, 0x28, 0x3c },
-	{ },
-	{ }	/* pixel clock = 134.61MHz for V=74.81Hz */
-};
+   { 0x200, 0x200, 0x200 },
 
-/* Register values for 1280x960, 75Hz mode (19) */
-static struct aty_regvals aty_gx_reg_init_19 = {
-	{ 0x10, 0x28, 0x3c },
-	{ },
-	{ }	/* pixel clock = 126.01MHz for V=75.01 Hz */
-};
+    { 0x1200a5, 0x1200a3, 0x1200a3 },
+    { 0x30c0200, 0x30e0300, 0x30e0300 },
+    { 0x2, 0x3, 0x3 },
+
+    0x9f00d2, 0x3ff0429, 0x30400, 0x28100040,
+    { 0xd4, 0x9 }
+};     
 
 /* Register values for 1152x870, 75Hz mode (18) */
 static struct aty_regvals aty_gx_reg_init_18 = {
-	{ 0x10, 0x28, 0x50 },
-	{ },
-	{ }	/* pixel clock = 100.33MHz for V=75.31Hz */
-};
+    { 0x200, 0x200, 0x200 },
 
-/* Register values for 1024x768, 75Hz mode (17) */
-static struct aty_regvals aty_gx_reg_init_17 = {
-	{ 0x10, 0x28, 0x50 },
-	{ },
-	{ }	/* pixel clock = 79.55MHz for V=74.50Hz */
-};
+    { 0x300097, 0x300095, 0x300094 },
+    { 0x3090200, 0x30e0300, 0x30e0600 },
+    { 0x2, 0x3, 0x6 },
 
-/* Register values for 1024x768, 72Hz mode (15) */
-static struct aty_regvals aty_gx_reg_init_15 = {
-	{ 0x10, 0x28, 0x50 },
-	{ },
-	{ }	/* pixel clock = 78.12MHz for V=72.12Hz */
+    0x8f00b5, 0x3650392, 0x230368, 0x24100040,
+    { 0x53, 0x3 }
 };
 
-#endif
-
-
-/* Register values for 1280x1024, 60Hz mode (20) */
-static struct aty_regvals aty_gx_reg_init_20 = {
-   { 0, 0, 0 },
-
-    { 0x310086, 0x310084, 0x310084 },
-    { 0x3070200, 0x30e0300, 0x30e0300 },
-    { 0x2002312, 0x3002312, 0x3002312 },
-
-    0x7f00a5, 0x2ff0325, 0x260302, 0x20100000,
-    { 0x88, 0x7 }
-};     
-
 /* Register values for 1024x768, 75Hz mode (17) */
 static struct aty_regvals aty_gx_reg_init_17 = {
-    { 0, 0, 0 },
+    { 0x200, 0x200, 0x200 },
 
-    { 0xc0085, 0xc0083, 0xc0083 },
-    { 0x3070200, 0x30e0300, 0x30e0300 },
-    { 0x2002312, 0x3002312, 0x3002312 },
+    { 0x2c0087, 0x2c0085, 0x2c0084 },
+    { 0x3070200, 0x30e0300, 0x30e0600 },
+    { 0x2, 0x3, 0x6 },
 
-    0x7f00a3, 0x2ff031f, 0x30300, 0x20100000,
-    { 0x41, 0x3 }
+    0x7f00a5, 0x2ff0323, 0x230302, 0x20100000,
+    { 0x42, 0x3 }
 };
 
 /* Register values for 1024x768, 72Hz mode (15) */
@@ -87,14 +60,14 @@
 
 /* Register values for 832x624, 75Hz mode (13) */
 static struct aty_regvals aty_gx_reg_init_13 = {
-	{ 0x200, 0x200, 0x200 },
+    { 0x200, 0x200, 0x200 },
 
-	{ 0x28006f, 0x28006d, 0x28006c },
-	{ 0x3050200, 0x30b0300, 0x30e0600 },
-	{ 0x2002312, 0x3002312, 0x6002312 },
+    { 0x28006f, 0x28006d, 0x28006c },
+    { 0x3050200, 0x30b0300, 0x30e0600 },
+    { 0x2, 0x3, 0x6 },
 
-	0x67008f, 0x26f029a, 0x230270, 0x1a100040,
-        { 0x4f, 0x05 }
+    0x67008f, 0x26f029a, 0x230270, 0x1a100040,
+    { 0x4f, 0x5 }
 };
 
 #if 0		/* not filled in yet */
diff -u --recursive --new-file v2.1.96/linux/drivers/macintosh/aty.c linux/drivers/macintosh/aty.c
--- v2.1.96/linux/drivers/macintosh/aty.c	Tue Mar 10 10:03:32 1998
+++ linux/drivers/macintosh/aty.c	Tue Apr 14 17:33:59 1998
@@ -18,12 +18,12 @@
 #include <linux/string.h>
 #include <linux/vc_ioctl.h>
 #include <linux/pci.h>
-#include <linux/bios32.h>
 #include <linux/nvram.h>
 #include <linux/selection.h>
 #include <linux/vt_kern.h>
 #include <asm/prom.h>
 #include <asm/io.h>
+#include <asm/pgtable.h>
 #include <asm/pci-bridge.h>
 #include "pmac-cons.h"
 #include "aty.h"
@@ -61,10 +61,12 @@
 static aty_regvals *get_aty_struct(void);
 
 static unsigned char *frame_buffer;
+static unsigned long frame_buffer_phys;
 static int total_vram;		/* total amount of video memory, bytes */
 static int chip_type;		/* what chip type was detected */
 
 static unsigned long ati_regbase;
+static unsigned long ati_regbase_phys;
 static struct aty_cmap_regs *aty_cmap_regs;
 
 #if 0
@@ -126,7 +128,8 @@
 	&aty_gx_reg_init_15,
 	NULL,
 	&aty_gx_reg_init_17,
-	NULL, NULL,
+	&aty_gx_reg_init_18,
+	NULL,
 	&aty_gx_reg_init_20
 };
 
@@ -323,7 +326,8 @@
 	printk("Warning: expecting 1 or 3 addresses for ATY (got %d)",
 	       dp->n_addrs);
 
-	ati_regbase = (int) ioremap((0x7ffc00 + dp->addrs[0].address), 0x1000);
+	ati_regbase_phys = 0x7ffc00 + dp->addrs[0].address;
+	ati_regbase = (int) ioremap(ati_regbase_phys, 0x1000);
 	aty_cmap_regs = (struct aty_cmap_regs *) (ati_regbase + 0xC0);
 
 	/* enable memory-space accesses using config-space command register */
@@ -384,12 +388,12 @@
 			total_vram = 0x80000;
 		}
 
-#if 1
+#if 0
 	printk("aty_display_init: node = %p, addrs = ", dp->node);
 	printk(" %x(%x)", dp->addrs[0].address, dp->addrs[0].size);
 	printk(", intrs =");
 	for (i = 0; i < dp->n_intrs; ++i)
-	printk(" %x", dp->intrs[i]);
+	printk(" %x", dp->intrs[i].line);
 	printk("\nregbase: %x pci loc: %x:%x total_vram: %x cregs: %x\n", (int) ati_regbase,
 		bus, devfn, total_vram, (int) aty_cmap_regs);
 #endif
@@ -398,10 +402,11 @@
 
 	/* use the big-endian aperture (??) */
 	addr += 0x800000;
-	frame_buffer = ioremap(addr, 0x800000);
+	frame_buffer_phys = addr;
+	frame_buffer = __ioremap(addr, 0x800000, _PAGE_WRITETHRU);
 
 	sense = read_aty_sense();
-	printk("monitor sense = %x\n", sense);
+	printk(KERN_INFO "monitor sense = %x\n", sense);
 	if (video_mode == VMODE_NVRAM) {
 		video_mode = nvram_read_byte(NV_VMODE);
 		init = get_aty_struct();
@@ -720,11 +725,11 @@
 		break;
 	}
 	display_info.fb_address = (chip_type != MACH64_GT_ID) ?
-		(unsigned long) frame_buffer + init->offset[color_mode] :
-		(unsigned long) frame_buffer;
-	display_info.cmap_adr_address = (unsigned long) &aty_cmap_regs->windex;
-	display_info.cmap_data_address = (unsigned long) &aty_cmap_regs->lut;
-	display_info.disp_reg_address = ati_regbase;
+		frame_buffer_phys + init->offset[color_mode] :
+		frame_buffer_phys;
+	display_info.cmap_adr_address = ati_regbase_phys + 0xc0;
+	display_info.cmap_data_address = ati_regbase_phys + 0xc1;
+	display_info.disp_reg_address = ati_regbase_phys;
 }
 
 int
diff -u --recursive --new-file v2.1.96/linux/drivers/macintosh/chips.c linux/drivers/macintosh/chips.c
--- v2.1.96/linux/drivers/macintosh/chips.c	Mon Jan 12 15:18:13 1998
+++ linux/drivers/macintosh/chips.c	Tue Apr 14 17:33:59 1998
@@ -14,10 +14,13 @@
 #include <linux/string.h>
 #include <linux/vc_ioctl.h>
 #include <linux/pci.h>
-#include <linux/bios32.h>
 #include <linux/selection.h>
 #include <asm/prom.h>
 #include <asm/io.h>
+#include <asm/pgtable.h>
+#include <asm/adb.h>
+#include <asm/cuda.h>
+#include <asm/pmu.h>
 #include <asm/pci-bridge.h>
 #include "pmac-cons.h"
 #include "chips.h"
@@ -26,6 +29,8 @@
 static unsigned char *frame_buffer;
 static unsigned char *blitter_regs;
 static unsigned char *io_space;
+static unsigned long chips_base_phys;
+static unsigned long chips_io_phys;
 
 void
 map_chips_display(struct device_node *dp)
@@ -35,17 +40,21 @@
     unsigned long addr;
 
     addr = dp->addrs[0].address;
-    frame_buffer = ioremap(addr + 0x800000, 0x100000);
+    chips_base_phys = addr;
+    frame_buffer = __ioremap(addr + 0x800000, 0x100000, _PAGE_WRITETHRU);
     blitter_regs = ioremap(addr + 0xC00000, 4096);
     
-    printk("Mapped chips65550 frame buffer at %p, blitter at %p\n", frame_buffer, blitter_regs);
+    printk("Mapped chips65550 frame buffer at %p, blitter at %p\n",
+	   frame_buffer, blitter_regs);
     
     if (pci_device_loc(dp, &bus, &devfn) == 0) {
 	pcibios_read_config_word(bus, devfn, PCI_COMMAND, &cmd);
 	cmd |= 3;	// enable memory and IO space
 	pcibios_write_config_word(bus, devfn, PCI_COMMAND, cmd);
-	io_space = ioremap((unsigned long) pci_io_base(bus), 4096);
-	printk("Mapped chips65550 IO space at %p\n", io_space);
+	io_space = (unsigned char *) pci_io_base(bus);
+	/* XXX really want the physical address here */
+	chips_io_phys = (unsigned long) pci_io_base(bus);
+	printk("Chips65550 IO space at %p\n", io_space);
     }
 
     video_mode = VMODE_800_600_60;
@@ -65,11 +74,15 @@
     unsigned *p;
     int i, hres;
 
-    if (video_mode != VMODE_800_600_60)
-	panic("chips65550: display mode %d not supported", video_mode);
+    if (video_mode != VMODE_800_600_60) {
+	printk(KERN_ERR "chips65550: display mode %d not supported", video_mode);
+	video_mode = VMODE_800_600_60;
+    }
 
-    if (color_mode != CMODE_8 && color_mode != CMODE_16)
-	panic("chips65550: color mode %d not supported", color_mode);
+    if (color_mode != CMODE_8 && color_mode != CMODE_16) {
+	printk(KERN_ERR "chips65550: color mode %d not supported", color_mode);
+	color_mode = CMODE_8;
+    }
     
     n_scanlines = 600;
     hres        = 800;
@@ -106,15 +119,18 @@
     display_info.pitch = line_pitch;
     display_info.mode = video_mode;
     strncpy(display_info.name, "chips65550", sizeof(display_info.name));
-    display_info.fb_address = (unsigned long) frame_buffer;
-    display_info.cmap_adr_address = 0;
-    display_info.cmap_data_address = 0;
-    display_info.disp_reg_address = 0;
+    display_info.fb_address = chips_base_phys + 0x800000;
+    display_info.cmap_adr_address = chips_io_phys + 0x3c8;
+    display_info.cmap_data_address = chips_io_phys + 0x3c9;
+    display_info.disp_reg_address = chips_base_phys + 0xC00000;
 
     /* Clear screen */
     p = (unsigned *) frame_buffer;
     for (i = n_scanlines * line_pitch / sizeof(unsigned); i != 0; --i)
 	*p++ = 0;
+
+    /* Turn on backlight */
+    pmu_enable_backlight(1);
 }
 
 int
@@ -164,4 +180,5 @@
 void
 chips_set_blanking(int blank_mode)
 {
+    pmu_enable_backlight(blank_mode == VESA_NO_BLANKING);
 }
diff -u --recursive --new-file v2.1.96/linux/drivers/macintosh/control.c linux/drivers/macintosh/control.c
--- v2.1.96/linux/drivers/macintosh/control.c	Mon Jan 12 15:18:13 1998
+++ linux/drivers/macintosh/control.c	Tue Apr 14 17:33:59 1998
@@ -18,6 +18,7 @@
 #include <asm/io.h>
 #include <asm/adb.h>
 #include <asm/cuda.h>
+#include <asm/pgtable.h>
 #include <linux/selection.h>
 #include "pmac-cons.h"
 #include "control.h"
@@ -88,6 +89,10 @@
 static struct control_regs *disp_regs;
 static int control_use_bank2;
 
+static unsigned long frame_buffer_phys;
+static unsigned long disp_regs_phys;
+static unsigned long cmap_regs_phys;
+
 /*
  * Register initialization tables for the control display.
  *
@@ -317,7 +322,7 @@
 		printk(" %x(%x)", dp->addrs[i].address, dp->addrs[i].size);
 	printk(", intrs =");
 	for (i = 0; i < dp->n_intrs; ++i)
-		printk(" %x", dp->intrs[i]);
+		printk(" %x", dp->intrs[i].line);
 	printk("\n");
 #endif
 
@@ -329,12 +334,15 @@
 			/* use the big-endian aperture (??) */
 			addr += 0x800000;
 			/* map at most 8MB for the frame buffer */
-			frame_buffer = ioremap(addr, 0x800000);
+			frame_buffer_phys = addr;
+			frame_buffer = __ioremap(addr, 0x800000, _PAGE_WRITETHRU);
 		} else {
+			disp_regs_phys = addr;
 			disp_regs = ioremap(addr, size);
 		}
 	}
-	cmap_regs = ioremap(0xf301b000, 0x1000);	/* XXX not in prom? */
+	cmap_regs_phys = 0xf301b000;	/* XXX not in prom? */
+	cmap_regs = ioremap(cmap_regs_phys, 0x1000);
 
 	/* Work out which banks of VRAM we have installed. */
 	frame_buffer[0] = 0x5a;
@@ -454,10 +462,10 @@
 	display_info.pitch = line_pitch;
 	display_info.mode = video_mode;
 	strncpy(display_info.name, "control", sizeof(display_info.name));
-	display_info.fb_address = (unsigned long) frame_buffer + init->offset[color_mode];
-	display_info.cmap_adr_address = (unsigned long) &cmap_regs->addr;
-	display_info.cmap_data_address = (unsigned long) &cmap_regs->lut;
-	display_info.disp_reg_address = (unsigned long) &disp_regs;
+	display_info.fb_address = frame_buffer_phys + init->offset[color_mode];
+	display_info.cmap_adr_address = cmap_regs_phys;
+	display_info.cmap_data_address = cmap_regs_phys + 0x30;
+	display_info.disp_reg_address = disp_regs_phys;
 }
 
 int
diff -u --recursive --new-file v2.1.96/linux/drivers/macintosh/imstt.c linux/drivers/macintosh/imstt.c
--- v2.1.96/linux/drivers/macintosh/imstt.c	Sat Aug 16 09:53:08 1997
+++ linux/drivers/macintosh/imstt.c	Tue Apr 14 17:33:59 1998
@@ -2,28 +2,41 @@
  * imstt.c: Console support for PowerMac "imstt" display adaptor.
  *
  * Copyright (C) 1997 Sigurdur Asgeirsson
+ * Modified by Danilo Beuche 1997
  *	
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
  * as published by the Free Software Foundation; either version
  * 2 of the License, or (at your option) any later version.
  */
+#include <linux/module.h>
+
+#include <linux/config.h>
+
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/delay.h>
 #include <linux/string.h>
 #include <linux/vc_ioctl.h>
 #include <linux/pci.h>
-#include <linux/bios32.h>
 #include <linux/nvram.h>
 #include <asm/prom.h>
 #include <asm/io.h>
+#include <asm/pgtable.h>
 #include <asm/pci-bridge.h>
 #include <linux/selection.h>
 #include <linux/vt_kern.h>
 #include "pmac-cons.h"
 #include "imstt.h"
 
+
+enum { 
+  IBMRAMDAC = 0x00,
+  TVPRAMDAC  = 0x01
+};
+
+
+// IMS TWIN TURBO 
 enum
 {
   S1SA		=  0, /* 0x00 */
@@ -79,6 +92,8 @@
 #endif
 };
 
+
+// IBM RAMDAC
 enum
 {
  PADDRW		= 0x00,
@@ -97,12 +112,256 @@
  PC0		= 0x23
 };
 
+// TI TVP 3030 RAMDAC Direct Registers
+enum 
+{
+	TVPADDRW = 0x00, // 0  Palette/Cursor RAM Write Adress/Index
+	TVPPDATA = 0x04, // 1  Palette Data RAM Data
+	TVPPMASK = 0x08, // 2  Pixel Read-Mask
+	TVPPADRR = 0x0c, // 3  Palette/Cursor RAM Read Adress
+	TVPCADRW = 0x10, // 4  Cursor/Overscan Color Write Address
+	TVPCDATA = 0x14, // 5  Cursor/Overscan Color Data
+			 // 6  reserved
+	TVPCADRR = 0x1c, // 7  Cursor/Overscan Color Read Address
+			 // 8  reserved
+	TVPDCCTL = 0x24, // 9  Direct Cursor Control
+	TVPIDATA = 0x28, // 10 Index Data
+	TVPCRDAT = 0x2c, // 11 Cursor RAM Data
+	TVPCXPOL = 0x30, // 12 Cursor-Position X LSB
+	TVPCXPOH = 0x34, // 13 Cursor-Position X MSB
+	TVPCYPOL = 0x38, // 14 Cursor-Position Y LSB
+	TVPCYPOH = 0x3c, // 15 Cursor-Position Y MSB
+};
+
+// TI TVP 3030 RAMDAC Indirect Registers
+enum
+{
+	TVPIRREV = 0x01, // Silicon Revision [RO] 
+	TVPIRICC = 0x06, // Indirect Cursor Control 	(0x00) 
+	TVPIRBRC = 0x07, // Byte Router Control 	(0xe4)
+	TVPIRLAC = 0x0f, // Latch Control 		(0x06)
+	TVPIRTCC = 0x18, // True Color Control  	(0x80)
+	TVPIRMXC = 0x19, // Multiplex Control		(0x98)
+	TVPIRCLS = 0x1a, // Clock Selection		(0x07)
+	TVPIRPPG = 0x1c, // Palette Page		(0x00)
+	TVPIRGEC = 0x1d, // General Control 		(0x00)
+	TVPIRMIC = 0x1e, // Miscellaneous Control	(0x00)
+	TVPIRPLA = 0x2c, // PLL Address			
+	TVPIRPPD = 0x2d, // Pixel Clock PLL Data
+	TVPIRMPD = 0x2e, // Memory Clock PLL Data
+	TVPIRLPD = 0x2f, // Loop Clock PLL Data
+	TVPIRCKL = 0x30, // Color-Key Overlay Low
+	TVPIRCKH = 0x31, // Color-Key Overlay High 
+	TVPIRCRL = 0x32, // Color-Key Red Low
+	TVPIRCRH = 0x33, // Color-Key Red High 
+	TVPIRCGL = 0x34, // Color-Key Green Low
+	TVPIRCGH = 0x35, // Color-Key Green High 
+	TVPIRCBL = 0x36, // Color-Key Blue Low
+	TVPIRCBH = 0x37, // Color-Key Blue High 
+	TVPIRCKC = 0x38, // Color-Key Control 		(0x00)
+	TVPIRMLC = 0x39, // MCLK/Loop Clock Control	(0x18)
+	TVPIRSEN = 0x3a, // Sense Test			(0x00)
+	TVPIRTMD = 0x3b, // Test Mode Data
+	TVPIRRML = 0x3c, // CRC Remainder LSB [RO]
+	TVPIRRMM = 0x3d, // CRC Remainder MSB [RO]
+	TVPIRRMS = 0x3e, // CRC  Bit Select [WO] 
+	TVPIRDID = 0x3f, // Device ID [RO] 		(0x30)
+	TVPIRRES = 0xff, // Software Reset [WO]
+
+};
+
 struct initvalues
 {
   unsigned char	addr, value;
 };
 
-static struct initvalues initregs[] = 
+
+
+// Values which only depend on resolution not on color mode
+struct tt_single_rmodevals
+{
+	unsigned short hes;	
+	unsigned short heb;	
+	unsigned short hsb;	
+	unsigned short ht;	
+	unsigned short ves;	
+	unsigned short veb;	
+	unsigned short vsb;	
+	unsigned short vt;	
+};
+
+struct tvp_single_rmodevals
+{
+	unsigned char pclk_n;
+	unsigned char pclk_m;
+	unsigned char pclk_p;
+};
+
+struct ibm_single_rmodevals
+{
+	unsigned char pclk_m;
+	unsigned char pclk_n;
+	unsigned char pclk_p;
+	unsigned char pclk_c;
+};
+
+// Values which only depend on color mode not on resolution
+struct tvp_single_cmodevals
+{
+	unsigned char tcc; // True Color control
+	unsigned char mxc; // Multiplexer control
+	unsigned char lckl_n;	// N value of LCKL PLL
+};
+
+struct ibm_single_cmodevals
+{
+	unsigned char pformat; // pixel format
+};
+	
+// Values of the tvp which change depending on colormode x resolution
+struct tvp_single_crmodevals
+{
+	unsigned char mlc;	// Memory Loop Config 0x39
+	unsigned char lckl_p;	// P value of LCKL PLL
+};
+
+struct ibm_single_crmodevals
+{
+    // oh nothing changes
+};
+
+// complete configuration for a resolution in all depths 
+// 0 = 8 Bit, 15/16 bit = 1 , 32 Bit = 2 
+struct ims_crmodevals
+{
+	int pitch;
+	struct tt_single_rmodevals tt[2];  // for each ramdac  seperate tt config
+
+	struct tvp_single_rmodevals tvp_clock;  // for each ramdac  seperate clock config
+	struct tvp_single_crmodevals tvp[3]; 	   // for each colormode 
+
+	struct ibm_single_rmodevals ibm_clock;  // for each ramdac  seperate clock config
+//	struct ibm_single_crmodevals ibm[3];	   // for each color mode 
+};
+
+struct ims_modevals
+{
+	int dac;			  // which dac do we have
+	int total_vram;			  // how much vram is on board
+	int sense;			  // what monitor
+	unsigned char*	fb;		  // frame buffer address
+	unsigned char*	fb_phys;	  // frame buffer address
+	unsigned char*  cmap;		  // dac address
+	unsigned char*  cmap_phys;	  // dac address
+	unsigned int*	dc;		  // tt address
+	unsigned int*	dc_phys;	  // tt address
+
+	struct initvalues*     init[2];	  // initial register settings for each ramdac
+
+	struct ims_crmodevals* mode[20];  // for each possible mode
+	
+	struct  tvp_single_cmodevals tvp[3]; // for each color mode
+	
+	struct  ibm_single_cmodevals ibm[3]; // for each color mode
+};
+
+
+struct ims_crmodevals imsmode_6 =
+{
+	640,
+	{
+  		{ 0x08, 0x12, 0x62, 0x6C, 0x0003, 0x002A, 0x020A, 0x020C },
+		{ 0x04, 0x0009, 0x0031, 0x0036, 0x0003, 0x002a, 0x020a, 0x020d },
+	},
+	{	0xef, 0x2e, 0xb2 },
+	{
+		{ 0x39, 0xf3 },
+		{ 0x39, 0xf3 },
+		{ 0x38, 0xf3 }
+	},
+	// IBM CLOCK
+	{       0x78, 0x13, 0x02, 0x02 },
+};
+
+struct ims_crmodevals imsmode_13 =
+{
+	832,
+	{
+  		{ 0x05, 0x20, 0x88, 0x90, 0x0003, 0x0028, 0x0298, 0x029B },
+		{ 0x04, 0x0011, 0x0045, 0x0048, 0x0003, 0x002a, 0x029a, 0x029b},
+	},
+	{	0xfe, 0x3e, 0xf1 },
+	{
+		{ 0x39, 0xf3 },
+		{ 0x38, 0xf3 },
+		{ 0x38, 0xf2 }
+	},
+	{ 0x3E, 0x0A, 0x01, 0x02 }
+};
+struct ims_crmodevals imsmode_17 =
+{
+	1024,
+	{
+  		{ 0x0A, 0x1C, 0x9C, 0xA6, 0x0003, 0x0020, 0x0320, 0x0323 } ,
+		{ 0x06, 0x0210, 0x0250, 0x0053, 0x1003, 0x0021, 0x0321, 0x0324 },
+	},
+	{	0xfc, 0x3a, 0xf1 },
+	{
+		{ 0x39, 0xf3 },
+		{ 0x38, 0xf3 },
+		{ 0x38, 0xf2 }
+	},
+	{ 0x07, 0x00, 0x01, 0x02 }
+};
+struct ims_crmodevals imsmode_18 =
+{
+	1152,
+	{
+		{ 0, 0, 0, 0, 0, 0, 0, 0 },
+  		{ 0x09, 0x0011, 0x059, 0x5b, 0x0003, 0x0031, 0x0397, 0x039a }, 
+	},
+	{	0xfd, 0x3a, 0xf1 },
+	{
+		{ 0x39, 0xf3 },
+		{ 0x38, 0xf3 },
+		{ 0x38, 0xf2 }
+	},
+	{ 0, 0, 0, 0 }
+};
+struct ims_crmodevals imsmode_19 =
+{
+	1280,
+	{
+		{ 0, 0, 0, 0, 0, 0, 0, 0 },
+		{ 0x09, 0x0016, 0x0066, 0x0069, 0x0003, 0x0027, 0x03e7, 0x03e8 },
+	},
+	{	0xf7, 0x36, 0xf0 },
+	{
+		{ 0x38, 0xf3 },
+		{ 0x38, 0xf2 },
+		{ 0x38, 0xf1 }
+	},
+	{ 0, 0, 0, 0 }
+};
+struct ims_crmodevals imsmode_20 =
+{
+	1280,
+	{
+		{ 0, 0, 0, 0, 0, 0, 0, 0 },
+		{ 0x09, 0x0018, 0x0068, 0x006a, 0x0003, 0x0029, 0x0429, 0x042a },
+	},
+	{	0xf0, 0x2d, 0xf0 },
+	{
+		{ 0x38, 0xf3 },
+		{ 0x38, 0xf2 },
+		{ 0x38, 0xf1 }
+	},
+      	{ 0, 0, 0, 0 }
+};
+
+// IBM RAMDAC initial register values
+
+static struct initvalues ibm_initregs[] = 
 {
  { 0x02, 0x21 },	/* (0x01) Miscellaneous Clock Control */
  { 0x03, 0x00 },	/* (0x00) Sync Control */
@@ -129,64 +388,114 @@
  { 0x71, 0x45 },	/* (0x00) Miscellaneous Control 2 */
  { 0x72, 0x00 },	/* (0x00) Miscellaneous Control 3 */
  { 0x78, 0x00 },	/* (0x00) Key Control/DB Operation */
+ { 0x00, 0x00 } 
+};
+
+
+static struct initvalues tvp_initregs[] = 
+{
+{ 0x6, 0x00},
+{ 0x7, 0xe4},
+{ 0xf, 0x06},
+{ 0x18, 0x80},
+{ 0x19, 0x4d},
+{ 0x1a, 0x05},
+{ 0x1c, 0x00},
+{ 0x1d, 0x00},
+{ 0x1e, 0x08},
+{ 0x30, 0xff},
+{ 0x31, 0xff},
+{ 0x32, 0xff},
+{ 0x33, 0xff},
+{ 0x34, 0xff},
+{ 0x35, 0xff},
+{ 0x36, 0xff},
+{ 0x37, 0xff},
+{ 0x38, 0x00},
+{ TVPIRPLA, 0x00 },
+{ TVPIRPPD, 0xc0 },
+{ TVPIRPPD, 0xd5 },
+{ TVPIRPPD, 0xea },
+{ TVPIRPLA, 0x00 },
+{ TVPIRMPD, 0xb9 },
+{ TVPIRMPD, 0x3a },
+{ TVPIRMPD, 0xb1 },
+{ TVPIRPLA, 0x00 },
+{ TVPIRLPD, 0xc1 },
+{ TVPIRLPD, 0x3d },
+{ TVPIRLPD, 0xf3 },
+{ 0x00, 0x00 } 
 };
 
-static void set_imstt_clock(unsigned char *params);
+
+static struct ims_modevals ims_info =
+{
+	-1,	// DAC
+	-1,	// VRAM
+	-1, 	// Monitor;
+	0,	// Framebuffer
+	0,	// Framebuffer_phys
+	0,	// colormap
+	0,	// colormap_phys
+	0,	// dc
+	0,	// dc_phys
+	{ ibm_initregs, tvp_initregs},
+	{
+		NULL,
+		NULL,
+		NULL,
+		NULL,
+		&imsmode_6,
+		&imsmode_6,
+		NULL,
+		NULL,
+		NULL,
+		NULL,
+		NULL,
+		NULL,
+		&imsmode_13,
+		NULL,
+		NULL,
+		NULL,
+		&imsmode_17,
+		&imsmode_18,
+		&imsmode_19,
+		&imsmode_20
+	},
+	{
+		{ 0x80, 0x4d, 0xc1 },
+		{ 0x44, 0x55, 0xe1 },
+		{ 0x46, 0x5d, 0xf1 }
+	},
+	{
+		{ 0x03 },
+		{ 0x04 },
+		{ 0x06 }
+	}	
+};	
+		
+	
+
+
+// static void set_imstt_clock(unsigned char *params);
+static void map_imstt_display(struct device_node *, int);
 static int read_imstt_sense(void);
 static int imstt_vram_reqd(int vmode, int cmode);
 
-static int total_vram = 2 * 1024 * 1024;		/* total amount of video memory, bytes */
-static unsigned char *frame_buffer;
-static unsigned char *cmap_regs;
-static unsigned *dc_regs;
 
+#if 0
+static int get_tvp_ireg(int iaddr)
+{
+	ims_info.cmap[0] = iaddr & 0xff; eieio();
+	return ims_info.cmap[40];
+}
+#endif
 
-/*
- * Register initialization tables for the imstt display.
- *
- * Dot clock rate is 20MHz * (m + 1) / ((n + 1) * (p ? 2 * p : 1)
- * where m = clk[0], n = clk[1], p = clk[2]
- * clk[3] is c, charge pump bias which depends on the VCO frequency  
- */
-struct imstt_regvals {
-	unsigned short		cfg[8];
-	unsigned char		clk[4];
-	unsigned long		pitch[3];
-} imsttmode;
-
-/* Register values for 1024x768, 75Hz mode (17) */
-static struct imstt_regvals imstt_reg_init_17 = {
-  { 0x0A, 0x1C, 0x9C, 0xA6, 0x0003, 0x0020, 0x0320, 0x0323 }, 
-  { 0x07, 0x00, 0x01, 0x02 },
-  { 0x0400, 0x0800, 0x1000 }
-};
-
-/* Register values for 832x624, 75Hz mode (13) */
-static struct imstt_regvals imstt_reg_init_13 = {
-  { 0x05, 0x20, 0x88, 0x90, 0x0003, 0x0028, 0x0298, 0x029B },
-  { 0x3E, 0x0A, 0x01, 0x02 },
-  { 832, 832 * 2, 832 * 4 }
-};
-
-/* Register values for 640x480, 67Hz mode (6) */
-static struct imstt_regvals imstt_reg_init_6 = {
-  { 0x08, 0x12, 0x62, 0x6C, 0x0003, 0x002A, 0x020A, 0x020C },
-  { 0x78, 0x13, 0x02, 0x02 },
-  { 640, 640 * 2, 640 * 4 }
-};
-
-static struct imstt_regvals *imstt_reg_init[20] = {
-	NULL, NULL, NULL, NULL,
-	&imstt_reg_init_6, // fake'm out
-	&imstt_reg_init_6,
-	NULL, NULL, NULL,
-	NULL, NULL, NULL,
-	&imstt_reg_init_13,
-	NULL, NULL, NULL,
-	&imstt_reg_init_17,
-	NULL, NULL, NULL
-};
-
+static void set_tvp_ireg(int iaddr,unsigned char value)
+{
+	ims_info.cmap[0] = iaddr & 0xff; eieio();
+	ims_info.cmap[40] = value; eieio();
+}
 /*
  * Get the monitor sense value.
  * Note that this can be called before calibrate_delay,
@@ -199,28 +508,28 @@
 	int sense;
 	unsigned gio, gioe;
 
-	gio = ld_le32(dc_regs + GIO) & ~0x0038;
+	gio = ld_le32(ims_info.dc + GIO) & ~0x0038;
 	gioe = ld_le32(dc_
 	
-	out_le32(dc_regs + GIOE, reg);	/* drive all lines high */
+	out_le32(ims_info.dc + GIOE, reg);	/* drive all lines high */
 	__delay(200);
-	out_le32(dc_regs + GIOE, 077);	/* turn off drivers */
+	out_le32(ims_info.dc + GIOE, 077);	/* turn off drivers */
 	__delay(2000);
-	sense = (in_le32(dc_regs + GIOE) & 0x1c0) << 2;
+	sense = (in_le32(ims_info.dc + GIOE) & 0x1c0) << 2;
 
 	/* drive each sense line low in turn and collect the other 2 */
-	out_le32(dc_regs + GIOE, 033);	/* drive A low */
+	out_le32(ims_info.dc + GIOE, 033);	/* drive A low */
 	__delay(2000);
-	sense |= (in_le32(dc_regs + GIOE) & 0xc0) >> 2;
-	out_le32(dc_regs + GIOE, 055);	/* drive B low */
+	sense |= (in_le32(ims_info.dc + GIOE) & 0xc0) >> 2;
+	out_le32(ims_info.dc + GIOE, 055);	/* drive B low */
 	__delay(2000);
-	sense |= ((in_le32(dc_regs + GIOE) & 0x100) >> 5)
-		| ((in_le32(dc_regs + GIOE) & 0x40) >> 4);
-	out_le32(dc_regs + GIOE, 066);	/* drive C low */
+	sense |= ((in_le32(ims_info.dc + GIOE) & 0x100) >> 5)
+		| ((in_le32(ims_info.dc + GIOE) & 0x40) >> 4);
+	out_le32(ims_info.dc + GIOE, 066);	/* drive C low */
 	__delay(2000);
-	sense |= (in_le32(dc_regs + GIOE) & 0x180) >> 7;
+	sense |= (in_le32(ims_info.dc + GIOE) & 0x180) >> 7;
 
-	out_le32(dc_regs + GIOE, 077);	/* turn off drivers */
+	out_le32(ims_info.dc + GIOE, 077);	/* turn off drivers */
 	return sense;
 #else
 	return 0;
@@ -229,12 +538,24 @@
 
 static inline int imstt_vram_reqd(int vmode, int cmode)
 {
-	return vmode_attrs[vmode-1].vres
-		* imstt_reg_init[vmode-1]->pitch[cmode];
+	return vmode_attrs[vmode-1].vres *
+		(ims_info.mode[vmode-1])->pitch * ( 1 << cmode);
+}
+
+void
+map_imstt_display_tvp(struct device_node *dp)
+{
+    map_imstt_display(dp,1);
 }
 
 void
-map_imstt_display(struct device_node *dp)
+map_imstt_display_ibm(struct device_node *dp)
+{
+    map_imstt_display(dp,0);
+}
+
+static void
+map_imstt_display(struct device_node *dp, int which)
 {
 	int i, sense;
 	unsigned long addr, size, tmp;
@@ -244,7 +565,7 @@
 	if (dp->next != 0)
 		printk("Warning: only using first imstt display device\n");
 
-#if 1
+#if 0
 	printk("pmac_display_init: node = %p, addrs =", dp->node);
 	for (i = 0; i < dp->n_addrs; ++i)
 		printk(" %x(%x)", dp->addrs[i].address, dp->addrs[i].size);
@@ -259,12 +580,14 @@
 		addr = dp->addrs[i].address;
 		size = dp->addrs[i].size;
 		if (size >= 0x02000000) {
-			frame_buffer = ioremap(addr, size);
-			dc_regs = (unsigned*)(frame_buffer + 0x00800000);
-			cmap_regs = (unsigned char*)(frame_buffer + 0x00840000);
-
-			printk("mapped frame_buffer=%x(%x)", (unsigned)frame_buffer, (unsigned)size);
-			printk(" dc_regs=%x, cmap_regs=%x\n", (unsigned)dc_regs, (unsigned)cmap_regs);
+			ims_info.fb = __ioremap(addr, size, _PAGE_NO_CACHE);
+			ims_info.fb_phys = (unsigned char*)addr;
+			ims_info.dc = (unsigned*)(ims_info.fb + 0x00800000);
+			ims_info.dc_phys = (unsigned*)(ims_info.fb_phys + 0x00800000);
+			ims_info.cmap = (unsigned char*)(ims_info.fb + 0x00840000);
+			ims_info.cmap_phys = (unsigned char*)(ims_info.fb_phys + 0x00840000);
+			printk("mapped ims_info.fb=%x(%x)", (unsigned)ims_info.fb, (unsigned)size);
+			printk(" ims_info.dc=%x, ims_info.cmap=%x\n", (unsigned)ims_info.dc, (unsigned)ims_info.cmap);
 		}
 	}
 
@@ -282,12 +605,28 @@
 	else
 	  printk("unable to find pci device\n");
 
-	tmp = in_le32(dc_regs + SSTATUS);
+	tmp = in_le32(ims_info.dc + SSTATUS);
 	printk("chip version %ld, ", (tmp & 0x0F00) >> 8);
 
-	tmp = in_le32(dc_regs + PRC);
-	total_vram = (tmp & 0x0004) ? 0x000400000L : 0x000200000L;
-	printk("VRAM size %ldM\n", total_vram / 0x000100000L);
+	tmp = in_le32(ims_info.dc + PRC);
+	
+	if (0 == which )
+	    ims_info.total_vram = (tmp & 0x0004) ? 0x000400000L : 0x000200000L;
+	else
+	    ims_info.total_vram = 0x000800000L;
+	
+	printk("VRAM size %ldM\n", ims_info.total_vram / 0x000100000L);
+	
+	if (ims_info.total_vram == 0x000800000L)
+	{
+		ims_info.dac = TVPRAMDAC;
+		printk("Selecting TVP 3030 RAMDAC\n");
+	}
+	else
+	{
+		ims_info.dac = IBMRAMDAC;
+		printk("Selecting  IBM RAMDAC\n");
+	}
 
 	sense = read_imstt_sense();
 	printk("Monitor sense value = 0x%x, ", sense);
@@ -311,147 +650,232 @@
 	if (color_mode < CMODE_8 || color_mode > CMODE_32)
 		color_mode = CMODE_8;
 	while (color_mode > CMODE_8
-	       && imstt_vram_reqd(video_mode, color_mode) > total_vram)
+	       && imstt_vram_reqd(video_mode, color_mode) > ims_info.total_vram)
 		--color_mode;
 
 #endif
-
+	// Hack Hack Hack !!!
 	video_mode = VMODE_640_480_67;
 	color_mode = CMODE_8;
 }
 
+/*
+ * We dont need it ( all is done in ims_init )
 static void
-set_imstt_clock(unsigned char *params)
+set_imstt_clock_tvp(char* tvprv)
 {
-  cmap_regs[PIDXHI] = 0; eieio();
-  cmap_regs[PIDXLO] = PM0; eieio();
-  cmap_regs[PIDXDATA] = params[0]; eieio();
+  int j;
+  for (j=0;j<3;j++)
+	{
+  		set_tvp_ireg(TVPIRPLA,(j << 4) | (j << 2) | j); // Select same value for all plls
+		set_tvp_ireg(TVPIRPPD,tvprv[j]);
+		set_tvp_ireg(TVPIRMPD,tvprv[3+j]);
+		set_tvp_ireg(TVPIRLPD,tvprv[6+j]);
+	}
+}
 
-  cmap_regs[PIDXLO] = PN0; eieio();
-  cmap_regs[PIDXDATA] = params[1]; eieio();
+static void
+set_imstt_clock_ibm(unsigned char *params)
+{
+  ims_info.cmap[PIDXHI] = 0; eieio();
+  ims_info.cmap[PIDXLO] = PM0; eieio();
+  ims_info.cmap[PIDXDATA] = params[0]; eieio();
+
+  ims_info.cmap[PIDXLO] = PN0; eieio();
+  ims_info.cmap[PIDXDATA] = params[1]; eieio();
 
-  cmap_regs[PIDXLO] = PP0; eieio();
-  cmap_regs[PIDXDATA] = params[2]; eieio();
+  ims_info.cmap[PIDXLO] = PP0; eieio();
+  ims_info.cmap[PIDXDATA] = params[2]; eieio();
 	
-  cmap_regs[PIDXLO] = PC0; eieio();
-  cmap_regs[PIDXDATA] = params[3]; eieio();
+  ims_info.cmap[PIDXLO] = PC0; eieio();
+  ims_info.cmap[PIDXDATA] = params[3]; eieio();
 }
+*/
 
 void
 imstt_init()
 {
   int	                 i, yoff, hres;
-  unsigned long	         ctl, pitch, tmp;
-  unsigned char	         pformat;
-  unsigned               *p;
-  struct imstt_regvals   *init;
-
-  if (video_mode <= 0 || video_mode > VMODE_MAX
-      || (init = imstt_reg_init[video_mode-1]) == 0)
-    panic("imstt: display mode %d not supported", video_mode);
+  unsigned long	         ctl, pitch, tmp, scrCmode;
+  struct ims_crmodevals   *init;
+
+  if (video_mode <= 0 || video_mode > VMODE_MAX ) panic("imstt: display mode %d not supported(not in valid range)", video_mode);
+  if ((init = ims_info.mode[video_mode-1]) == 0) panic("imstt: display mode %d not supported(no mode definition)", video_mode);
+  if (init->tt[ims_info.dac].vt == 0) panic("imstt: display mode %d not supported (no timing definition)", video_mode);
+	
 
   n_scanlines = vmode_attrs[video_mode-1].vres;
   hres = vmode_attrs[video_mode-1].hres;
   pixel_size = 1 << color_mode;
-  line_pitch = init->pitch[color_mode];
+  line_pitch = init->pitch * pixel_size;
   row_pitch = line_pitch * 16;
 
   /* initialize the card */
-  tmp = in_le32(dc_regs + STGCTL);
-  out_le32(dc_regs + STGCTL, tmp & ~0x1);
+  tmp = in_le32(ims_info.dc + STGCTL);
+  out_le32(ims_info.dc + STGCTL, tmp & ~0x1);
 #if 0
-  out_le32(dc_regs + SCR, 0);
+  out_le32(ims_info.dc + SCR, 0);
 #endif
 
-  cmap_regs[PPMASK] = 0xFF;
-  /* set default values for DAC registers */ 
-  cmap_regs[PIDXHI] = 0; eieio();
-  for(i = 0; i < sizeof(initregs) / sizeof(*initregs); i++) {
-    cmap_regs[PIDXLO] = initregs[i].addr; eieio();
-    cmap_regs[PIDXDATA] = initregs[i].value; eieio();
+  switch(ims_info.dac)
+  {
+  case IBMRAMDAC:
+      ims_info.cmap[PPMASK] = 0xFF; eieio();
+      ims_info.cmap[PIDXHI] = 0x00; eieio();
+      for (i = 0; ims_info.init[IBMRAMDAC][i].addr != 0 && ims_info.init[IBMRAMDAC][i].value != 0 ;i++)
+	  {
+	      ims_info.cmap[PIDXLO] = ims_info.init[IBMRAMDAC][i].addr; eieio();
+	      ims_info.cmap[PIDXDATA] = ims_info.init[IBMRAMDAC][i].value; eieio();
+	  }
+
+      ims_info.cmap[PIDXHI] = 0; eieio();
+      ims_info.cmap[PIDXLO] = PM0; eieio();
+      ims_info.cmap[PIDXDATA] = init->ibm_clock.pclk_m; eieio();
+
+      ims_info.cmap[PIDXLO] = PN0; eieio();
+      ims_info.cmap[PIDXDATA] = init->ibm_clock.pclk_n; eieio();
+
+      ims_info.cmap[PIDXLO] = PP0; eieio();
+      ims_info.cmap[PIDXDATA] = init->ibm_clock.pclk_p; eieio();
+	
+      ims_info.cmap[PIDXLO] = PC0; eieio();
+      ims_info.cmap[PIDXDATA] = init->ibm_clock.pclk_c; eieio();
+      
+      ims_info.cmap[PIDXLO] = PPIXREP; eieio();
+      ims_info.cmap[PIDXDATA] = ims_info.ibm[color_mode].pformat; eieio();
+
+      break;
+  case TVPRAMDAC:
+	for (i = 0; ims_info.init[TVPRAMDAC][i].addr != 0 && ims_info.init[TVPRAMDAC][i].value != 0 ;i++)
+	{
+		set_tvp_ireg(ims_info.init[TVPRAMDAC][i].addr,ims_info.init[TVPRAMDAC][i].value);	
+	}
+    	set_tvp_ireg(TVPIRPLA,0x00);
+    	set_tvp_ireg(TVPIRPPD,init->tvp_clock.pclk_n);
+    	set_tvp_ireg(TVPIRPPD,init->tvp_clock.pclk_m);
+    	set_tvp_ireg(TVPIRPPD,init->tvp_clock.pclk_p);
+
+    	set_tvp_ireg(TVPIRTCC,ims_info.tvp[color_mode].tcc);
+    	set_tvp_ireg(TVPIRMXC,ims_info.tvp[color_mode].mxc);
+
+    	set_tvp_ireg(TVPIRPLA,0x00);
+    	set_tvp_ireg(TVPIRLPD,ims_info.tvp[color_mode].lckl_n);
+
+    	set_tvp_ireg(TVPIRPLA,0x15);
+    	set_tvp_ireg(TVPIRMLC,(init->tvp[color_mode]).mlc);
+
+    	set_tvp_ireg(TVPIRPLA,0x2a);
+    	set_tvp_ireg(TVPIRLPD,init->tvp[color_mode].lckl_p);
+	break;
   }
-  set_imstt_clock(init->clk);
+
 
   switch(color_mode) {
   case CMODE_32:
-    ctl = 0x17b5;
-    pitch = init->pitch[2] / 4;
-    pformat = 0x06;
+    ctl = 0x1785;
+    pitch = init->pitch;
+    scrCmode = 0x300;
     break;
   case CMODE_16:
-    ctl = 0x17b3;
-    pitch = init->pitch[1] / 4;
-    pformat = 0x04;
+    ctl = 0x1783;
+    pitch = init->pitch / 2;
+    scrCmode = 0x100;
     break;
   case CMODE_8:
   default:
-    ctl = 0x17b1;
-    pitch = init->pitch[0] / 4;
-    pformat = 0x03;
+    ctl = 0x1781;
+    pitch = init->pitch / 4;
+    scrCmode = 0x000;
     break;
   }
 
-  out_le32(&dc_regs[HES], init->cfg[0]);
-  out_le32(&dc_regs[HEB], init->cfg[1]);
-  out_le32(&dc_regs[HSB], init->cfg[2]);
-  out_le32(&dc_regs[HT], init->cfg[3]);
-  out_le32(&dc_regs[VES], init->cfg[4]);
-  out_le32(&dc_regs[VEB], init->cfg[5]);
-  out_le32(&dc_regs[VSB], init->cfg[6]);
-  out_le32(&dc_regs[VT], init->cfg[7]);
-  out_le32(&dc_regs[HCIV], 1);
-  out_le32(&dc_regs[VCIV], 1);
-  out_le32(&dc_regs[TCDR], 4);
-  out_le32(&dc_regs[VIL], 0);
-	
-  out_le32(&dc_regs[SSR], 0);
-  out_le32(&dc_regs[HRIR], 0x0200);
-  out_le32(&dc_regs[CMR], 0x01FF);
-  out_le32(&dc_regs[SRGCTL], 0x0003);
-  if(total_vram == 0x000200000)
-     out_le32(&dc_regs[SCR], 0x0059D);
-  else {
-    pitch /= 2;
-    out_le32(&dc_regs[SCR], 0x00D0DC);
+  out_le32(&ims_info.dc[HES], init->tt[ims_info.dac].hes);
+  out_le32(&ims_info.dc[HEB], init->tt[ims_info.dac].heb);
+  out_le32(&ims_info.dc[HSB], init->tt[ims_info.dac].hsb);
+  out_le32(&ims_info.dc[HT], init->tt[ims_info.dac].ht);
+  out_le32(&ims_info.dc[VES], init->tt[ims_info.dac].ves);
+  out_le32(&ims_info.dc[VEB], init->tt[ims_info.dac].veb);
+  out_le32(&ims_info.dc[VSB], init->tt[ims_info.dac].vsb);
+  out_le32(&ims_info.dc[VT], init->tt[ims_info.dac].vt);
+  out_le32(&ims_info.dc[HCIV], 1);
+  out_le32(&ims_info.dc[VCIV], 1);
+  out_le32(&ims_info.dc[TCDR], 4);
+  out_le32(&ims_info.dc[VIL], 0);
+	
+  out_le32(&ims_info.dc[SSR], 0);
+  out_le32(&ims_info.dc[HRIR], 0x0200);
+  out_le32(&ims_info.dc[CMR], 0x01FF);
+  out_le32(&ims_info.dc[SRGCTL], 0x0003);
+  switch(ims_info.total_vram)
+  {
+	case 0x000200000:
+     		out_le32(&ims_info.dc[SCR], 0x0059D| scrCmode);
+		break;
+	case 0x000400000:
+    		pitch /= 2;
+    		out_le32(&ims_info.dc[SCR], 0x00D0DC | scrCmode);
+		break;
+	case 0x000800000:
+    		pitch /= 2;
+    		out_le32(&ims_info.dc[SCR], 0x0150DD | scrCmode);
+		break;
   }
 
-  out_le32(&dc_regs[SPR], pitch);
-
-  cmap_regs[PIDXLO] = PPIXREP; eieio();
-  cmap_regs[PIDXDATA] = pformat; eieio();
+  out_le32(&ims_info.dc[SPR], pitch);
 
+  if (ims_info.dac == IBMRAMDAC)
+  {
+	  
 
+  }
+  
   pmac_init_palette();	/* Initialize colormap */
 
-  out_le32(&dc_regs[STGCTL], ctl);
+  out_le32(&ims_info.dc[STGCTL], ctl);
 
   yoff = (n_scanlines % 16) / 2;
-  fb_start = frame_buffer + yoff * line_pitch;
+  fb_start = ims_info.fb + yoff * line_pitch;
 
   /* Clear screen */
-  p = (unsigned *)frame_buffer;
-  for (i = n_scanlines * line_pitch / sizeof(unsigned); i != 0; --i)
-    *p++ = 0;
+  {
+	unsigned long *p;
+  	p = (unsigned long*)ims_info.fb;
+  	for (i = n_scanlines * line_pitch / sizeof(unsigned); i != 0; --i)
+    		*p++ = 0;
+  }
 
   display_info.height = n_scanlines;
   display_info.width = hres;
   display_info.depth = pixel_size * 8;
   display_info.pitch = line_pitch;
   display_info.mode = video_mode;
-  strncpy(display_info.name, "IMS,tt128mb", sizeof(display_info.name));
-  display_info.fb_address = (unsigned long) frame_buffer;
-  display_info.cmap_adr_address = (unsigned long) &cmap_regs[PADDRW];
-  display_info.cmap_data_address = (unsigned long) &cmap_regs[PDATA];
+
+  if (ims_info.dac == IBMRAMDAC )
+      strncpy(display_info.name, "IMS,tt128mb2/4", sizeof(display_info.name));
+  else
+      strncpy(display_info.name, "IMS,tt128mb8/8A", sizeof(display_info.name));
+  
+  display_info.fb_address = (unsigned long) ims_info.fb_phys;
+  display_info.cmap_adr_address = (unsigned long) &ims_info.cmap_phys[PADDRW];
+  display_info.cmap_data_address = (unsigned long) &ims_info.cmap_phys[PDATA];
   display_info.disp_reg_address = (unsigned long) NULL;
 }
 
 int
 imstt_setmode(struct vc_mode *mode, int doit)
 {
-	int cmode;
+  int cmode;
+  struct ims_crmodevals   *init;
+
+  if (video_mode <= 0 || video_mode > VMODE_MAX )
+		return -EINVAL;
+  if ((init = ims_info.mode[video_mode-1]) == 0)
+		return -EINVAL;
+  if (init->tt[ims_info.dac].vt == 0)
+		return -EINVAL;
 
 	if (mode->mode <= 0 || mode->mode > VMODE_MAX
-	    || imstt_reg_init[mode->mode-1] == 0)
+	    || (ims_info.mode[mode->mode-1] == 0))
 		return -EINVAL;
 	switch (mode->depth) {
 	case 24:
@@ -468,7 +892,7 @@
 	default:
 		return -EINVAL;
 	}
-	if (imstt_vram_reqd(mode->mode, cmode) > total_vram)
+	if (imstt_vram_reqd(mode->mode, cmode) > ims_info.total_vram)
 		return -EINVAL;
 	if (doit) {
 		video_mode = mode->mode;
@@ -478,17 +902,32 @@
 	return 0;
 }
 
+// set palette for TI TVP3030 ramdac (used on 8MB version)
 void
-imstt_set_palette(unsigned char red[], unsigned char green[],
+imstt_set_palette_tvp(unsigned char red[], unsigned char green[],
+		    unsigned char blue[], int index, int ncolors)
+{
+	int i;
+	for (i = 0; i < ncolors; ++i) {
+		ims_info.cmap[TVPADDRW] = index + i;	eieio();
+		ims_info.cmap[TVPPDATA] = red[i];	eieio();
+		ims_info.cmap[TVPPDATA] = green[i];	eieio();
+		ims_info.cmap[TVPPDATA] = blue[i];	eieio();
+	}
+}
+
+// set palette for IBM ramdac (used on 2MB/4MB version)
+void
+imstt_set_palette_ibm(unsigned char red[], unsigned char green[],
 		    unsigned char blue[], int index, int ncolors)
 {
 	int i;
 
 	for (i = 0; i < ncolors; ++i) {
-		cmap_regs[PADDRW] = index + i;	eieio();
-		cmap_regs[PDATA] = red[i];	eieio();
-		cmap_regs[PDATA] = green[i];	eieio();
-		cmap_regs[PDATA] = blue[i];	eieio();
+		ims_info.cmap[PADDRW] = index + i;	eieio();
+		ims_info.cmap[PDATA] = red[i];	eieio();
+		ims_info.cmap[PDATA] = green[i];	eieio();
+		ims_info.cmap[PDATA] = blue[i];	eieio();
 	}
 }
 
@@ -497,10 +936,12 @@
 {
 	long ctrl;
 
-	ctrl = ld_le32(dc_regs + STGCTL) | 0x0030;
+	ctrl = ld_le32(ims_info.dc + STGCTL) | 0x0030;
 	if (blank_mode & VESA_VSYNC_SUSPEND)
 		ctrl &= ~0x0020;
 	if (blank_mode & VESA_HSYNC_SUSPEND)
 		ctrl &= ~0x0010;
-	out_le32(dc_regs + STGCTL, ctrl);
+	out_le32(ims_info.dc + STGCTL, ctrl);
 }
+
+
diff -u --recursive --new-file v2.1.96/linux/drivers/macintosh/imstt.h linux/drivers/macintosh/imstt.h
--- v2.1.96/linux/drivers/macintosh/imstt.h	Sat Aug 16 09:53:08 1997
+++ linux/drivers/macintosh/imstt.h	Tue Apr 14 17:33:59 1998
@@ -9,10 +9,13 @@
  * 2 of the License, or (at your option) any later version.
  */
 
-extern void map_imstt_display(struct device_node *);
+extern void map_imstt_display_ibm(struct device_node *);
+extern void map_imstt_display_tvp(struct device_node *);
 extern void imstt_init(void);
 extern int imstt_setmode(struct vc_mode *mode, int doit);
-extern void imstt_set_palette(unsigned char red[], unsigned char green[],
+extern void imstt_set_palette_ibm(unsigned char red[], unsigned char green[],
+				unsigned char blue[], int index, int ncolors);
+extern void imstt_set_palette_tvp(unsigned char red[], unsigned char green[],
 				unsigned char blue[], int index, int ncolors);
 extern void imstt_set_blanking(int blank_mode);
 
diff -u --recursive --new-file v2.1.96/linux/drivers/macintosh/mac_keyb.c linux/drivers/macintosh/mac_keyb.c
--- v2.1.96/linux/drivers/macintosh/mac_keyb.c	Mon Jan 12 15:18:13 1998
+++ linux/drivers/macintosh/mac_keyb.c	Tue Apr 14 17:33:59 1998
@@ -15,8 +15,8 @@
 #include <linux/mm.h>
 #include <linux/signal.h>
 #include <linux/ioport.h>
+#include <linux/init.h>
 
-#include <asm/keyboard.h>
 #include <asm/bitops.h>
 #include <asm/adb.h>
 #include <asm/cuda.h>
@@ -28,6 +28,140 @@
 #define KEYB_LEDREG	2	/* register # for leds on ADB keyboard */
 #define MOUSE_DATAREG	0	/* reg# for movement/button codes from mouse */
 
+static u_short macplain_map[NR_KEYS] = __initdata {
+	0xfb61,	0xfb73,	0xfb64,	0xfb66,	0xfb68,	0xfb67,	0xfb7a,	0xfb78,
+	0xfb63,	0xfb76,	0xf200,	0xfb62,	0xfb71,	0xfb77,	0xfb65,	0xfb72,
+	0xfb79,	0xfb74,	0xf031,	0xf032,	0xf033,	0xf034,	0xf036,	0xf035,
+	0xf03d,	0xf039,	0xf037,	0xf02d,	0xf038,	0xf030,	0xf05d,	0xfb6f,
+	0xfb75,	0xf05b,	0xfb69,	0xfb70,	0xf201,	0xfb6c,	0xfb6a,	0xf027,
+	0xfb6b,	0xf03b,	0xf05c,	0xf02c,	0xf02f,	0xfb6e,	0xfb6d,	0xf02e,
+	0xf009,	0xf020,	0xf060,	0xf07f,	0xf200,	0xf01b,	0xf702,	0xf703,
+	0xf700,	0xf207,	0xf701,	0xf601,	0xf602,	0xf600,	0xf603,	0xf200,
+	0xf200,	0xf310,	0xf200,	0xf30c,	0xf200,	0xf30a,	0xf200,	0xf208,
+	0xf200,	0xf200,	0xf200,	0xf30d,	0xf30e,	0xf200,	0xf30b,	0xf200,
+	0xf200,	0xf200,	0xf300,	0xf301,	0xf302,	0xf303,	0xf304,	0xf305,
+	0xf306,	0xf307,	0xf200,	0xf308,	0xf309,	0xf200,	0xf200,	0xf200,
+	0xf104,	0xf105,	0xf106,	0xf102,	0xf107,	0xf108,	0xf200,	0xf10a,
+	0xf200,	0xf10c,	0xf200,	0xf209,	0xf200,	0xf109,	0xf200,	0xf10b,
+	0xf200,	0xf11d,	0xf115,	0xf114,	0xf118,	0xf116,	0xf103,	0xf117,
+	0xf101,	0xf119,	0xf100,	0xf700,	0xf701,	0xf702,	0xf200,	0xf200,
+};
+
+static u_short macshift_map[NR_KEYS] __initdata = {
+	0xfb41,	0xfb53,	0xfb44,	0xfb46,	0xfb48,	0xfb47,	0xfb5a,	0xfb58,
+	0xfb43,	0xfb56,	0xf200,	0xfb42,	0xfb51,	0xfb57,	0xfb45,	0xfb52,
+	0xfb59,	0xfb54,	0xf021,	0xf040,	0xf023,	0xf024,	0xf05e,	0xf025,
+	0xf02b,	0xf028,	0xf026,	0xf05f,	0xf02a,	0xf029,	0xf07d,	0xfb4f,
+	0xfb55,	0xf07b,	0xfb49,	0xfb50,	0xf201,	0xfb4c,	0xfb4a,	0xf022,
+	0xfb4b,	0xf03a,	0xf07c,	0xf03c,	0xf03f,	0xfb4e,	0xfb4d,	0xf03e,
+	0xf009,	0xf020,	0xf07e,	0xf07f,	0xf200,	0xf01b,	0xf702,	0xf703,
+	0xf700,	0xf207,	0xf701,	0xf601,	0xf602,	0xf600,	0xf603,	0xf200,
+	0xf200,	0xf310,	0xf200,	0xf30c,	0xf200,	0xf30a,	0xf200,	0xf208,
+	0xf200,	0xf200,	0xf200,	0xf30d,	0xf30e,	0xf200,	0xf30b,	0xf200,
+	0xf200,	0xf200,	0xf300,	0xf301,	0xf302,	0xf303,	0xf304,	0xf305,
+	0xf306,	0xf307,	0xf200,	0xf308,	0xf309,	0xf200,	0xf200,	0xf200,
+	0xf10e,	0xf10f,	0xf110,	0xf10c,	0xf111,	0xf112,	0xf200,	0xf10a,
+	0xf200,	0xf10c,	0xf200,	0xf203,	0xf200,	0xf113,	0xf200,	0xf10b,
+	0xf200,	0xf11d,	0xf115,	0xf114,	0xf20b,	0xf116,	0xf10d,	0xf117,
+	0xf10b,	0xf20a,	0xf10a,	0xf700,	0xf701,	0xf702,	0xf200,	0xf200,
+};
+
+static u_short macaltgr_map[NR_KEYS] __initdata = {
+	0xf914,	0xfb73,	0xf917,	0xf919,	0xfb68,	0xfb67,	0xfb7a,	0xfb78,
+	0xf916,	0xfb76,	0xf200,	0xf915,	0xfb71,	0xfb77,	0xf918,	0xfb72,
+	0xfb79,	0xfb74,	0xf200,	0xf040,	0xf200,	0xf024,	0xf200,	0xf200,
+	0xf200,	0xf05d,	0xf07b,	0xf05c,	0xf05b,	0xf07d,	0xf07e,	0xfb6f,
+	0xfb75,	0xf200,	0xfb69,	0xfb70,	0xf201,	0xfb6c,	0xfb6a,	0xf200,
+	0xfb6b,	0xf200,	0xf200,	0xf200,	0xf200,	0xfb6e,	0xfb6d,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf702,	0xf703,
+	0xf700,	0xf207,	0xf701,	0xf601,	0xf602,	0xf600,	0xf603,	0xf200,
+	0xf200,	0xf310,	0xf200,	0xf30c,	0xf200,	0xf30a,	0xf200,	0xf208,
+	0xf200,	0xf200,	0xf200,	0xf30d,	0xf30e,	0xf200,	0xf30b,	0xf200,
+	0xf200,	0xf200,	0xf90a,	0xf90b,	0xf90c,	0xf90d,	0xf90e,	0xf90f,
+	0xf910,	0xf911,	0xf200,	0xf912,	0xf913,	0xf200,	0xf200,	0xf200,
+	0xf510,	0xf511,	0xf512,	0xf50e,	0xf513,	0xf514,	0xf200,	0xf516,
+	0xf200,	0xf10c,	0xf200,	0xf202,	0xf200,	0xf515,	0xf200,	0xf517,
+	0xf200,	0xf11d,	0xf115,	0xf114,	0xf118,	0xf116,	0xf50f,	0xf117,
+	0xf50d,	0xf119,	0xf50c,	0xf700,	0xf701,	0xf702,	0xf200,	0xf200,
+};
+
+static u_short macctrl_map[NR_KEYS] __initdata = {
+	0xf001,	0xf013,	0xf004,	0xf006,	0xf008,	0xf007,	0xf01a,	0xf018,
+	0xf003,	0xf016,	0xf200,	0xf002,	0xf011,	0xf017,	0xf005,	0xf012,
+	0xf019,	0xf014,	0xf200,	0xf000,	0xf01b,	0xf01c,	0xf01e,	0xf01d,
+	0xf200,	0xf200,	0xf01f,	0xf01f,	0xf07f,	0xf200,	0xf01d,	0xf00f,
+	0xf015,	0xf01b,	0xf009,	0xf010,	0xf201,	0xf00c,	0xf00a,	0xf007,
+	0xf00b,	0xf200,	0xf01c,	0xf200,	0xf07f,	0xf00e,	0xf00d,	0xf20e,
+	0xf200,	0xf000,	0xf000,	0xf008,	0xf200,	0xf200,	0xf702,	0xf703,
+	0xf700,	0xf207,	0xf701,	0xf601,	0xf602,	0xf600,	0xf603,	0xf200,
+	0xf200,	0xf310,	0xf200,	0xf30c,	0xf200,	0xf30a,	0xf200,	0xf208,
+	0xf200,	0xf200,	0xf200,	0xf30d,	0xf30e,	0xf200,	0xf30b,	0xf200,
+	0xf200,	0xf200,	0xf300,	0xf301,	0xf302,	0xf303,	0xf304,	0xf305,
+	0xf306,	0xf307,	0xf200,	0xf308,	0xf309,	0xf200,	0xf200,	0xf200,
+	0xf104,	0xf105,	0xf106,	0xf102,	0xf107,	0xf108,	0xf200,	0xf10a,
+	0xf200,	0xf10c,	0xf200,	0xf204,	0xf200,	0xf109,	0xf200,	0xf10b,
+	0xf200,	0xf11d,	0xf115,	0xf114,	0xf118,	0xf116,	0xf103,	0xf117,
+	0xf101,	0xf119,	0xf100,	0xf700,	0xf701,	0xf702,	0xf200,	0xf200,
+};
+
+static u_short macshift_ctrl_map[NR_KEYS] __initdata = {
+	0xf001,	0xf013,	0xf004,	0xf006,	0xf008,	0xf007,	0xf01a,	0xf018,
+	0xf003,	0xf016,	0xf200,	0xf002,	0xf011,	0xf017,	0xf005,	0xf012,
+	0xf019,	0xf014,	0xf200,	0xf000,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xf01f,	0xf200,	0xf200,	0xf200,	0xf00f,
+	0xf015,	0xf200,	0xf009,	0xf010,	0xf201,	0xf00c,	0xf00a,	0xf200,
+	0xf00b,	0xf200,	0xf200,	0xf200,	0xf200,	0xf00e,	0xf00d,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf702,	0xf703,
+	0xf700,	0xf207,	0xf701,	0xf601,	0xf602,	0xf600,	0xf603,	0xf200,
+	0xf200,	0xf310,	0xf200,	0xf30c,	0xf200,	0xf30a,	0xf200,	0xf208,
+	0xf200,	0xf200,	0xf200,	0xf30d,	0xf30e,	0xf200,	0xf30b,	0xf200,
+	0xf200,	0xf200,	0xf300,	0xf301,	0xf302,	0xf303,	0xf304,	0xf305,
+	0xf306,	0xf307,	0xf200,	0xf308,	0xf309,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf10c,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf11d,	0xf115,	0xf114,	0xf118,	0xf116,	0xf200,	0xf117,
+	0xf200,	0xf119,	0xf200,	0xf700,	0xf701,	0xf702,	0xf200,	0xf20c,
+};
+
+static u_short macalt_map[NR_KEYS] __initdata = {
+	0xf861,	0xf873,	0xf864,	0xf866,	0xf868,	0xf867,	0xf87a,	0xf878,
+	0xf863,	0xf876,	0xf200,	0xf862,	0xf871,	0xf877,	0xf865,	0xf872,
+	0xf879,	0xf874,	0xf831,	0xf832,	0xf833,	0xf834,	0xf836,	0xf835,
+	0xf83d,	0xf839,	0xf837,	0xf82d,	0xf838,	0xf830,	0xf85d,	0xf86f,
+	0xf875,	0xf85b,	0xf869,	0xf870,	0xf80d,	0xf86c,	0xf86a,	0xf827,
+	0xf86b,	0xf83b,	0xf85c,	0xf82c,	0xf82f,	0xf86e,	0xf86d,	0xf82e,
+	0xf809,	0xf820,	0xf860,	0xf87f,	0xf200,	0xf81b,	0xf702,	0xf703,
+	0xf700,	0xf207,	0xf701,	0xf210,	0xf211,	0xf600,	0xf603,	0xf200,
+	0xf200,	0xf310,	0xf200,	0xf30c,	0xf200,	0xf30a,	0xf200,	0xf208,
+	0xf200,	0xf200,	0xf200,	0xf30d,	0xf30e,	0xf200,	0xf30b,	0xf200,
+	0xf200,	0xf200,	0xf900,	0xf901,	0xf902,	0xf903,	0xf904,	0xf905,
+	0xf906,	0xf907,	0xf200,	0xf908,	0xf909,	0xf200,	0xf200,	0xf200,
+	0xf504,	0xf505,	0xf506,	0xf502,	0xf507,	0xf508,	0xf200,	0xf50a,
+	0xf200,	0xf10c,	0xf200,	0xf209,	0xf200,	0xf509,	0xf200,	0xf50b,
+	0xf200,	0xf11d,	0xf115,	0xf114,	0xf118,	0xf116,	0xf503,	0xf117,
+	0xf501,	0xf119,	0xf500,	0xf700,	0xf701,	0xf702,	0xf200,	0xf200,
+};
+
+static u_short macctrl_alt_map[NR_KEYS] __initdata = {
+	0xf801,	0xf813,	0xf804,	0xf806,	0xf808,	0xf807,	0xf81a,	0xf818,
+	0xf803,	0xf816,	0xf200,	0xf802,	0xf811,	0xf817,	0xf805,	0xf812,
+	0xf819,	0xf814,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf80f,
+	0xf815,	0xf200,	0xf809,	0xf810,	0xf201,	0xf80c,	0xf80a,	0xf200,
+	0xf80b,	0xf200,	0xf200,	0xf200,	0xf200,	0xf80e,	0xf80d,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf702,	0xf703,
+	0xf700,	0xf207,	0xf701,	0xf601,	0xf602,	0xf600,	0xf603,	0xf200,
+	0xf200,	0xf310,	0xf200,	0xf30c,	0xf200,	0xf30a,	0xf200,	0xf208,
+	0xf200,	0xf200,	0xf200,	0xf30d,	0xf30e,	0xf200,	0xf30b,	0xf200,
+	0xf200,	0xf200,	0xf300,	0xf301,	0xf302,	0xf303,	0xf304,	0xf305,
+	0xf306,	0xf307,	0xf200,	0xf308,	0xf309,	0xf200,	0xf200,	0xf200,
+	0xf504,	0xf505,	0xf506,	0xf502,	0xf507,	0xf508,	0xf200,	0xf50a,
+	0xf200,	0xf10c,	0xf200,	0xf200,	0xf200,	0xf509,	0xf200,	0xf50b,
+	0xf200,	0xf11d,	0xf115,	0xf114,	0xf118,	0xf116,	0xf503,	0xf117,
+	0xf501,	0xf119,	0xf500,	0xf700,	0xf701,	0xf702,	0xf200,	0xf200,
+};
+
+
 static void kbd_repeat(unsigned long);
 static struct timer_list repeat_timer = { NULL, NULL, 0, 0, kbd_repeat };
 static int last_keycode;
@@ -38,8 +172,7 @@
 
 /* XXX: Hook for mouse driver */
 void (*adb_mouse_interrupt_hook) (char *, int);
-int adb_emulate_button2;
-int adb_emulate_button3;
+static int adb_emulate_buttons = 0;
 extern int console_loglevel;
 
 extern struct kbd_struct kbd_table[];
@@ -47,6 +180,9 @@
 extern void handle_scancode(unsigned char);
 extern void put_queue(int);
 
+static struct adb_ids keyboard_ids;
+static struct adb_ids mouse_ids;
+
 /* this map indicates which keys shouldn't autorepeat. */
 static unsigned char dont_repeat[128] = {
 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@@ -118,6 +254,10 @@
 	up_flag = (keycode & 0x80);
 	keycode &= 0x7f;
 
+	/* on the powerbook 3400, the power key gives code 0x7e */
+	if (keycode == 0x7e)
+		keycode = 0x7f;
+
 	if (!repeat)
 		del_timer(&repeat_timer);
 
@@ -130,7 +270,8 @@
 	 *	Might also want to know how many buttons need to be emulated.
 	 *	-> hide this as function in arch/m68k/mac ?
 	 */
-	if (adb_mouse_interrupt_hook || console_loglevel == 10) {
+	if ( (adb_emulate_buttons) &&
+	     (adb_mouse_interrupt_hook || console_loglevel == 10) ) {
 		unsigned char button, button2, button3, fake_event;
 		static unsigned char button2state=0, button3state=0; /* up */
 		/* faked ADB packet */
@@ -140,19 +281,19 @@
 		fake_event = 0;
 		switch (keycode) {	/* which 'button' ? */
 			case 0x7c:	/* R-option */
-				button2 = (!up_flag);		/* new state */
-				if (button2 != button2state)	/* change ? */
-					button = 2; 
-				button2state = button2;		/* save state */
-				fake_event = 2;
-				break; 
-			case 0x7d:	/* R-control */
 				button3 = (!up_flag);		/* new state */
 				if (button3 != button3state)	/* change ? */ 
 					button = 3; 
 				button3state = button3; 	/* save state */
 				fake_event = 3;
 				break; 
+			case 0x7d:	/* R-control */
+				button2 = (!up_flag);		/* new state */
+				if (button2 != button2state)	/* change ? */
+					button = 2; 
+				button2state = button2;		/* save state */
+				fake_event = 2;
+				break; 
 		}
 		if (fake_event && console_loglevel >= 8)
 			printk("fake event: button2 %d button3 %d button %d\n",
@@ -365,57 +506,134 @@
 };
 
 static struct adb_request led_request;
-static int leds_pending;
+static int leds_pending[16];
+static int pending_devs[16];
+static int pending_led_start=0;
+static int pending_led_end=0;
+
+static void real_mackbd_leds(unsigned char leds, int device)
+{
+
+    if (led_request.complete) {
+	adb_request(&led_request, leds_done, 0, 3,
+		    ADB_WRITEREG(device, KEYB_LEDREG), 0xff,
+		    ~mac_ledmap[leds]);
+    } else {
+	if (!(leds_pending[device] & 0x100)) {
+	    pending_devs[pending_led_end] = device;
+	    pending_led_end++;
+	    pending_led_end = (pending_led_end < 16) ? pending_led_end : 0;
+	}
+	leds_pending[device] = leds | 0x100;
+    }
+}
 
 void mackbd_leds(unsigned char leds)
 {
-	if (led_request.complete) {
-		adb_request(&led_request, leds_done, 0, 3,
-			     ADB_WRITEREG(ADB_KEYBOARD, KEYB_LEDREG),
-			     0xff, ~mac_ledmap[leds]);
-	} else
-		leds_pending = leds | 0x100;
+    int i;
+
+    for(i = 0; i < keyboard_ids.nids; i++)
+	real_mackbd_leds(leds,keyboard_ids.id[i]);
 }
 
 static void leds_done(struct adb_request *req)
 {
-	int leds;
+    int leds,device;
+
+    if (pending_led_start != pending_led_end) {
+	device = pending_devs[pending_led_start];
+	leds = leds_pending[device] & 0xff;
+	leds_pending[device] = 0;
+	pending_led_start++;
+	pending_led_start = (pending_led_start < 16) ? pending_led_start : 0;
+	real_mackbd_leds(leds,device);
+    }
 
-	if (leds_pending) {
-		leds = leds_pending & 0xff;
-		leds_pending = 0;
-		mackbd_leds(leds);
-	}
 }
 
-void mackbd_init_hw(void)
+__initfunc(void mackbd_init_hw(void))
 {
 	struct adb_request req;
+	int i;
+
+	if ( (_machine != _MACH_chrp) && (_machine != _MACH_Pmac) )
+	    return;
+
+	/* setup key map */
+	memcpy(plain_map, macplain_map, sizeof(plain_map));
+	memcpy(shift_map, macshift_map, sizeof(shift_map));
+	memcpy(altgr_map, macaltgr_map, sizeof(altgr_map));
+	memcpy(ctrl_map, macctrl_map, sizeof(ctrl_map));
+	memcpy(shift_ctrl_map, macshift_ctrl_map, sizeof(shift_ctrl_map));
+	memcpy(alt_map, macalt_map, sizeof(alt_map));
+	memcpy(ctrl_alt_map, macctrl_alt_map, sizeof(ctrl_alt_map));
 
 	/* initialize mouse interrupt hook */
 	adb_mouse_interrupt_hook = NULL;
-	/* assume broken mouse :-) - should be adjusted based on 
-	 * result of the mouse setup !! (or passed as  kernel option) */
-	adb_emulate_button2 = 1;
-	adb_emulate_button3 = 1;
-
-	adb_register(ADB_KEYBOARD, keyboard_input);
-	adb_register(ADB_MOUSE, mouse_input);
-
-	/* turn off all leds */
-	adb_request(&req, NULL, ADBREQ_SYNC, 3,
-		    ADB_WRITEREG(ADB_KEYBOARD, KEYB_LEDREG), 0xff, 0xff);
+
+	adb_register(ADB_KEYBOARD, 5, &keyboard_ids, keyboard_input);
+	adb_register(ADB_MOUSE, 1, &mouse_ids, mouse_input);
+
+	for(i = 0; i < keyboard_ids.nids; i++) {
+	    /* turn off all leds */
+	    adb_request(&req, NULL, ADBREQ_SYNC, 3,
+	    ADB_WRITEREG(keyboard_ids.id[i], KEYB_LEDREG), 0xff, 0xff);
+	}
 
 	/* get the keyboard to send separate codes for
 	   left and right shift, control, option keys. */
-	adb_request(&req, NULL, ADBREQ_SYNC, 3,
-		    ADB_WRITEREG(ADB_KEYBOARD, 3), 0, 3);
+	for(i = 0;i < keyboard_ids.nids; i++) {
+	    /* get the keyboard to send separate codes for
+	    left and right shift, control, option keys. */
+	    adb_request(&req, NULL, ADBREQ_SYNC, 3,
+	    ADB_WRITEREG(keyboard_ids.id[i], 3), 0, 3);
+	}
 
 	led_request.complete = 1;
 
 	/* Try to switch the mouse (id 3) to handler 4, for three-button
 	   mode. (0x20 is Service Request Enable, 0x03 is Device ID). */
-	adb_request(&req, NULL, ADBREQ_SYNC, 3,
-		    ADB_WRITEREG(ADB_MOUSE, 3), 0x23, 4 );
+	for(i = 0; i < mouse_ids.nids; i++) {
+	    adb_request(&req, NULL, ADBREQ_SYNC | ADBREQ_REPLY, 1,
+			ADB_READREG(mouse_ids.id[i], 1));
+
+	    if ((req.reply_len) &&
+		(req.reply[1] == 0x9a) && (req.reply[2] == 0x21)) {
+
+		printk("aha, trackball found at %d\n", mouse_ids.id[i]);
+
+		adb_request(&req, NULL, ADBREQ_SYNC, 3,
+		ADB_WRITEREG(mouse_ids.id[i], 3), 0x63, 4 );
+
+		adb_request(&req, NULL, ADBREQ_SYNC, 3,
+		ADB_WRITEREG(mouse_ids.id[i],1), 00,0x81);
+
+		adb_request(&req, NULL, ADBREQ_SYNC, 3,
+		ADB_WRITEREG(mouse_ids.id[i],1), 01,0x81);
+
+		adb_request(&req, NULL, ADBREQ_SYNC, 3,
+		ADB_WRITEREG(mouse_ids.id[i],1), 02,0x81);
+
+		adb_request(&req, NULL, ADBREQ_SYNC, 3,
+		ADB_WRITEREG(mouse_ids.id[i],1), 03,0x38);
+
+		adb_request(&req, NULL, ADBREQ_SYNC, 3,
+		ADB_WRITEREG(mouse_ids.id[i],1), 00,0x81);
+
+		adb_request(&req, NULL, ADBREQ_SYNC, 3,
+		ADB_WRITEREG(mouse_ids.id[i],1), 01,0x81);
+
+		adb_request(&req, NULL, ADBREQ_SYNC, 3,
+		ADB_WRITEREG(mouse_ids.id[i],1), 02,0x81);
+
+		adb_request(&req, NULL, ADBREQ_SYNC, 3,
+		ADB_WRITEREG(mouse_ids.id[i],1), 03,0x38);
+	    }
+	}
 }
 
+void adb_setup_mouse( char *s, int *ints )
+{
+       if (ints[0] >= 1)
+	       adb_emulate_buttons = ints[1];
+}
diff -u --recursive --new-file v2.1.96/linux/drivers/macintosh/macio-adb.c linux/drivers/macintosh/macio-adb.c
--- v2.1.96/linux/drivers/macintosh/macio-adb.c	Mon Jan 12 15:18:13 1998
+++ linux/drivers/macintosh/macio-adb.c	Tue Apr 14 17:33:59 1998
@@ -10,6 +10,7 @@
 #include <asm/prom.h>
 #include <asm/adb.h>
 #include <asm/io.h>
+#include <asm/pgtable.h>
 #include <asm/hydra.h>
 #include <asm/irq.h>
 #include <asm/system.h>
@@ -71,7 +72,7 @@
 	if (adbs == 0)
 		return;
 
-#if 1
+#if 0
 	{ int i;
 
 	printk("macio_adb_init: node = %p, addrs =", adbs->node);
@@ -79,16 +80,17 @@
 		printk(" %x(%x)", adbs->addrs[i].address, adbs->addrs[i].size);
 	printk(", intrs =");
 	for (i = 0; i < adbs->n_intrs; ++i)
-		printk(" %x", adbs->intrs[i]);
+		printk(" %x", adbs->intrs[i].line);
 	printk("\n"); }
 #endif
 	
-	adb = (volatile struct adb_regs *) adbs->addrs->address;
+	adb = (volatile struct adb_regs *)
+		ioremap(adbs->addrs->address, sizeof(struct adb_regs));
 
-	if (request_irq(openpic_to_irq(adbs->intrs[0]), macio_adb_interrupt,
+	if (request_irq(adbs->intrs[0].line, macio_adb_interrupt,
 			0, "ADB", (void *)0)) {
 		printk(KERN_ERR "ADB: can't get irq %d\n",
-		       openpic_to_irq(adbs->intrs[0]));
+		       adbs->intrs[0].line);
 		return;
 	}
 
diff -u --recursive --new-file v2.1.96/linux/drivers/macintosh/mackeymap.c linux/drivers/macintosh/mackeymap.c
--- v2.1.96/linux/drivers/macintosh/mackeymap.c	Mon Jan 12 15:18:13 1998
+++ linux/drivers/macintosh/mackeymap.c	Wed Dec 31 16:00:00 1969
@@ -1,262 +0,0 @@
-/* Do not edit this file! It was automatically generated by   */
-/*    loadkeys --mktable defkeymap.map > defkeymap.c          */
-
-#include <linux/types.h>
-#include <linux/keyboard.h>
-#include <linux/kd.h>
-
-u_short plain_map[NR_KEYS] = {
-	0xfb61,	0xfb73,	0xfb64,	0xfb66,	0xfb68,	0xfb67,	0xfb7a,	0xfb78,
-	0xfb63,	0xfb76,	0xf200,	0xfb62,	0xfb71,	0xfb77,	0xfb65,	0xfb72,
-	0xfb79,	0xfb74,	0xf031,	0xf032,	0xf033,	0xf034,	0xf036,	0xf035,
-	0xf03d,	0xf039,	0xf037,	0xf02d,	0xf038,	0xf030,	0xf05d,	0xfb6f,
-	0xfb75,	0xf05b,	0xfb69,	0xfb70,	0xf201,	0xfb6c,	0xfb6a,	0xf027,
-	0xfb6b,	0xf03b,	0xf05c,	0xf02c,	0xf02f,	0xfb6e,	0xfb6d,	0xf02e,
-	0xf009,	0xf020,	0xf060,	0xf07f,	0xf200,	0xf01b,	0xf702,	0xf703,
-	0xf700,	0xf207,	0xf701,	0xf601,	0xf602,	0xf600,	0xf603,	0xf200,
-	0xf200,	0xf310,	0xf200,	0xf30c,	0xf200,	0xf30a,	0xf200,	0xf208,
-	0xf200,	0xf200,	0xf200,	0xf30d,	0xf30e,	0xf200,	0xf30b,	0xf200,
-	0xf200,	0xf200,	0xf300,	0xf301,	0xf302,	0xf303,	0xf304,	0xf305,
-	0xf306,	0xf307,	0xf200,	0xf308,	0xf309,	0xf200,	0xf200,	0xf200,
-	0xf104,	0xf105,	0xf106,	0xf102,	0xf107,	0xf108,	0xf200,	0xf10a,
-	0xf200,	0xf10c,	0xf200,	0xf209,	0xf200,	0xf109,	0xf200,	0xf10b,
-	0xf200,	0xf11d,	0xf115,	0xf114,	0xf118,	0xf116,	0xf103,	0xf117,
-	0xf101,	0xf119,	0xf100,	0xf700,	0xf701,	0xf702,	0xf200,	0xf200,
-};
-
-static u_short shift_map[NR_KEYS] = {
-	0xfb41,	0xfb53,	0xfb44,	0xfb46,	0xfb48,	0xfb47,	0xfb5a,	0xfb58,
-	0xfb43,	0xfb56,	0xf200,	0xfb42,	0xfb51,	0xfb57,	0xfb45,	0xfb52,
-	0xfb59,	0xfb54,	0xf021,	0xf040,	0xf023,	0xf024,	0xf05e,	0xf025,
-	0xf02b,	0xf028,	0xf026,	0xf05f,	0xf02a,	0xf029,	0xf07d,	0xfb4f,
-	0xfb55,	0xf07b,	0xfb49,	0xfb50,	0xf201,	0xfb4c,	0xfb4a,	0xf022,
-	0xfb4b,	0xf03a,	0xf07c,	0xf03c,	0xf03f,	0xfb4e,	0xfb4d,	0xf03e,
-	0xf009,	0xf020,	0xf07e,	0xf07f,	0xf200,	0xf01b,	0xf702,	0xf703,
-	0xf700,	0xf207,	0xf701,	0xf601,	0xf602,	0xf600,	0xf603,	0xf200,
-	0xf200,	0xf310,	0xf200,	0xf30c,	0xf200,	0xf30a,	0xf200,	0xf208,
-	0xf200,	0xf200,	0xf200,	0xf30d,	0xf30e,	0xf200,	0xf30b,	0xf200,
-	0xf200,	0xf200,	0xf300,	0xf301,	0xf302,	0xf303,	0xf304,	0xf305,
-	0xf306,	0xf307,	0xf200,	0xf308,	0xf309,	0xf200,	0xf200,	0xf200,
-	0xf10e,	0xf10f,	0xf110,	0xf10c,	0xf111,	0xf112,	0xf200,	0xf10a,
-	0xf200,	0xf10c,	0xf200,	0xf203,	0xf200,	0xf113,	0xf200,	0xf10b,
-	0xf200,	0xf11d,	0xf115,	0xf114,	0xf20b,	0xf116,	0xf10d,	0xf117,
-	0xf10b,	0xf20a,	0xf10a,	0xf700,	0xf701,	0xf702,	0xf200,	0xf200,
-};
-
-static u_short altgr_map[NR_KEYS] = {
-	0xf914,	0xfb73,	0xf917,	0xf919,	0xfb68,	0xfb67,	0xfb7a,	0xfb78,
-	0xf916,	0xfb76,	0xf200,	0xf915,	0xfb71,	0xfb77,	0xf918,	0xfb72,
-	0xfb79,	0xfb74,	0xf200,	0xf040,	0xf200,	0xf024,	0xf200,	0xf200,
-	0xf200,	0xf05d,	0xf07b,	0xf05c,	0xf05b,	0xf07d,	0xf07e,	0xfb6f,
-	0xfb75,	0xf200,	0xfb69,	0xfb70,	0xf201,	0xfb6c,	0xfb6a,	0xf200,
-	0xfb6b,	0xf200,	0xf200,	0xf200,	0xf200,	0xfb6e,	0xfb6d,	0xf200,
-	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf702,	0xf703,
-	0xf700,	0xf207,	0xf701,	0xf601,	0xf602,	0xf600,	0xf603,	0xf200,
-	0xf200,	0xf310,	0xf200,	0xf30c,	0xf200,	0xf30a,	0xf200,	0xf208,
-	0xf200,	0xf200,	0xf200,	0xf30d,	0xf30e,	0xf200,	0xf30b,	0xf200,
-	0xf200,	0xf200,	0xf90a,	0xf90b,	0xf90c,	0xf90d,	0xf90e,	0xf90f,
-	0xf910,	0xf911,	0xf200,	0xf912,	0xf913,	0xf200,	0xf200,	0xf200,
-	0xf510,	0xf511,	0xf512,	0xf50e,	0xf513,	0xf514,	0xf200,	0xf516,
-	0xf200,	0xf10c,	0xf200,	0xf202,	0xf200,	0xf515,	0xf200,	0xf517,
-	0xf200,	0xf11d,	0xf115,	0xf114,	0xf118,	0xf116,	0xf50f,	0xf117,
-	0xf50d,	0xf119,	0xf50c,	0xf700,	0xf701,	0xf702,	0xf200,	0xf200,
-};
-
-static u_short ctrl_map[NR_KEYS] = {
-	0xf001,	0xf013,	0xf004,	0xf006,	0xf008,	0xf007,	0xf01a,	0xf018,
-	0xf003,	0xf016,	0xf200,	0xf002,	0xf011,	0xf017,	0xf005,	0xf012,
-	0xf019,	0xf014,	0xf200,	0xf000,	0xf01b,	0xf01c,	0xf01e,	0xf01d,
-	0xf200,	0xf200,	0xf01f,	0xf01f,	0xf07f,	0xf200,	0xf01d,	0xf00f,
-	0xf015,	0xf01b,	0xf009,	0xf010,	0xf201,	0xf00c,	0xf00a,	0xf007,
-	0xf00b,	0xf200,	0xf01c,	0xf200,	0xf07f,	0xf00e,	0xf00d,	0xf20e,
-	0xf200,	0xf000,	0xf000,	0xf008,	0xf200,	0xf200,	0xf702,	0xf703,
-	0xf700,	0xf207,	0xf701,	0xf601,	0xf602,	0xf600,	0xf603,	0xf200,
-	0xf200,	0xf310,	0xf200,	0xf30c,	0xf200,	0xf30a,	0xf200,	0xf208,
-	0xf200,	0xf200,	0xf200,	0xf30d,	0xf30e,	0xf200,	0xf30b,	0xf200,
-	0xf200,	0xf200,	0xf300,	0xf301,	0xf302,	0xf303,	0xf304,	0xf305,
-	0xf306,	0xf307,	0xf200,	0xf308,	0xf309,	0xf200,	0xf200,	0xf200,
-	0xf104,	0xf105,	0xf106,	0xf102,	0xf107,	0xf108,	0xf200,	0xf10a,
-	0xf200,	0xf10c,	0xf200,	0xf204,	0xf200,	0xf109,	0xf200,	0xf10b,
-	0xf200,	0xf11d,	0xf115,	0xf114,	0xf118,	0xf116,	0xf103,	0xf117,
-	0xf101,	0xf119,	0xf100,	0xf700,	0xf701,	0xf702,	0xf200,	0xf200,
-};
-
-static u_short shift_ctrl_map[NR_KEYS] = {
-	0xf001,	0xf013,	0xf004,	0xf006,	0xf008,	0xf007,	0xf01a,	0xf018,
-	0xf003,	0xf016,	0xf200,	0xf002,	0xf011,	0xf017,	0xf005,	0xf012,
-	0xf019,	0xf014,	0xf200,	0xf000,	0xf200,	0xf200,	0xf200,	0xf200,
-	0xf200,	0xf200,	0xf200,	0xf01f,	0xf200,	0xf200,	0xf200,	0xf00f,
-	0xf015,	0xf200,	0xf009,	0xf010,	0xf201,	0xf00c,	0xf00a,	0xf200,
-	0xf00b,	0xf200,	0xf200,	0xf200,	0xf200,	0xf00e,	0xf00d,	0xf200,
-	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf702,	0xf703,
-	0xf700,	0xf207,	0xf701,	0xf601,	0xf602,	0xf600,	0xf603,	0xf200,
-	0xf200,	0xf310,	0xf200,	0xf30c,	0xf200,	0xf30a,	0xf200,	0xf208,
-	0xf200,	0xf200,	0xf200,	0xf30d,	0xf30e,	0xf200,	0xf30b,	0xf200,
-	0xf200,	0xf200,	0xf300,	0xf301,	0xf302,	0xf303,	0xf304,	0xf305,
-	0xf306,	0xf307,	0xf200,	0xf308,	0xf309,	0xf200,	0xf200,	0xf200,
-	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
-	0xf200,	0xf10c,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
-	0xf200,	0xf11d,	0xf115,	0xf114,	0xf118,	0xf116,	0xf200,	0xf117,
-	0xf200,	0xf119,	0xf200,	0xf700,	0xf701,	0xf702,	0xf200,	0xf20c,
-};
-
-static u_short alt_map[NR_KEYS] = {
-	0xf861,	0xf873,	0xf864,	0xf866,	0xf868,	0xf867,	0xf87a,	0xf878,
-	0xf863,	0xf876,	0xf200,	0xf862,	0xf871,	0xf877,	0xf865,	0xf872,
-	0xf879,	0xf874,	0xf831,	0xf832,	0xf833,	0xf834,	0xf836,	0xf835,
-	0xf83d,	0xf839,	0xf837,	0xf82d,	0xf838,	0xf830,	0xf85d,	0xf86f,
-	0xf875,	0xf85b,	0xf869,	0xf870,	0xf80d,	0xf86c,	0xf86a,	0xf827,
-	0xf86b,	0xf83b,	0xf85c,	0xf82c,	0xf82f,	0xf86e,	0xf86d,	0xf82e,
-	0xf809,	0xf820,	0xf860,	0xf87f,	0xf200,	0xf81b,	0xf702,	0xf703,
-	0xf700,	0xf207,	0xf701,	0xf210,	0xf211,	0xf600,	0xf603,	0xf200,
-	0xf200,	0xf310,	0xf200,	0xf30c,	0xf200,	0xf30a,	0xf200,	0xf208,
-	0xf200,	0xf200,	0xf200,	0xf30d,	0xf30e,	0xf200,	0xf30b,	0xf200,
-	0xf200,	0xf200,	0xf900,	0xf901,	0xf902,	0xf903,	0xf904,	0xf905,
-	0xf906,	0xf907,	0xf200,	0xf908,	0xf909,	0xf200,	0xf200,	0xf200,
-	0xf504,	0xf505,	0xf506,	0xf502,	0xf507,	0xf508,	0xf200,	0xf50a,
-	0xf200,	0xf10c,	0xf200,	0xf209,	0xf200,	0xf509,	0xf200,	0xf50b,
-	0xf200,	0xf11d,	0xf115,	0xf114,	0xf118,	0xf116,	0xf503,	0xf117,
-	0xf501,	0xf119,	0xf500,	0xf700,	0xf701,	0xf702,	0xf200,	0xf200,
-};
-
-static u_short ctrl_alt_map[NR_KEYS] = {
-	0xf801,	0xf813,	0xf804,	0xf806,	0xf808,	0xf807,	0xf81a,	0xf818,
-	0xf803,	0xf816,	0xf200,	0xf802,	0xf811,	0xf817,	0xf805,	0xf812,
-	0xf819,	0xf814,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
-	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf80f,
-	0xf815,	0xf200,	0xf809,	0xf810,	0xf201,	0xf80c,	0xf80a,	0xf200,
-	0xf80b,	0xf200,	0xf200,	0xf200,	0xf200,	0xf80e,	0xf80d,	0xf200,
-	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf702,	0xf703,
-	0xf700,	0xf207,	0xf701,	0xf601,	0xf602,	0xf600,	0xf603,	0xf200,
-	0xf200,	0xf310,	0xf200,	0xf30c,	0xf200,	0xf30a,	0xf200,	0xf208,
-	0xf200,	0xf200,	0xf200,	0xf30d,	0xf30e,	0xf200,	0xf30b,	0xf200,
-	0xf200,	0xf200,	0xf300,	0xf301,	0xf302,	0xf303,	0xf304,	0xf305,
-	0xf306,	0xf307,	0xf200,	0xf308,	0xf309,	0xf200,	0xf200,	0xf200,
-	0xf504,	0xf505,	0xf506,	0xf502,	0xf507,	0xf508,	0xf200,	0xf50a,
-	0xf200,	0xf10c,	0xf200,	0xf200,	0xf200,	0xf509,	0xf200,	0xf50b,
-	0xf200,	0xf11d,	0xf115,	0xf114,	0xf118,	0xf116,	0xf503,	0xf117,
-	0xf501,	0xf119,	0xf500,	0xf700,	0xf701,	0xf702,	0xf200,	0xf200,
-};
-
-ushort *key_maps[MAX_NR_KEYMAPS] = {
-	plain_map, shift_map, altgr_map, 0,
-	ctrl_map, shift_ctrl_map, 0, 0,
-	alt_map, 0, 0, 0,
-	ctrl_alt_map,	0
-};
-
-unsigned int keymap_count = 7;
-
-/*
- * Philosophy: most people do not define more strings, but they who do
- * often want quite a lot of string space. So, we statically allocate
- * the default and allocate dynamically in chunks of 512 bytes.
- */
-
-char func_buf[] = {
-	'\033', '[', '[', 'A', 0, 
-	'\033', '[', '[', 'B', 0, 
-	'\033', '[', '[', 'C', 0, 
-	'\033', '[', '[', 'D', 0, 
-	'\033', '[', '[', 'E', 0, 
-	'\033', '[', '1', '7', '~', 0, 
-	'\033', '[', '1', '8', '~', 0, 
-	'\033', '[', '1', '9', '~', 0, 
-	'\033', '[', '2', '0', '~', 0, 
-	'\033', '[', '2', '1', '~', 0, 
-	'\033', '[', '2', '3', '~', 0, 
-	'\033', '[', '2', '4', '~', 0, 
-	'\033', '[', '2', '5', '~', 0, 
-	'\033', '[', '2', '6', '~', 0, 
-	'\033', '[', '2', '8', '~', 0, 
-	'\033', '[', '2', '9', '~', 0, 
-	'\033', '[', '3', '1', '~', 0, 
-	'\033', '[', '3', '2', '~', 0, 
-	'\033', '[', '3', '3', '~', 0, 
-	'\033', '[', '3', '4', '~', 0, 
-	'\033', '[', '1', '~', 0, 
-	'\033', '[', '2', '~', 0, 
-	'\033', '[', '3', '~', 0, 
-	'\033', '[', '4', '~', 0, 
-	'\033', '[', '5', '~', 0, 
-	'\033', '[', '6', '~', 0, 
-	'\033', '[', 'M', 0, 
-	'\033', '[', 'P', 0, 
-};
-
-char *funcbufptr = func_buf;
-int funcbufsize = sizeof(func_buf);
-int funcbufleft = 0;          /* space left */
-
-char *func_table[MAX_NR_FUNC] = {
-	func_buf + 0,
-	func_buf + 5,
-	func_buf + 10,
-	func_buf + 15,
-	func_buf + 20,
-	func_buf + 25,
-	func_buf + 31,
-	func_buf + 37,
-	func_buf + 43,
-	func_buf + 49,
-	func_buf + 55,
-	func_buf + 61,
-	func_buf + 67,
-	func_buf + 73,
-	func_buf + 79,
-	func_buf + 85,
-	func_buf + 91,
-	func_buf + 97,
-	func_buf + 103,
-	func_buf + 109,
-	func_buf + 115,
-	func_buf + 120,
-	func_buf + 125,
-	func_buf + 130,
-	func_buf + 135,
-	func_buf + 140,
-	func_buf + 145,
-	0,
-	0,
-	func_buf + 149,
-	0,
-};
-
-struct kbdiacr accent_table[MAX_DIACR] = {
-	{'`', 'A', '\300'},	{'`', 'a', '\340'},
-	{'\'', 'A', '\301'},	{'\'', 'a', '\341'},
-	{'^', 'A', '\302'},	{'^', 'a', '\342'},
-	{'~', 'A', '\303'},	{'~', 'a', '\343'},
-	{'"', 'A', '\304'},	{'"', 'a', '\344'},
-	{'O', 'A', '\305'},	{'o', 'a', '\345'},
-	{'0', 'A', '\305'},	{'0', 'a', '\345'},
-	{'A', 'A', '\305'},	{'a', 'a', '\345'},
-	{'A', 'E', '\306'},	{'a', 'e', '\346'},
-	{',', 'C', '\307'},	{',', 'c', '\347'},
-	{'`', 'E', '\310'},	{'`', 'e', '\350'},
-	{'\'', 'E', '\311'},	{'\'', 'e', '\351'},
-	{'^', 'E', '\312'},	{'^', 'e', '\352'},
-	{'"', 'E', '\313'},	{'"', 'e', '\353'},
-	{'`', 'I', '\314'},	{'`', 'i', '\354'},
-	{'\'', 'I', '\315'},	{'\'', 'i', '\355'},
-	{'^', 'I', '\316'},	{'^', 'i', '\356'},
-	{'"', 'I', '\317'},	{'"', 'i', '\357'},
-	{'-', 'D', '\320'},	{'-', 'd', '\360'},
-	{'~', 'N', '\321'},	{'~', 'n', '\361'},
-	{'`', 'O', '\322'},	{'`', 'o', '\362'},
-	{'\'', 'O', '\323'},	{'\'', 'o', '\363'},
-	{'^', 'O', '\324'},	{'^', 'o', '\364'},
-	{'~', 'O', '\325'},	{'~', 'o', '\365'},
-	{'"', 'O', '\326'},	{'"', 'o', '\366'},
-	{'/', 'O', '\330'},	{'/', 'o', '\370'},
-	{'`', 'U', '\331'},	{'`', 'u', '\371'},
-	{'\'', 'U', '\332'},	{'\'', 'u', '\372'},
-	{'^', 'U', '\333'},	{'^', 'u', '\373'},
-	{'"', 'U', '\334'},	{'"', 'u', '\374'},
-	{'\'', 'Y', '\335'},	{'\'', 'y', '\375'},
-	{'T', 'H', '\336'},	{'t', 'h', '\376'},
-	{'s', 's', '\337'},	{'"', 'y', '\377'},
-	{'s', 'z', '\337'},	{'i', 'j', '\377'},
-};
-
-unsigned int accent_table_size = 68;
diff -u --recursive --new-file v2.1.96/linux/drivers/macintosh/macserial.c linux/drivers/macintosh/macserial.c
--- v2.1.96/linux/drivers/macintosh/macserial.c	Mon Jan 12 15:18:14 1998
+++ linux/drivers/macintosh/macserial.c	Tue Apr 14 17:33:59 1998
@@ -20,13 +20,21 @@
 #include <linux/mm.h>
 #include <linux/kernel.h>
 #include <linux/delay.h>
+#include <linux/init.h>
+#ifdef CONFIG_SERIAL_CONSOLE
+#include <linux/console.h>
+#endif
 
 #include <asm/io.h>
+#include <asm/pgtable.h>
 #include <asm/irq.h>
 #include <asm/prom.h>
 #include <asm/system.h>
 #include <asm/segment.h>
 #include <asm/bitops.h>
+#ifdef CONFIG_KGDB
+#include <asm/kgdb.h>
+#endif
 
 #include "macserial.h"
 
@@ -43,7 +51,6 @@
    in the order we want. */
 #define RECOVERY_DELAY	eieio()
 
-struct mac_zschannel *zs_kgdbchan;
 struct mac_zschannel zs_channels[NUM_CHANNELS];
 
 struct mac_serial zs_soft[NUM_CHANNELS];
@@ -51,31 +58,24 @@
 struct mac_serial *zs_chain;	/* list of all channels */
 
 struct tty_struct zs_ttys[NUM_CHANNELS];
-/** struct tty_struct *zs_constty; **/
 
-/* Console hooks... */
-static int zs_cons_chan = 0;
-struct mac_serial *zs_consinfo = 0;
-struct mac_zschannel *zs_conschan;
-
-/*
- * Initialization values for when a channel is used for
- * kernel gdb support.
- */
-static unsigned char kgdb_regs[16] = {
-	0, 0, 0,		/* write 0, 1, 2 */
-	(Rx8 | RxENABLE),	/* write 3 */
-	(X16CLK | SB1),		/* write 4 */
-	(Tx8 | TxENAB | RTS),	/* write 5 */
-	0, 0, 0,		/* write 6, 7, 8 */
-	(NV),			/* write 9 */
-	(NRZ),			/* write 10 */
-	(TCBR | RCBR),		/* write 11 */
-	1, 0,			/* 38400 baud divisor, write 12 + 13 */
-	(BRENABL),		/* write 14 */
-	(DCDIE)			/* write 15 */
-};
+#ifdef CONFIG_SERIAL_CONSOLE
+static struct console sercons;
+#endif
 
+#ifdef CONFIG_KGDB
+struct mac_zschannel *zs_kgdbchan;
+static unsigned char scc_inittab[] = {
+	9,  0x80,	/* reset A side (CHRA) */
+	13, 0,		/* set baud rate divisor */
+	12, 1,
+	14, 1,		/* baud rate gen enable, src=rtxc (BRENABL) */
+	11, 0x50,	/* clocks = br gen (RCBR | TCBR) */
+	5,  0x6a,	/* tx 8 bits, assert RTS (Tx8 | TxENAB | RTS) */
+	4,  0x44,	/* x16 clock, 1 stop (SB1 | X16CLK)*/
+	3,  0xc1,	/* rx enable, 8 bits (RxENABLE | Rx8)*/
+};
+#endif
 #define ZS_CLOCK         3686400 	/* Z8530 RTxC input clock rate */
 
 DECLARE_TASK_QUEUE(tq_serial);
@@ -90,8 +90,8 @@
 /* number of characters left in xmit buffer before we ask for more */
 #define WAKEUP_CHARS 256
 
-/* Debugging... DEBUG_INTR is bad to use when one of the zs
- * lines is your console ;(
+/*
+ * Debugging.
  */
 #undef SERIAL_DEBUG_INTR
 #undef SERIAL_DEBUG_OPEN
@@ -233,23 +233,6 @@
 	return;
 }
 
-static inline void kgdb_chaninit(struct mac_serial *ss, int intson, int bps)
-{
-	int brg;
-
-	if (intson) {
-		kgdb_regs[R1] = INT_ALL_Rx;
-		kgdb_regs[R9] |= MIE;
-	} else {
-		kgdb_regs[R1] = 0;
-		kgdb_regs[R9] &= ~MIE;
-	}
-	brg = BPS_TO_BRG(bps, ZS_CLOCK/16);
-	kgdb_regs[R12] = brg;
-	kgdb_regs[R13] = brg >> 8;
-	load_zsregs(ss->zs_channel, kgdb_regs);
-}
-
 /* Utility routines for the Zilog */
 static inline int get_zsbaud(struct mac_serial *ss)
 {
@@ -300,8 +283,6 @@
 	mark_bh(SERIAL_BH);
 }
 
-extern void breakpoint(void);  /* For the KGDB frame character */
-
 static _INLINE_ void receive_chars(struct mac_serial *info,
 				   struct pt_regs *regs)
 {
@@ -313,17 +294,13 @@
 		stat = read_zsreg(info->zs_channel, R1);
 		ch = read_zsdata(info->zs_channel);
 
-#if 0	/* KGDB not yet supported */
-		/* Look for kgdb 'stop' character, consult the gdb documentation
-		 * for remote target debugging and arch/sparc/kernel/sparc-stub.c
-		 * to see how all this works.
-		 */
-		if ((info->kgdb_channel) && (ch =='\003')) {
-			breakpoint();
-			continue;
+#ifdef CONFIG_KGDB
+		if (info->kgdb_channel) {
+			if (ch == 0x03 || ch == '$')
+				breakpoint();
+			return;
 		}
 #endif
-
 		if (!tty)
 			continue;
 		tty_flip_buffer_push(tty);
@@ -767,93 +744,6 @@
 	restore_flags(flags);
 }
 
-/* This is for console output over ttya/ttyb */
-static void rs_put_char(char ch)
-{
-	struct mac_zschannel *chan = zs_conschan;
-	int loops = 0;
-	unsigned long flags;
-
-	if(!chan)
-		return;
-
-	save_flags(flags); cli();
-	while ((read_zsreg(chan, 0) & Tx_BUF_EMP) == 0)
-		if (++loops >= 1000000)
-			break;
-	write_zsdata(chan, ch);
-	restore_flags(flags);
-}
-
-/* These are for receiving and sending characters under the kgdb
- * source level kernel debugger.
- */
-void putDebugChar(char kgdb_char)
-{
-	struct mac_zschannel *chan = zs_kgdbchan;
-
-	while ((read_zsreg(chan, 0) & Tx_BUF_EMP) == 0)
-		udelay(5);
-	write_zsdata(chan, kgdb_char);
-}
-
-char getDebugChar(void)
-{
-	struct mac_zschannel *chan = zs_kgdbchan;
-
-	while ((read_zsreg(chan, 0) & Rx_CH_AV) == 0)
-		udelay(5);
-	return read_zsdata(chan);
-}
-
-/*
- * Fair output driver allows a process to speak.
- */
-static void rs_fair_output(void)
-{
-	int left;		/* Output no more than that */
-	unsigned long flags;
-	struct mac_serial *info = zs_consinfo;
-	char c;
-
-	if (info == 0) return;
-	if (info->xmit_buf == 0) return;
-
-	save_flags(flags);  cli();
-	left = info->xmit_cnt;
-	while (left != 0) {
-		c = info->xmit_buf[info->xmit_tail];
-		info->xmit_tail = (info->xmit_tail+1) & (SERIAL_XMIT_SIZE-1);
-		info->xmit_cnt--;
-		restore_flags(flags);
-
-		rs_put_char(c);
-
-		save_flags(flags);  cli();
-		left = MIN(info->xmit_cnt, left-1);
-	}
-
-	restore_flags(flags);
-	return;
-}
-
-/*
- * zs_console_print is registered for printk.
- */
-static void zs_console_print(const char *p)
-{
-	char c;
-
-	while ((c = *(p++)) != 0) {
-		if (c == '\n')
-			rs_put_char('\r');
-		rs_put_char(c);
-	}
-
-	/* Comment this if you want to have a strict interrupt-driven output */
-	rs_fair_output();
-}
-
 static void rs_flush_chars(struct tty_struct *tty)
 {
 	struct mac_serial *info = (struct mac_serial *)tty->driver_data;
@@ -1204,6 +1094,10 @@
 	int error;
 	struct mac_serial * info = (struct mac_serial *)tty->driver_data;
 
+#ifdef CONFIG_KGDB
+	if (info->kgdb_channel)
+		return -ENODEV;
+#endif
 	if (serial_paranoia_check(info, tty->device, "rs_ioctl"))
 		return -ENODEV;
 
@@ -1340,7 +1234,6 @@
 	 * At this point we stop accepting input.  To do this, we
 	 * disable the receiver and receive interrupts.
 	 */
-	/** if (!info->iscons) ... **/
 	info->curregs[3] &= ~RxENABLE;
 	info->pendregs[3] = info->curregs[3];
 	write_zsreg(info->zs_channel, 3, info->curregs[3]);
@@ -1580,9 +1473,10 @@
 		return -ENODEV;
 	info = zs_soft + line;
 
-	/* Is the kgdb running over this line? */
+#ifdef CONFIG_KGDB
 	if (info->kgdb_channel)
 		return -ENODEV;
+#endif
 	if (serial_paranoia_check(info, tty->device, "rs_open"))
 		return -ENODEV;
 #ifdef SERIAL_DEBUG_OPEN
@@ -1632,6 +1526,13 @@
 			*tty->termios = info->callout_termios;
 		change_speed(info);
 	}
+#ifdef CONFIG_SERIAL_CONSOLE
+	if (sercons.cflag && sercons.index == line) {
+		tty->termios->c_cflag = sercons.cflag;
+		sercons.cflag = 0;
+		change_speed(info);
+	}
+#endif
 
 	info->session = current->session;
 	info->pgrp = current->pgrp;
@@ -1672,15 +1573,15 @@
 				continue;
 			}
 			zs_channels[n].control = (volatile unsigned char *)
-				ch->addrs[0].address;
+				ioremap(ch->addrs[0].address, 0x1000);
 			zs_channels[n].data = zs_channels[n].control
 				+ ch->addrs[0].size / 2;
 			zs_soft[n].zs_channel = &zs_channels[n];
-			zs_soft[n].irq = ch->intrs[0];
-			if (request_irq(ch->intrs[0], rs_interrupt, 0,
+			zs_soft[n].irq = ch->intrs[0].line;
+			if (request_irq(ch->intrs[0].line, rs_interrupt, 0,
 					"SCC", &zs_soft[n]))
-				panic("macserial: can't get irq %d",
-				      ch->intrs[0]);
+				printk(KERN_ERR "macserial: can't get irq %d\n",
+				       ch->intrs[0].line);
 			/* XXX this assumes the prom puts chan A before B */
 			if (n & 1)
 				zs_soft[n].zs_chan_a = &zs_channels[n-1];
@@ -1769,6 +1670,11 @@
 	save_flags(flags); cli();
 
 	for (channel = 0; channel < zs_channels_found; ++channel) {
+#ifdef CONFIG_KGDB
+		if (zs_soft[channel].kgdb_channel) {
+			continue;
+		}
+#endif
 		zs_soft[channel].clk_divisor = 16;
 		zs_soft[channel].zs_baud = get_zsbaud(&zs_soft[channel]);
 
@@ -1779,17 +1685,15 @@
 			write_zsreg(zs_soft[channel].zs_channel, R9,
 				    (NV | MIE));
 		}
-		/* If this is the kgdb line, enable interrupts because we
-		 * now want to receive the 'control-c' character from the
-		 * client attached to us asynchronously.
-		 */
-		if (zs_soft[channel].kgdb_channel)
-			kgdb_chaninit(&zs_soft[channel], 1,
-				      zs_soft[channel].zs_baud);
 	}
 
 	for (info = zs_chain, i = 0; info; info = info->zs_next, i++)
 	{
+#ifdef CONFIG_KGDB
+		if (info->kgdb_channel) {
+			continue;
+		}
+#endif
 		info->magic = SERIAL_MAGIC;
 		info->port = (int) info->zs_channel->control;
 		info->line = i;
@@ -1832,84 +1736,212 @@
 	return;
 }
 
-extern void register_console(void (*proc)(const char *));
+/*
+ * ------------------------------------------------------------
+ * Serial console driver
+ * ------------------------------------------------------------
+ */
+#ifdef CONFIG_SERIAL_CONSOLE
+
 
 /*
- * Initialization values for when a channel is used for
- * a serial console.
+ *	Print a string to the serial port trying not to disturb
+ *	any possible real use of the port...
  */
-static unsigned char cons_init_regs[16] = {
-	0, 0, 0,		/* write 0, 1, 2 */
-	(Rx8 | RxENABLE),	/* write 3 */
-	(X16CLK | SB1),		/* write 4 */
-	(Tx8 | TxENAB | RTS),	/* write 5 */
-	0, 0, 0,		/* write 6, 7, 8 */
-	0,			/* write 9 */
-	(NRZ),			/* write 10 */
-	(TCBR | RCBR),		/* write 11 */
-	1, 0,			/* 38400 baud divisor, write 12 + 13 */
-	(BRENABL),		/* write 14 */
-	0			/* write 15 */
-};
+static void serial_console_write(struct console *co, const char *s,
+				 unsigned count)
+{
+}
 
 /*
- * Hooks for running a serial console.  con_init() calls this if the
- * console is being run over one of the serial ports.
- * 'channel' is decoded as 1=modem, 2=printer.
+ *	Receive character from the serial port
  */
-void
-rs_cons_hook(int chip, int out, int channel)
+static int serial_console_wait_key(struct console *co)
 {
-	int brg;
+	return 0;
+}
 
-	if (!out)
-		return;
-	if (zs_consinfo != 0) {
-		printk("rs_cons_hook called twice?\n");
-		return;
-	}
-	if (zs_chain == 0)
-		probe_sccs();
-	--channel;
-	if (channel < 0 || channel >= zs_channels_found) {
-		printk("rs_cons_hook: channel = %d?\n", channel);
-		return;
+static kdev_t serial_console_device(struct console *c)
+{
+	return MKDEV(TTY_MAJOR, 64 + c->index);
+}
+
+/*
+ *	Setup initial baud/bits/parity. We do two things here:
+ *	- construct a cflag setting for the first rs_open()
+ *	- initialize the serial port
+ *	Return non-zero if we didn't find a serial port.
+ */
+__initfunc(static int serial_console_setup(struct console *co, char *options))
+{
+	struct serial_state *ser;
+	unsigned cval;
+	int	baud = 9600;
+	int	bits = 8;
+	int	parity = 'n';
+	int	cflag = CREAD | HUPCL | CLOCAL;
+	int	quot = 0;
+	char	*s;
+
+	if (options) {
+		baud = simple_strtoul(options, NULL, 10);
+		s = options;
+		while(*s >= '0' && *s <= '9')
+			s++;
+		if (*s)
+			parity = *s++;
+		if (*s)
+			bits   = *s - '0';
 	}
 
-	zs_cons_chan = channel;
-	zs_consinfo = &zs_soft[channel];
-	zs_conschan = zs_consinfo->zs_channel;
-	zs_consinfo->clk_divisor = 16;
-	zs_consinfo->zs_baud = 38400;
-	zs_consinfo->is_cons = 1;
-
-	memcpy(zs_consinfo->curregs, cons_init_regs, sizeof(cons_init_regs));
-	brg = BPS_TO_BRG(zs_consinfo->zs_baud, ZS_CLOCK/16);
-	zs_consinfo->curregs[R12] = brg;
-	zs_consinfo->curregs[R13] = brg >> 8;
-	load_zsregs(zs_conschan, zs_consinfo->curregs);
+	/*
+	 *	Now construct a cflag setting.
+	 */
+	switch(baud) {
+	case 1200:
+		cflag |= B1200;
+		break;
+	case 2400:
+		cflag |= B2400;
+		break;
+	case 4800:
+		cflag |= B4800;
+		break;
+	case 19200:
+		cflag |= B19200;
+		break;
+	case 38400:
+		cflag |= B38400;
+		break;
+	case 57600:
+		cflag |= B57600;
+		break;
+	case 115200:
+		cflag |= B115200;
+		break;
+	case 9600:
+	default:
+		cflag |= B9600;
+		break;
+	}
+	switch(bits) {
+	case 7:
+		cflag |= CS7;
+		break;
+	default:
+	case 8:
+		cflag |= CS8;
+		break;
+	}
+	switch(parity) {
+	case 'o': case 'O':
+		cflag |= PARODD;
+		break;
+	case 'e': case 'E':
+		cflag |= PARENB;
+		break;
+	}
+	co->cflag = cflag;
 
-	register_console(zs_console_print);
-	printk("zs%d: console I/O\n", channel);
+	return 0;
 }
 
+static struct console sercons = {
+	"ttyS",
+	serial_console_write,
+	NULL,
+	serial_console_device,
+	serial_console_wait_key,
+	NULL,
+	serial_console_setup,
+	CON_PRINTBUFFER,
+	-1,
+	0,
+	NULL
+};
+
+/*
+ *	Register console.
+ */
+__initfunc (long serial_console_init(long kmem_start, long kmem_end))
+{
+	register_console(&sercons);
+	return kmem_start;
+}
+#endif /* ifdef CONFIG_SERIAL_CONSOLE */
+#ifdef CONFIG_KGDB
+/* These are for receiving and sending characters under the kgdb
+ * source level kernel debugger.
+ */
+void putDebugChar(char kgdb_char)
+{
+	struct mac_zschannel *chan = zs_kgdbchan;
+	while ((read_zsreg(chan, 0) & Tx_BUF_EMP) == 0)
+		udelay(5);
+	write_zsdata(chan, kgdb_char);
+}
+char getDebugChar(void)
+{
+	struct mac_zschannel *chan = zs_kgdbchan;
+	while((read_zsreg(chan, 0) & Rx_CH_AV) == 0)
+		eieio(); /*barrier();*/
+	return read_zsdata(chan);
+}
+void kgdb_interruptible(int yes)
+{
+	struct mac_zschannel *chan = zs_kgdbchan;
+	int one, nine;
+	nine = read_zsreg(chan, 9);
+	if (yes == 1) {
+		one = EXT_INT_ENAB|INT_ALL_Rx;
+		nine |= MIE;
+		printk("turning serial ints on\n");
+	} else {
+		one = RxINT_DISAB;
+		nine &= ~MIE;
+		printk("turning serial ints off\n");
+	}
+	write_zsreg(chan, 1, one);
+	write_zsreg(chan, 9, nine);
+}
+/* This sets up the serial port we're using, and turns on
+ * interrupts for that channel, so kgdb is usable once we're done.
+ */
+static inline void kgdb_chaninit(struct mac_zschannel *ms, int intson, int bps)
+{
+	int brg;
+	int i, x;
+	volatile char *sccc = ms->control;
+	brg = BPS_TO_BRG(bps, ZS_CLOCK/16);
+	printk("setting bps on kgdb line to %d [brg=%x]\n", bps, brg);
+	for (i = 20000; i != 0; --i) {
+		x = *sccc; eieio();
+	}
+	for (i = 0; i < sizeof(scc_inittab); ++i) {
+		write_zsreg(ms, scc_inittab[i], scc_inittab[i+1]);
+		i++;
+	}
+}
 /* This is called at boot time to prime the kgdb serial debugging
- * serial line.  The 'tty_num' argument is 0 for /dev/ttyS0 and 1
- * for /dev/ttyS1 which is determined in setup_arch() from the
+ * serial line.  The 'tty_num' argument is 0 for /dev/ttya and 1
+ * for /dev/ttyb which is determined in setup_arch() from the
  * boot command line flags.
  */
-void
-rs_kgdb_hook(int tty_num)
+__initfunc(void zs_kgdb_hook(int tty_num))
 {
+	/* Find out how many Z8530 SCCs we have */
 	if (zs_chain == 0)
 		probe_sccs();
+	zs_soft[tty_num].zs_channel = &zs_channels[tty_num];
 	zs_kgdbchan = zs_soft[tty_num].zs_channel;
+	zs_soft[tty_num].change_needed = 0;
 	zs_soft[tty_num].clk_divisor = 16;
-	zs_soft[tty_num].zs_baud = get_zsbaud(&zs_soft[tty_num]);
+	zs_soft[tty_num].zs_baud = 38400;
 	zs_soft[tty_num].kgdb_channel = 1;     /* This runs kgdb */
 	zs_soft[tty_num ^ 1].kgdb_channel = 0; /* This does not */
 	/* Turn on transmitter/receiver at 8-bits/char */
-	kgdb_chaninit(&zs_soft[tty_num], 0, 9600);
-	ZS_CLEARERR(zs_kgdbchan);
-	ZS_CLEARFIFO(zs_kgdbchan);
+        kgdb_chaninit(zs_soft[tty_num].zs_channel, 1, 38400);
+	printk("KGDB: on channel %d initialized\n", tty_num);
+	set_debug_traps(); /* init stub */
 }
+#endif /* ifdef CONFIG_KGDB */
diff -u --recursive --new-file v2.1.96/linux/drivers/macintosh/mediabay.c linux/drivers/macintosh/mediabay.c
--- v2.1.96/linux/drivers/macintosh/mediabay.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/macintosh/mediabay.c	Tue Apr 14 17:33:59 1998
@@ -0,0 +1,236 @@
+/*
+ * Driver for the media bay on the PowerBook 3400 and 2400.
+ *
+ * Copyright (C) 1998 Paul Mackerras.
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version
+ *  2 of the License, or (at your option) any later version.
+ */
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/hdreg.h>
+#include <asm/prom.h>
+#include <asm/pgtable.h>
+#include <asm/io.h>
+#include <asm/ohare.h>
+#include <asm/mediabay.h>
+
+struct media_bay_hw {
+	unsigned char	b0;
+	unsigned char	contents;
+	unsigned char	b2;
+	unsigned char	b3;
+	unsigned	feature;
+};
+
+static volatile struct media_bay_hw *mb_addr;
+
+#define MB_CONTENTS()	((in_8(&mb_addr->contents) >> 4) & 7)
+#define SET_FEATURES(set, clr) \
+	out_le32(&mb_addr->feature, \
+		 (in_le32(&mb_addr->feature) & ~(clr)) | (set));
+
+static int media_bay_id = -1;
+static int mb_ready;
+static int mb_last_value;
+static int mb_value_count;
+
+int media_bay_present;
+
+#ifdef CONFIG_BLK_DEV_IDE
+unsigned long mb_cd_base;
+int mb_cd_index = -1;
+int mb_cd_irq;
+
+/* check the busy bit in the media-bay ide interface
+   (assumes the media-bay contains an ide device) */
+#define MB_IDE_READY()	((in_8((volatile unsigned char *) \
+			       (mb_cd_base + 0x70)) & 0x80) == 0)
+#endif
+
+/*
+ * Consider the media-bay ID value stable if it is the same for
+ * this many consecutive samples (at intervals of 1/HZ seconds).
+ */
+#define MB_STABLE_COUNT	4
+
+/*
+ * Hold the media-bay reset signal true for this many ticks
+ * after a device is inserted before releasing it.
+ */
+#define MB_RESET_COUNT	10
+
+/*
+ * Wait this many ticks after an IDE device (e.g. CD-ROM) is inserted
+ * (or until the device is ready) before registering the IDE interface.
+ */
+#define MB_IDE_WAIT	500
+
+static void poll_media_bay(void);
+static void set_media_bay(int id);
+
+/*
+ * It seems that the bit for the media-bay interrupt in the IRQ_LEVEL
+ * register is always set when there is something in the media bay.
+ * This causes problems for the interrupt code if we attach an interrupt
+ * handler to the media-bay interrupt, because it tends to go into
+ * an infinite loop calling the media bay interrupt handler.
+ * Therefore we do it all by polling the media bay once each tick.
+ */
+
+void
+media_bay_init(void)
+{
+	struct device_node *np;
+
+	np = find_devices("media-bay");
+	if (np == NULL || np->n_addrs == 0)
+		return;
+	mb_addr = (volatile struct media_bay_hw *)
+		ioremap(np->addrs[0].address, sizeof(struct media_bay_hw));
+
+#if 0
+	if (np->n_intrs == 0) {
+		printk(KERN_WARNING "No interrupt for media bay?\n");
+	} else {
+		if (request_irq(np->intrs[0].line, media_bay_intr, 0,
+				"Media bay", NULL))
+			printk(KERN_WARNING "Couldn't get IRQ %d for "
+			       "media bay\n", np->intrs[0].line);
+	}
+#endif
+
+	media_bay_present = 1;
+	set_media_bay(MB_CONTENTS());
+	if (media_bay_id != MB_NO) {
+		SET_FEATURES(0, OH_BAY_RESET);
+		mb_ready = 1;
+	}
+}
+
+#if 0
+static void
+media_bay_intr(int irq, void *devid, struct pt_regs *regs)
+{
+	int id = MB_CONTENTS();
+
+	if (id == MB_NO)
+		set_media_bay(id);
+}
+#endif
+
+int
+check_media_bay(int what)
+{
+	return what == media_bay_id && mb_ready;
+}
+
+/*
+ * This procedure runs as a kernel thread to poll the media bay
+ * once each tick and register and unregister the IDE interface
+ * with the IDE driver.  It needs to be a thread because
+ * ide_register can't be called from interrupt context.
+ */
+int
+media_bay_task(void *x)
+{
+	int prev = media_bay_id;
+	int reset_timer = 0;
+#ifdef CONFIG_BLK_DEV_IDE
+	int cd_timer = 0;
+#endif
+
+	strcpy(current->comm, "media-bay");
+	for (;;) {
+		poll_media_bay();
+		if (media_bay_id != prev) {
+			reset_timer = (media_bay_id != MB_NO)?
+				MB_RESET_COUNT: 0;
+			mb_ready = 0;
+#ifdef CONFIG_BLK_DEV_IDE
+			cd_timer = 0;
+			if (media_bay_id != MB_CD && mb_cd_index >= 0) {
+				printk(KERN_DEBUG "Unregistering mb ide\n");
+				ide_unregister(mb_cd_index);
+				mb_cd_index = -1;
+			}
+#endif
+		} else if (reset_timer) {
+			if (--reset_timer == 0) {
+				SET_FEATURES(0, OH_BAY_RESET);
+				mb_ready = 1;
+#ifdef CONFIG_BLK_DEV_IDE
+				if (media_bay_id == MB_CD && mb_cd_base != 0)
+					cd_timer = MB_IDE_WAIT;
+#endif
+			}
+#ifdef CONFIG_BLK_DEV_IDE
+		} else if (cd_timer && (--cd_timer == 0 || MB_IDE_READY())
+			   && mb_cd_index < 0) {
+			mb_cd_index = ide_register(mb_cd_base, 0, mb_cd_irq);
+			printk(KERN_DEBUG "media-bay is ide %d\n", mb_cd_index);
+#endif
+		}
+
+		prev = media_bay_id;
+		current->state = TASK_INTERRUPTIBLE;
+		current->timeout = jiffies + 1;
+		schedule();
+		if (signal_pending(current))
+			return 0;
+	}
+}
+
+void
+poll_media_bay(void)
+{
+	int id = MB_CONTENTS();
+
+	if (id == mb_last_value) {
+		if (id != media_bay_id
+		    && ++mb_value_count >= MB_STABLE_COUNT)
+			set_media_bay(id);
+	} else {
+		mb_last_value = id;
+		mb_value_count = 0;
+	}
+}
+
+static void
+set_media_bay(int id)
+{
+	u32 clr, set;
+
+	media_bay_id = id;
+	mb_last_value = id;
+	clr = OH_FLOPPY_ENABLE | OH_IDECD_POWER;
+	set = 0;
+	switch (id) {
+	case MB_CD:
+		set = OH_BAY_ENABLE | OH_IDECD_POWER | OH_BAY_IDE_ENABLE;
+		printk(KERN_INFO "media bay contains a CD-ROM drive\n");
+		break;
+	case MB_FD:
+		set = OH_BAY_ENABLE | OH_BAY_FLOPPY_ENABLE | OH_FLOPPY_ENABLE;
+		printk(KERN_INFO "media bay contains a floppy disk drive\n");
+		break;
+	case MB_NO:
+		printk(KERN_INFO "media bay is empty\n");
+		break;
+	default:
+		set = OH_BAY_ENABLE;
+		printk(KERN_INFO "media bay contains an unknown device (%d)\n",
+		       id);
+		break;
+	}
+
+	SET_FEATURES(set, clr);
+	printk(KERN_DEBUG "feature reg now %x\n", in_le32(&mb_addr->feature));
+}
diff -u --recursive --new-file v2.1.96/linux/drivers/macintosh/platinum.c linux/drivers/macintosh/platinum.c
--- v2.1.96/linux/drivers/macintosh/platinum.c	Sat Aug 16 09:53:08 1997
+++ linux/drivers/macintosh/platinum.c	Tue Apr 14 17:33:59 1998
@@ -16,6 +16,7 @@
 #include <linux/nvram.h>
 #include <asm/prom.h>
 #include <asm/io.h>
+#include <asm/pgtable.h>
 #include <linux/selection.h>
 #include "pmac-cons.h"
 #include "platinum.h"
@@ -58,6 +59,10 @@
 static struct cmap_regs *cmap_regs;
 static volatile struct platinum_regs *plat_regs;
 
+static unsigned long frame_buffer_phys;
+static unsigned long cmap_regs_phys;
+static unsigned long plat_regs_phys;
+
 /*
  * Register initialization tables for the platinum display.
  *
@@ -403,14 +408,17 @@
 		size = dp->addrs[i].size;
 		if (size >= 0x400000) {
 			/* frame buffer - map only 4MB */
-			frame_buffer = ioremap(addr, 0x400000);
+			frame_buffer_phys = addr;
+			frame_buffer = __ioremap(addr, 0x400000, _PAGE_WRITETHRU);
 			base_frame_buffer = frame_buffer;
 		} else {
 			/* registers */
+			plat_regs_phys = addr;
 			plat_regs = ioremap(addr, size);
 		}
 	}
-	cmap_regs = ioremap(0xf301b000, 0x1000);	/* XXX not in prom? */
+	cmap_regs_phys = 0xf301b000;	/* XXX not in prom? */
+	cmap_regs = ioremap(cmap_regs_phys, 0x1000);
 
 	/* Grok total video ram */
 	plat_regs->reg[16].r = (unsigned)frame_buffer;
@@ -560,10 +568,10 @@
 	display_info.pitch = line_pitch;
 	display_info.mode = video_mode;
 	strncpy(display_info.name, "platinum", sizeof(display_info.name));
-	display_info.fb_address = (unsigned long) frame_buffer + 0x10;
-	display_info.cmap_adr_address = (unsigned long) &cmap_regs->addr;
-	display_info.cmap_data_address = (unsigned long) &cmap_regs->lut;
-	display_info.disp_reg_address = (unsigned long) &plat_regs;
+	display_info.fb_address = frame_buffer_phys + init->fb_offset + 0x10;
+	display_info.cmap_adr_address = cmap_regs_phys;
+	display_info.cmap_data_address = cmap_regs_phys + 0x30;
+	display_info.disp_reg_address = plat_regs_phys;
 }
 
 int
diff -u --recursive --new-file v2.1.96/linux/drivers/macintosh/pmac-cons.c linux/drivers/macintosh/pmac-cons.c
--- v2.1.96/linux/drivers/macintosh/pmac-cons.c	Mon Jan 12 15:18:14 1998
+++ linux/drivers/macintosh/pmac-cons.c	Tue Apr 14 17:33:59 1998
@@ -26,6 +26,7 @@
 #include <linux/selection.h>
 #include <linux/console_struct.h>
 #include <linux/vt_kern.h>
+#include "../char/console_macros.h"
 #include "pmac-cons.h"
 #include "control.h"
 #include "platinum.h"
@@ -95,6 +96,7 @@
 	{0x72d, VMODE_832_624_75},	/* 16" RGB (Goldfish) */
 	{0x730, VMODE_768_576_50I},	/* PAL (Alternate) */
 	{0x73a, VMODE_1152_870_75},	/* 3rd party 19" */
+	{0x73f, VMODE_640_480_67},	/* no sense lines connected at all */
 	{-1,	VMODE_640_480_60},	/* catch-all, must be last */
 };
 
@@ -183,12 +185,22 @@
 	  aty_setmode, aty_set_palette, aty_set_blanking },
  	{ "ATY,XCLAIMVR", map_aty_display, aty_init,
 	  aty_setmode, aty_set_palette, aty_set_blanking },
- 	{ "ATY,RAGEII_M", map_aty_display, aty_init, // untested!!
+#if 0 /* problematic */
+ 	{ "ATY,RAGEII_M", map_aty_display, aty_init,
+	  aty_setmode, aty_set_palette, aty_set_blanking },
+#endif
+	{ "ATY,XCLAIMVRPro", map_aty_display, aty_init,
+	  aty_setmode, aty_set_palette, aty_set_blanking },
+ 	{ "ATY,mach64_3DU", map_aty_display, aty_init,
 	  aty_setmode, aty_set_palette, aty_set_blanking },
 #endif
 #ifdef CONFIG_IMSTT_VIDEO
-	{ "IMS,tt128mb", map_imstt_display, imstt_init, 
-	  imstt_setmode, imstt_set_palette, imstt_set_blanking },
+	{ "IMS,tt128mb", map_imstt_display_ibm, imstt_init, 
+	  imstt_setmode, imstt_set_palette_ibm, imstt_set_blanking },
+	{ "IMS,tt128mb8", map_imstt_display_tvp, imstt_init, 
+	  imstt_setmode, imstt_set_palette_tvp, imstt_set_blanking },
+	{ "IMS,tt128mb8A", map_imstt_display_tvp, imstt_init, 
+	  imstt_setmode, imstt_set_palette_tvp, imstt_set_blanking },
 #endif
 	{ NULL }
 };
@@ -593,6 +605,16 @@
 }
 
 int
+console_setcmap(int n_entries, unsigned char *red,
+		unsigned char *green, unsigned char *blue)
+{
+	if (current_display == NULL || current_display->set_palette == NULL)
+		return -EOPNOTSUPP;
+	(*current_display->set_palette)(red, green, blue, 0, n_entries);
+	return 0;
+}
+
+int
 console_powermode(int mode)
 {
 	if (mode == VC_POWERMODE_INQUIRY)
@@ -600,7 +622,7 @@
 	if (mode < VESA_NO_BLANKING || mode > VESA_POWERDOWN)
 		return -EINVAL;
 	if (current_display == NULL || current_display->set_blanking == NULL)
-		return -ENXIO;
+		return mode == VESA_NO_BLANKING? 0: -ENXIO;
 	(*current_display->set_blanking)(mode);
 	vesa_blanked = mode;
 	return 0;
@@ -890,6 +912,7 @@
 static unsigned char *frame_buffer;
 static unsigned char *unknown_cmap_adr;
 static volatile unsigned char *unknown_cmap_data;
+static unsigned long frame_buffer_phys;
 
 static int map_unknown(struct device_node *dp)
 {
@@ -938,7 +961,8 @@
 		address = dp->addrs[i].address;
 	}
 	printk(KERN_INFO "%s: using address %x\n", dp->full_name, address);
-	frame_buffer = ioremap(address, len);
+	frame_buffer_phys = address;
+	frame_buffer = __ioremap(frame_buffer_phys, len, _PAGE_WRITETHRU);
 
 	video_mode = 0;
 	color_mode = CMODE_8;
@@ -961,15 +985,15 @@
 	display_info.pitch = line_pitch;
 	display_info.mode = video_mode;
 	strncpy(display_info.name, dp->name, sizeof(display_info.name));
-	display_info.fb_address = (unsigned long) frame_buffer;
+	display_info.fb_address = frame_buffer_phys;
 	display_info.cmap_adr_address = 0;
 	display_info.cmap_data_address = 0;
 	unknown_cmap_adr = 0;
 	/* XXX kludge for ati */
 	if (strncmp(dp->name, "ATY,", 4) == 0) {
-		display_info.disp_reg_address = address + 0x7ffc00;
-		display_info.cmap_adr_address = address + 0x7ffcc0;
-		display_info.cmap_data_address = address + 0x7ffcc1;
+		display_info.disp_reg_address = frame_buffer_phys + 0x7ffc00;
+		display_info.cmap_adr_address = frame_buffer_phys + 0x7ffcc0;
+		display_info.cmap_data_address = frame_buffer_phys + 0x7ffcc1;
 		unknown_cmap_adr = ioremap(address + 0x7ff000, 0x1000) + 0xcc0;
 		unknown_cmap_data = unknown_cmap_adr + 1;
 	}
diff -u --recursive --new-file v2.1.96/linux/drivers/macintosh/valkyrie.c linux/drivers/macintosh/valkyrie.c
--- v2.1.96/linux/drivers/macintosh/valkyrie.c	Mon Jan 12 15:18:14 1998
+++ linux/drivers/macintosh/valkyrie.c	Tue Apr 14 17:33:59 1998
@@ -18,6 +18,7 @@
 #include <asm/io.h>
 #include <asm/adb.h>
 #include <asm/cuda.h>
+#include <asm/pgtable.h>
 #include <linux/selection.h>
 #include "pmac-cons.h"
 #include "valkyrie.h"
@@ -62,6 +63,10 @@
 static struct cmap_regs *cmap_regs;
 static struct valkyrie_regs *disp_regs;
 
+static unsigned long frame_buffer_phys;
+static unsigned long disp_regs_phys;
+static unsigned long cmap_regs_phys;
+
 /*
  * Register initialization tables for the valkyrie display.
  *
@@ -161,7 +166,7 @@
 	__delay(20000);
 	sense |= (in_8(&disp_regs->msense) & 0x60) >> 5;
 
-	out_8(&disp_regs->msense, 7);
+	out_8(&disp_regs->msense, 0);
 	return sense;
 }
 
@@ -178,9 +183,12 @@
 
 	/* Map in frame buffer and registers */
 	addr = dp->addrs[0].address;
-	frame_buffer = ioremap(addr, 0x100000);
-	disp_regs = ioremap(addr + 0x30a000, 4096);
-	cmap_regs = ioremap(addr + 0x304000, 4096);
+	frame_buffer_phys = addr;
+	frame_buffer = __ioremap(addr, 0x100000, _PAGE_WRITETHRU);
+	disp_regs_phys = addr + 0x30a000;
+	disp_regs = ioremap(disp_regs_phys, 4096);
+	cmap_regs_phys = addr + 0x304000;
+	cmap_regs = ioremap(cmap_regs_phys, 4096);
 
 	/* Read the monitor sense value and choose the video mode */
 	sense = read_valkyrie_sense();
@@ -266,10 +274,10 @@
 	display_info.pitch = line_pitch;
 	display_info.mode = video_mode;
 	strncpy(display_info.name, "valkyrie", sizeof(display_info.name));
-	display_info.fb_address = (unsigned long) frame_buffer + 0x1000;
-	display_info.cmap_adr_address = (unsigned long) &cmap_regs->addr;
-	display_info.cmap_data_address = (unsigned long) &cmap_regs->lut;
-	display_info.disp_reg_address = (unsigned long) &disp_regs;
+	display_info.fb_address = frame_buffer_phys + 0x1000;
+	display_info.cmap_adr_address = cmap_regs_phys;
+	display_info.cmap_data_address = cmap_regs_phys + 8;
+	display_info.disp_reg_address = disp_regs_phys;
 }
 
 int
diff -u --recursive --new-file v2.1.96/linux/drivers/macintosh/via-cuda.c linux/drivers/macintosh/via-cuda.c
--- v2.1.96/linux/drivers/macintosh/via-cuda.c	Mon Jan 12 15:18:14 1998
+++ linux/drivers/macintosh/via-cuda.c	Tue Apr 14 17:33:59 1998
@@ -18,6 +18,7 @@
 #include <asm/adb.h>
 #include <asm/cuda.h>
 #include <asm/io.h>
+#include <asm/pgtable.h>
 #include <asm/system.h>
 
 static volatile unsigned char *via;
@@ -98,7 +99,7 @@
 	printk(" %x(%x)", vias->addrs[i].address, vias->addrs[i].size);
     printk(", intrs =");
     for (i = 0; i < vias->n_intrs; ++i)
-	printk(" %x", vias->intrs[i]);
+	printk(" %x", vias->intrs[i].line);
     printk("\n"); }
 #endif
 
@@ -108,7 +109,7 @@
 	if (vias->n_addrs < 1 || vias->n_intrs < 1)
 	    return;
     }
-    via = (volatile unsigned char *) vias->addrs->address;
+    via = (volatile unsigned char *) ioremap(vias->addrs->address, 0x2000);
 
     if (!init_via()) {
 	printk(KERN_ERR "init_via failed\n");
@@ -117,8 +118,8 @@
 
     cuda_state = idle;
 
-    if (request_irq(vias->intrs[0], via_interrupt, 0, "VIA", (void *)0)) {
-	printk(KERN_ERR "VIA: can't get irq %d\n", vias->intrs[0]);
+    if (request_irq(vias->intrs[0].line, via_interrupt, 0, "VIA", (void *)0)) {
+	printk(KERN_ERR "VIA: can't get irq %d\n", vias->intrs[0].line);
 	return;
     }
 
@@ -238,6 +239,14 @@
 {
     unsigned long flags;
 
+    if (via == NULL) {
+	req->complete = 1;
+	return -ENXIO;
+    }
+    if (req->nbytes < 2 || req->data[0] > CUDA_PACKET) {
+	req->complete = 1;
+	return -EINVAL;
+    }
     req->next = 0;
     req->sent = 0;
     req->complete = 0;
@@ -388,6 +397,17 @@
 	if (reading_reply) {
 	    req = current_req;
 	    req->reply_len = reply_ptr - req->reply;
+	    if (req->data[0] == ADB_PACKET) {
+		/* Have to adjust the reply from ADB commands */
+		if (req->reply_len <= 2 || (req->reply[1] & 2) != 0) {
+		    /* the 0x2 bit indicates no response */
+		    req->reply_len = 0;
+		} else {
+		    /* leave just the command and result bytes in the reply */
+		    req->reply_len -= 2;
+		    memmove(req->reply, req->reply + 2, req->reply_len);
+		}
+	    }
 	    req->complete = 1;
 	    current_req = req->next;
 	    if (req->done)
diff -u --recursive --new-file v2.1.96/linux/drivers/macintosh/via-pmu.c linux/drivers/macintosh/via-pmu.c
--- v2.1.96/linux/drivers/macintosh/via-pmu.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/macintosh/via-pmu.c	Tue Apr 14 17:33:59 1998
@@ -0,0 +1,669 @@
+/*
+ * Device driver for the via-pmu on Apple Powermacs.
+ *
+ * The VIA (versatile interface adapter) interfaces to the PMU,
+ * a 6805 microprocessor core whose primary function is to control
+ * battery charging and system power on the PowerBook 3400 and 2400.
+ * The PMU also controls the ADB (Apple Desktop Bus) which connects
+ * to the keyboard and mouse, as well as the non-volatile RAM
+ * and the RTC (real time clock) chip.
+ *
+ * Copyright (C) 1998 Paul Mackerras and Fabio Riccardi.
+ */
+#include <stdarg.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <asm/prom.h>
+#include <asm/adb.h>
+#include <asm/pmu.h>
+#include <asm/cuda.h>
+#include <asm/io.h>
+#include <asm/pgtable.h>
+#include <asm/system.h>
+
+static volatile unsigned char *via;
+
+/* VIA registers - spaced 0x200 bytes apart */
+#define RS		0x200		/* skip between registers */
+#define B		0		/* B-side data */
+#define A		RS		/* A-side data */
+#define DIRB		(2*RS)		/* B-side direction (1=output) */
+#define DIRA		(3*RS)		/* A-side direction (1=output) */
+#define T1CL		(4*RS)		/* Timer 1 ctr/latch (low 8 bits) */
+#define T1CH		(5*RS)		/* Timer 1 counter (high 8 bits) */
+#define T1LL		(6*RS)		/* Timer 1 latch (low 8 bits) */
+#define T1LH		(7*RS)		/* Timer 1 latch (high 8 bits) */
+#define T2CL		(8*RS)		/* Timer 2 ctr/latch (low 8 bits) */
+#define T2CH		(9*RS)		/* Timer 2 counter (high 8 bits) */
+#define SR		(10*RS)		/* Shift register */
+#define ACR		(11*RS)		/* Auxiliary control register */
+#define PCR		(12*RS)		/* Peripheral control register */
+#define IFR		(13*RS)		/* Interrupt flag register */
+#define IER		(14*RS)		/* Interrupt enable register */
+#define ANH		(15*RS)		/* A-side data, no handshake */
+
+/* Bits in B data register: both active low */
+#define TACK		0x08		/* Transfer acknowledge (input) */
+#define TREQ		0x10		/* Transfer request (output) */
+
+/* Bits in ACR */
+#define SR_CTRL		0x1c		/* Shift register control bits */
+#define SR_EXT		0x0c		/* Shift on external clock */
+#define SR_OUT		0x10		/* Shift out if 1 */
+
+/* Bits in IFR and IER */
+#define IER_SET		0x80		/* set bits in IER */
+#define IER_CLR		0		/* clear bits in IER */
+#define SR_INT		0x04		/* Shift register full/empty */
+#define CB1_INT		0x10		/* transition on CB1 input */
+
+static enum pmu_state {
+	idle,
+	sending,
+	intack,
+	reading,
+	reading_intr,
+} pmu_state;
+
+static struct adb_request *current_req;
+static struct adb_request *last_req;
+static struct adb_request *req_awaiting_reply;
+static unsigned char interrupt_data[32];
+static unsigned char *reply_ptr;
+static int data_index;
+static int data_len;
+static int adb_int_pending;
+static int pmu_adb_flags;
+static int adb_dev_map = 0;
+static struct adb_request bright_req_1, bright_req_2;
+
+static int init_pmu(void);
+static int pmu_queue_request(struct adb_request *req);
+static void pmu_start(void);
+static void via_pmu_interrupt(int irq, void *arg, struct pt_regs *regs);
+static int pmu_adb_send_request(struct adb_request *req, int sync);
+static int pmu_adb_autopoll(int on);
+static void send_byte(int x);
+static void recv_byte(void);
+static void pmu_sr_intr(struct pt_regs *regs);
+static void pmu_done(struct adb_request *req);
+static void pmu_handle_data(unsigned char *data, int len,
+			    struct pt_regs *regs);
+static void set_brightness(int level);
+static void set_volume(int level);
+
+/*
+ * This table indicates for each PMU opcode:
+ * - the number of data bytes to be sent with the command, or -1
+ *   if a length byte should be sent,
+ * - the number of response bytes which the PMU will return, or
+ *   -1 if it will send a length byte.
+ */
+static s8 pmu_data_len[256][2] = {
+/*	   0	   1	   2	   3	   4	   5	   6	   7  */
+/*00*/	{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},
+/*08*/	{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},
+/*10*/	{ 1, 0},{ 1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},
+/*18*/	{ 0, 1},{ 0, 1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{ 0, 0},
+/*20*/	{-1, 0},{ 0, 0},{ 2, 0},{ 1, 0},{ 1, 0},{-1, 0},{-1, 0},{-1, 0},
+/*28*/	{ 0,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},
+/*30*/	{ 4, 0},{20, 0},{ 2, 0},{ 3, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},
+/*38*/	{ 0, 4},{ 0,20},{ 1, 1},{ 2, 1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},
+/*40*/	{ 1, 0},{ 1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},
+/*48*/	{ 0, 1},{ 0, 1},{-1,-1},{-1,-1},{ 1, 0},{-1,-1},{-1,-1},{-1,-1},
+/*50*/	{ 1, 0},{ 0, 0},{ 2, 0},{ 2, 0},{-1, 0},{ 1, 0},{ 3, 0},{ 1, 0},
+/*58*/	{ 0, 1},{ 1, 0},{ 0, 2},{ 0, 2},{ 0,-1},{-1,-1},{-1,-1},{-1,-1},
+/*60*/	{ 2, 0},{-1, 0},{ 2, 0},{ 0, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},
+/*68*/	{ 0, 3},{ 0, 3},{ 0, 2},{ 0, 8},{ 0,-1},{ 0,-1},{-1,-1},{-1,-1},
+/*70*/	{ 1, 0},{ 1, 0},{ 1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},
+/*78*/	{ 0,-1},{ 0,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{ 4, 1},{ 4, 1},
+/*80*/	{ 4, 0},{-1, 0},{ 0, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},
+/*88*/	{ 0, 5},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},
+/*90*/	{ 1, 0},{ 2, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},
+/*98*/	{ 0, 1},{ 0, 1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},
+/*a0*/	{ 2, 0},{ 2, 0},{ 2, 0},{ 4, 0},{-1, 0},{ 0, 0},{-1, 0},{-1, 0},
+/*a8*/	{ 1, 1},{ 1, 0},{ 3, 0},{ 2, 0},{-1,-1},{-1,-1},{-1,-1},{-1,-1},
+/*b0*/	{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},
+/*b8*/	{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},
+/*c0*/	{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},
+/*c8*/	{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},
+/*d0*/	{ 0, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},
+/*d8*/	{ 1, 1},{ 1, 1},{-1,-1},{-1,-1},{ 0, 1},{ 0,-1},{-1,-1},{-1,-1},
+/*e0*/	{-1, 0},{ 4, 0},{ 0, 1},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},
+/*e8*/	{ 3,-1},{-1,-1},{ 0, 1},{-1,-1},{ 0,-1},{-1,-1},{-1,-1},{ 0, 0},
+/*f0*/	{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},
+/*f8*/	{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},
+};
+
+void
+via_pmu_init()
+{
+	struct device_node *vias;
+
+	vias = find_devices("via-pmu");
+	if (vias == 0)
+		return;
+	if (vias->next != 0)
+		printk(KERN_WARNING "Warning: only using 1st via-pmu\n");
+
+#if 0
+	{ int i;
+
+	printk("via_pmu_init: node = %p, addrs =", vias->node);
+	for (i = 0; i < vias->n_addrs; ++i)
+		printk(" %x(%x)", vias->addrs[i].address, vias->addrs[i].size);
+	printk(", intrs =");
+	for (i = 0; i < vias->n_intrs; ++i)
+		printk(" %x", vias->intrs[i].line);
+	printk("\n"); }
+#endif
+
+	if (vias->n_addrs != 1 || vias->n_intrs != 1) {
+		printk(KERN_ERR "via-pmu: %d addresses, %d interrupts!\n",
+		       vias->n_addrs, vias->n_intrs);
+		if (vias->n_addrs < 1 || vias->n_intrs < 1)
+			return;
+	}
+	via = (volatile unsigned char *) ioremap(vias->addrs->address, 0x2000);
+
+	out_8(&via[IER], IER_CLR | 0x7f);	/* disable all intrs */
+
+	pmu_state = idle;
+
+	if (!init_pmu())
+		return;
+
+	if (request_irq(vias->intrs[0].line, via_pmu_interrupt, 0, "VIA-PMU",
+			(void *)0)) {
+		printk(KERN_ERR "VIA-PMU: can't get irq %d\n",
+		       vias->intrs[0].line);
+		return;
+	}
+
+	/* Enable interrupts */
+	out_8(&via[IER], IER_SET | SR_INT | CB1_INT);
+
+	/* Set function pointers */
+	adb_hardware = ADB_VIAPMU;
+	adb_send_request = pmu_adb_send_request;
+	adb_autopoll = pmu_adb_autopoll;
+
+	bright_req_1.complete = 1;
+	bright_req_2.complete = 1;
+}
+
+static int
+init_pmu()
+{
+	int timeout;
+	struct adb_request req;
+
+	out_8(&via[B], via[B] | TREQ);			/* negate TREQ */
+	out_8(&via[DIRB], (via[DIRB] | TREQ) & ~TACK);	/* TACK in, TREQ out */
+
+	pmu_request(&req, NULL, 2, PMU_SET_INTR_MASK, 0xff);
+	timeout = 100000;
+	while (!req.complete) {
+		if (--timeout < 0) {
+			printk(KERN_ERR "init_pmu: no response from PMU\n");
+			return 0;
+		}
+		udelay(10);
+		pmu_poll();
+	}
+
+	/* ack all pending interrupts */
+	timeout = 100000;
+	interrupt_data[0] = 1;
+	while (interrupt_data[0] || pmu_state != idle) {
+		if (--timeout < 0) {
+			printk(KERN_ERR "init_pmu: timed out acking intrs\n");
+			return 0;
+		}
+		if (pmu_state == idle)
+			adb_int_pending = 1;
+		via_pmu_interrupt(0, 0, 0);
+		udelay(10);
+	}
+
+	return 1;
+}
+
+/* Send an ADB command */
+static int
+pmu_adb_send_request(struct adb_request *req, int sync)
+{
+	int i;
+
+	for (i = req->nbytes - 1; i > 0; --i)
+		req->data[i+3] = req->data[i];
+	req->data[3] = req->nbytes - 1;
+	req->data[2] = pmu_adb_flags;
+	req->data[1] = req->data[0];
+	req->data[0] = PMU_ADB_CMD;
+	req->nbytes += 3;
+	req->reply_expected = 1;
+	req->reply_len = 0;
+	i = pmu_queue_request(req);
+	if (i)
+		return i;
+	if (sync) {
+		while (!req->complete)
+			pmu_poll();
+	}
+	return 0;
+}
+
+/* Enable/disable autopolling */
+static int
+pmu_adb_autopoll(int on)
+{
+	struct adb_request req;
+
+	if (on) {
+		pmu_request(&req, NULL, 5, PMU_ADB_CMD, 0, 0x86,
+			    adb_dev_map >> 8, adb_dev_map);
+		pmu_adb_flags = 2;
+	} else {
+		pmu_request(&req, NULL, 1, PMU_ADB_POLL_OFF);
+		pmu_adb_flags = 0;
+	}
+	while (!req.complete)
+		pmu_poll();
+	return 0;
+}
+
+/* Construct and send a pmu request */
+int
+pmu_request(struct adb_request *req, void (*done)(struct adb_request *),
+	    int nbytes, ...)
+{
+	va_list list;
+	int i;
+
+	if (nbytes < 0 || nbytes > 32) {
+		printk(KERN_ERR "pmu_request: bad nbytes (%d)\n", nbytes);
+		req->complete = 1;
+		return -EINVAL;
+	}
+	req->nbytes = nbytes;
+	req->done = done;
+	va_start(list, nbytes);
+	for (i = 0; i < nbytes; ++i)
+		req->data[i] = va_arg(list, int);
+	va_end(list);
+	if (pmu_data_len[req->data[0]][1] != 0) {
+		req->reply[0] = ADB_RET_OK;
+		req->reply_len = 1;
+	} else
+		req->reply_len = 0;
+	req->reply_expected = 0;
+	return pmu_queue_request(req);
+}
+
+/*
+ * This procedure handles requests written to /dev/adb where the
+ * first byte is CUDA_PACKET or PMU_PACKET.  For CUDA_PACKET, we
+ * emulate a few CUDA requests.
+ */
+int
+pmu_send_request(struct adb_request *req)
+{
+	int i;
+
+	switch (req->data[0]) {
+	case PMU_PACKET:
+		for (i = 0; i < req->nbytes - 1; ++i)
+			req->data[i] = req->data[i+1];
+		--req->nbytes;
+		if (pmu_data_len[req->data[0]][1] != 0) {
+			req->reply[0] = ADB_RET_OK;
+			req->reply_len = 1;
+		} else
+			req->reply_len = 0;
+		return pmu_queue_request(req);
+	case CUDA_PACKET:
+		switch (req->data[1]) {
+		case CUDA_GET_TIME:
+			if (req->nbytes != 2)
+				break;
+			req->data[0] = PMU_READ_RTC;
+			req->nbytes = 1;
+			req->reply_len = 3;
+			req->reply[0] = CUDA_PACKET;
+			req->reply[1] = 0;
+			req->reply[2] = CUDA_GET_TIME;
+			return pmu_queue_request(req);
+		case CUDA_SET_TIME:
+			if (req->nbytes != 6)
+				break;
+			req->data[0] = PMU_SET_RTC;
+			req->nbytes = 5;
+			for (i = 1; i <= 4; ++i)
+				req->data[i] = req->data[i+1];
+			req->reply_len = 0;
+			return pmu_queue_request(req);
+		}
+		break;
+	}
+	return -EINVAL;
+}
+
+int
+pmu_queue_request(struct adb_request *req)
+{
+	unsigned long flags;
+	int nsend;
+
+	if (via == NULL) {
+		req->complete = 1;
+		return -ENXIO;
+	}
+	if (req->nbytes <= 0) {
+		req->complete = 1;
+		return 0;
+	}
+	nsend = pmu_data_len[req->data[0]][0];
+	if (nsend >= 0 && req->nbytes != nsend + 1) {
+		req->complete = 1;
+		return -EINVAL;
+	}
+
+	req->next = 0;
+	req->sent = 0;
+	req->complete = 0;
+	save_flags(flags); cli();
+
+	if (current_req != 0) {
+		last_req->next = req;
+		last_req = req;
+	} else {
+		current_req = req;
+		last_req = req;
+		if (pmu_state == idle)
+			pmu_start();
+	}
+
+	restore_flags(flags);
+	return 0;
+}
+
+static void
+send_byte(int x)
+{
+	out_8(&via[ACR], 0x1c);
+	out_8(&via[SR], x);
+	out_8(&via[B], via[B] & ~0x10);		/* assert TREQ */
+}
+
+static void
+recv_byte()
+{
+	out_8(&via[ACR], 0x0c);
+	in_8(&via[SR]);		/* resets SR */
+	out_8(&via[B], via[B] & ~0x10);
+}
+
+static void
+pmu_start()
+{
+	unsigned long flags;
+	struct adb_request *req;
+
+	/* assert pmu_state == idle */
+	/* get the packet to send */
+	save_flags(flags); cli();
+	req = current_req;
+	if (req == 0 || pmu_state != idle
+	    || (req->reply_expected && req_awaiting_reply))
+		goto out;
+
+	pmu_state = sending;
+	data_index = 1;
+	data_len = pmu_data_len[req->data[0]][0];
+
+	/* set the shift register to shift out and send a byte */
+	send_byte(req->data[0]);
+
+out:
+	restore_flags(flags);
+}
+
+void
+pmu_poll()
+{
+	int ie;
+
+	ie = _disable_interrupts();
+	if (via[IFR] & (SR_INT | CB1_INT))
+		via_pmu_interrupt(0, 0, 0);
+	_enable_interrupts(ie);
+}
+
+static void
+via_pmu_interrupt(int irq, void *arg, struct pt_regs *regs)
+{
+	int intr;
+	int nloop = 0;
+
+	while ((intr = (in_8(&via[IFR]) & (SR_INT | CB1_INT))) != 0) {
+		if (++nloop > 1000) {
+			printk(KERN_DEBUG "PMU: stuck in intr loop, "
+			       "intr=%x pmu_state=%d\n", intr, pmu_state);
+			break;
+		}
+		if (intr & SR_INT)
+			pmu_sr_intr(regs);
+		else if (intr & CB1_INT) {
+			adb_int_pending = 1;
+			out_8(&via[IFR], CB1_INT);
+		}
+	}
+	if (pmu_state == idle) {
+		if (adb_int_pending) {
+			pmu_state = intack;
+			send_byte(PMU_INT_ACK);
+			adb_int_pending = 0;
+		} else if (current_req) {
+			pmu_start();
+		}
+	}
+}
+
+static void
+pmu_sr_intr(struct pt_regs *regs)
+{
+	struct adb_request *req;
+	int bite, timeout;
+
+	if (via[B] & TACK)
+		printk(KERN_DEBUG "PMU: sr_intr but ack still high! (%x)\n",
+		       via[B]);
+
+	/* if reading grab the byte, and reset the interrupt */
+	if ((via[ACR] & SR_OUT) == 0)
+		bite = in_8(&via[SR]);
+	out_8(&via[IFR], SR_INT);
+
+	/* reset TREQ and wait for TACK to go high */
+	out_8(&via[B], via[B] | TREQ);
+	timeout = 3200;
+	while ((in_8(&via[B]) & TACK) == 0) {
+		if (--timeout < 0) {
+			printk(KERN_ERR "PMU not responding (!ack)\n");
+			return;
+		}
+		udelay(10);
+	}
+
+	switch (pmu_state) {
+	case sending:
+		req = current_req;
+		if (data_len < 0) {
+			data_len = req->nbytes - 1;
+			send_byte(data_len);
+			break;
+		}
+		if (data_index <= data_len) {
+			send_byte(req->data[data_index++]);
+			break;
+		}
+		req->sent = 1;
+		data_len = pmu_data_len[req->data[0]][1];
+		if (data_len == 0) {
+			pmu_state = idle;
+			current_req = req->next;
+			if (req->reply_expected)
+				req_awaiting_reply = req;
+			else
+				pmu_done(req);
+		} else {
+			pmu_state = reading;
+			data_index = 0;
+			reply_ptr = req->reply + req->reply_len;
+			recv_byte();
+		}
+		break;
+
+	case intack:
+		data_index = 0;
+		data_len = -1;
+		pmu_state = reading_intr;
+		reply_ptr = interrupt_data;
+		recv_byte();
+		break;
+
+	case reading:
+	case reading_intr:
+		if (data_len == -1) {
+			data_len = bite;
+			if (bite > 32)
+				printk(KERN_ERR "PMU: bad reply len %d\n",
+				       bite);
+		} else {
+			reply_ptr[data_index++] = bite;
+		}
+		if (data_index < data_len) {
+			recv_byte();
+			break;
+		}
+
+		if (pmu_state == reading_intr) {
+			pmu_handle_data(interrupt_data, data_index, regs);
+		} else {
+			req = current_req;
+			current_req = req->next;
+			req->reply_len += data_index;
+			pmu_done(req);
+		}
+		pmu_state = idle;
+
+		break;
+
+	default:
+		printk(KERN_ERR "via_pmu_interrupt: unknown state %d?\n",
+		       pmu_state);
+	}
+}
+
+static void
+pmu_done(struct adb_request *req)
+{
+	req->complete = 1;
+	if (req->done)
+		(*req->done)(req);
+}
+
+/* Interrupt data could be the result data from an ADB cmd */
+static void
+pmu_handle_data(unsigned char *data, int len, struct pt_regs *regs)
+{
+	static int show_pmu_ints = 1;
+
+	if (len < 1) {
+		adb_int_pending = 0;
+		return;
+	}
+	if (data[0] & PMU_INT_ADB) {
+		if ((data[0] & PMU_INT_ADB_AUTO) == 0) {
+			struct adb_request *req = req_awaiting_reply;
+			if (req == 0) {
+				printk(KERN_ERR "PMU: extra ADB reply\n");
+				return;
+			}
+			req_awaiting_reply = 0;
+			if (len <= 2)
+				req->reply_len = 0;
+			else {
+				memcpy(req->reply, data + 1, len - 1);
+				req->reply_len = len - 1;
+			}
+			pmu_done(req);
+		} else {
+			adb_input(data+1, len-1, regs, 1);
+		}
+	} else {
+		if (data[0] == 0x08 && len == 3) {
+			/* sound/brightness buttons pressed */
+			set_brightness(data[1]);
+			set_volume(data[2]);
+		} else if (show_pmu_ints
+			   && !(data[0] == PMU_INT_TICK && len == 1)) {
+			int i;
+			printk(KERN_DEBUG "pmu intr");
+			for (i = 0; i < len; ++i)
+				printk(" %.2x", data[i]);
+			printk("\n");
+		}
+	}
+}
+
+int backlight_bright = -1;
+int backlight_enabled = 0;
+
+#define LEVEL_TO_BRIGHT(lev)	((lev) < 8? 0x7f: 0x4a - ((lev) >> 2))
+
+void
+pmu_enable_backlight(int on)
+{
+	struct adb_request req;
+
+	if (on) {
+		if (backlight_bright < 0) {
+			pmu_request(&req, NULL, 2, 0xd9, 0);
+			while (!req.complete)
+				pmu_poll();
+			backlight_bright = LEVEL_TO_BRIGHT(req.reply[1]);
+		}
+		pmu_request(&req, NULL, 2, PMU_BACKLIGHT_BRIGHT,
+			    backlight_bright);
+		while (!req.complete)
+			pmu_poll();
+	}
+	pmu_request(&req, NULL, 2, PMU_BACKLIGHT_CTRL, on? 0x81: 1);
+	while (!req.complete)
+		pmu_poll();
+	backlight_enabled = on;
+}
+
+static void
+set_brightness(int level)
+{
+	backlight_bright = LEVEL_TO_BRIGHT(level);
+	if (bright_req_1.complete)
+		pmu_request(&bright_req_1, NULL, 2, PMU_BACKLIGHT_BRIGHT,
+			    backlight_bright);
+	if (bright_req_2.complete) {
+		backlight_enabled = backlight_bright < 0x7f;
+		pmu_request(&bright_req_2, NULL, 2, PMU_BACKLIGHT_CTRL,
+			    backlight_enabled? 0x81: 1);
+	}
+}
+
+static void
+set_volume(int level)
+{
+}
diff -u --recursive --new-file v2.1.96/linux/drivers/misc/parport_pc.c linux/drivers/misc/parport_pc.c
--- v2.1.96/linux/drivers/misc/parport_pc.c	Tue Apr 14 14:29:20 1998
+++ linux/drivers/misc/parport_pc.c	Wed Apr 15 14:38:36 1998
@@ -745,7 +745,7 @@
 static int io[PARPORT_PC_MAX_PORTS+1] = { [0 ... PARPORT_PC_MAX_PORTS] = 0 };
 static int dma[PARPORT_PC_MAX_PORTS] = { [0 ... PARPORT_PC_MAX_PORTS-1] = PARPORT_DMA_NONE };
 static int irqval[PARPORT_PC_MAX_PORTS] = { [0 ... PARPORT_PC_MAX_PORTS-1] = PARPORT_IRQ_PROBEONLY };
-static char *irq = NULL;
+static const char *irq[PARPORT_PC_MAX_PORTS] = { NULL, };
 MODULE_PARM(io, "1-" __MODULE_STRING(PARPORT_PC_MAX_PORTS) "i");
 MODULE_PARM(irq, "1-" __MODULE_STRING(PARPORT_PC_MAX_PORTS) "s");
 MODULE_PARM(dma, "1-" __MODULE_STRING(PARPORT_PC_MAX_PORTS) "i");
diff -u --recursive --new-file v2.1.96/linux/drivers/misc/parport_share.c linux/drivers/misc/parport_share.c
--- v2.1.96/linux/drivers/misc/parport_share.c	Tue Apr 14 14:29:20 1998
+++ linux/drivers/misc/parport_share.c	Fri Apr 17 22:08:36 1998
@@ -398,6 +398,7 @@
 {
 	struct parport *port = dev->port;
 	struct pardevice *pd;
+	unsigned long flags;
 
 	/* Make sure that dev is the current device */
 	if (port->cad != dev) {
@@ -405,7 +406,9 @@
 		       "when not owner\n", port->name, dev->name);
 		return;
 	}
+	spin_lock_irqsave(&port->lock, flags);
 	port->cad = NULL;
+	spin_unlock_irqrestore(&port->lock, flags);
 
 	/* Save control registers */
 	port->ops->save_state(port, dev->state);
@@ -442,25 +445,23 @@
 	}
 }
 
-void parport_parse_irqs(int nports, const char *irqstr, int irqval[])
+void parport_parse_irqs(int nports, const char *irqstr[], int irqval[])
 {
 	unsigned int i;
-	for (i = 0; i < nports && irqstr; i++) {
-		if (!strncmp(irqstr, "auto", 4))
+	for (i = 0; i < nports && irqstr[i]; i++) {
+		if (!strncmp(irqstr[i], "auto", 4))
 			irqval[i] = PARPORT_IRQ_AUTO;
-		else if (!strncmp(irqstr, "none", 4))
+		else if (!strncmp(irqstr[i], "none", 4))
 			irqval[i] = PARPORT_IRQ_NONE;
 		else {
 			char *ep;
-			unsigned long r = simple_strtoul(irqstr, &ep, 0);
-			if (ep != irqstr)
+			unsigned long r = simple_strtoul(irqstr[i], &ep, 0);
+			if (ep != irqstr[i])
 				irqval[i] = r;
 			else {
-				printk("parport: bad irq specifier `%s'\n", irqstr);
+				printk("parport: bad irq specifier `%s'\n", irqstr[i]);
 				return;
 			}
 		}
-		irqstr = strchr(irqstr, ',');
-		if (irqstr) irqstr++;
 	}
 }
diff -u --recursive --new-file v2.1.96/linux/drivers/net/Config.in linux/drivers/net/Config.in
--- v2.1.96/linux/drivers/net/Config.in	Wed Apr  8 19:36:26 1998
+++ linux/drivers/net/Config.in	Fri Apr 17 22:07:13 1998
@@ -42,7 +42,7 @@
       fi
     fi
     tristate '3c509/3c579 support' CONFIG_EL3
-    tristate '3c590 series (592/595/597) "Vortex" support' CONFIG_VORTEX
+    tristate '3c590/3c900 series (592/595/597) "Vortex/Boomerang" support' CONFIG_VORTEX
   fi
   bool 'AMD LANCE and PCnet (AT1500 and NE2100) support' CONFIG_LANCE
   bool 'Western Digital/SMC cards' CONFIG_NET_VENDOR_SMC
diff -u --recursive --new-file v2.1.96/linux/drivers/net/hp100.c linux/drivers/net/hp100.c
--- v2.1.96/linux/drivers/net/hp100.c	Mon Apr  6 17:41:00 1998
+++ linux/drivers/net/hp100.c	Fri Apr 17 22:03:57 1998
@@ -2,7 +2,7 @@
 ** hp100.c 
 ** HP CASCADE Architecture Driver for 100VG-AnyLan Network Adapters
 **
-** $Id: hp100.c,v 1.56 1998/03/04 15:23:59 perex Exp perex $
+** $Id: hp100.c,v 1.57 1998/04/10 16:27:23 perex Exp perex $
 **
 ** Based on the HP100 driver written by Jaroslav Kysela <perex@jcu.cz>
 ** Extended for new busmaster capable chipsets by 
@@ -31,10 +31,23 @@
 **       -  some updates for EISA version of card
 **
 **
-** This source/code is public free; you can distribute it and/or modify 
-** it under terms of the GNU General Public License (published by the
-** Free Software Foundation) either version two of this License, or any 
-** later version.
+**   This code is free software; you can redistribute it and/or modify
+**   it under the terms of the GNU General Public License as published by
+**   the Free Software Foundation; either version 2 of the License, or
+**   (at your option) any later version.
+**
+**   This code is distributed in the hope that it will be useful,
+**   but WITHOUT ANY WARRANTY; without even the implied warranty of
+**   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+**   GNU General Public License for more details.
+**
+**   You should have received a copy of the GNU General Public License
+**   along with this program; if not, write to the Free Software
+**   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+**
+**
+** 1.56 -> 1.57
+**   - updates for new PCI interface for 2.1 kernels
 **
 ** 1.55 -> 1.56
 **   - removed printk in misc. interrupt and update statistics to allow
@@ -94,13 +107,15 @@
 #include <linux/config.h>  /* for CONFIG_PCI */
 #include <linux/delay.h>
 
-#if LINUX_VERSION_CODE < 0x020100
+#if LINUX_VERSION_CODE >= 0x020100
+#define LINUX_2_1
+typedef struct net_device_stats hp100_stats_t;
+EXPORT_NO_SYMBOLS;
+#else
+#include <linux/bios32.h>
 #define ioremap vremap
 #define iounmap vfree
 typedef struct enet_statistics hp100_stats_t;
-#else
-#define LINUX_2_1
-typedef struct net_device_stats hp100_stats_t;
 #endif
 
 #ifndef __initfunc
@@ -135,14 +150,14 @@
 #define PCI_DEVICE_ID_COMPEX2_100VG 0x0005
 #endif
 
-#define HP100_REGION_SIZE  0x20 /* for ioports */
+#define HP100_REGION_SIZE	0x20 /* for ioports */
 
-#define HP100_MAX_PACKET_SIZE  (1536+4)
-#define HP100_MIN_PACKET_SIZE  60
+#define HP100_MAX_PACKET_SIZE	(1536+4)
+#define HP100_MIN_PACKET_SIZE	60
 
 #ifndef HP100_DEFAULT_RX_RATIO
 /* default - 75% onboard memory on the card are used for RX packets */
-#define HP100_DEFAULT_RX_RATIO  75
+#define HP100_DEFAULT_RX_RATIO	75
 #endif
 
 #ifndef HP100_DEFAULT_PRIORITY_TX
@@ -175,8 +190,12 @@
   u_short priority_tx;    /* != 0 - priority tx */
   u_short mode;           /* PIO, Shared Mem or Busmaster */
   u_char bus;
+#ifndef LINUX_2_1
   u_char pci_bus;
   u_char pci_device_fn;
+#else
+  struct pci_dev *pci_dev;
+#endif
   short mem_mapped;	  /* memory mapped access */
   u_int *mem_ptr_virt;    /* virtual memory mapped area, maybe NULL */
   u_int *mem_ptr_phys;	  /* physical memory mapped area */
@@ -281,7 +300,11 @@
  *  prototypes
  */
 
-static int  hp100_probe1( struct device *dev, int ioaddr, u_char bus, u_char pci_bus, u_char pci_device_fn  );
+#ifdef LINUX_2_1
+static int  hp100_probe1( struct device *dev, int ioaddr, u_char bus, struct pci_dev *pci_dev );
+#else
+static int  hp100_probe1( struct device *dev, int ioaddr, u_char bus, u_char pci_bus, u_char pci_device_fn );
+#endif
 static int  hp100_open( struct device *dev );
 static int  hp100_close( struct device *dev );
 static int  hp100_start_xmit( struct sk_buff *skb, struct device *dev );
@@ -330,9 +353,6 @@
   int ioaddr = 0;
 #ifdef CONFIG_PCI
   int pci_start_index = 0;
-#ifdef LINUX_2_1
-  struct pci_dev *pdev;
-#endif
 #endif
 
 #ifdef HP100_DEBUG_B
@@ -344,12 +364,24 @@
     {
       if ( check_region( base_addr, HP100_REGION_SIZE ) ) return -EINVAL;
       if ( base_addr < 0x400 )
+#ifdef LINUX_2_1
+        return hp100_probe1( dev, base_addr, HP100_BUS_ISA, NULL );
+#else
         return hp100_probe1( dev, base_addr, HP100_BUS_ISA, 0, 0 );
+#endif
       if ( EISA_bus && base_addr >= 0x1c38 && ( (base_addr - 0x1c38) & 0x3ff ) == 0 )
+#ifdef LINUX_2_1
+        return hp100_probe1( dev, base_addr, HP100_BUS_EISA, NULL );
+#else
         return hp100_probe1( dev, base_addr, HP100_BUS_EISA, 0, 0 );
+#endif
 #ifdef CONFIG_PCI
+#ifdef LINUX_2_1
+      printk( "hp100: %s: You must specify card # in i/o address parameter for PCI bus...", dev->name );
+#else
       printk( "hp100: %s: You may specify card # in i/o address parameter for PCI bus...", dev->name );
       return hp100_probe1( dev, base_addr, HP100_BUS_PCI, 0, 0 );
+#endif
 #else
       return -ENODEV;
 #endif
@@ -365,13 +397,54 @@
   /* at first - scan PCI bus(es) */
 
 #ifdef CONFIG_PCI
-  if ( pci_present() )
+  if ( pcibios_present() )
     {
       int pci_index;
+#ifdef LINUX_2_1
+      struct pci_dev *pci_dev = NULL;
+      int pci_id_index;
+      u_short pci_command;
+#endif
 
 #ifdef HP100_DEBUG_PCI
       printk( "hp100: %s: PCI BIOS is present, checking for devices..\n", dev->name );
 #endif
+#ifdef LINUX_2_1
+      pci_index = 0;
+      for ( pci_id_index = 0; pci_id_index < HP100_PCI_IDS_SIZE; pci_id_index++ ) {
+        while ( (pci_dev = pci_find_device( hp100_pci_ids[ pci_id_index ].vendor,
+            			            hp100_pci_ids[ pci_id_index ].device,
+            			            pci_dev )) != NULL ) {
+          if ( pci_index < (pci_start_index & 7) ) {
+            pci_index++;
+            continue;
+          }
+          /* found... */
+          ioaddr = pci_dev -> base_address[ 0 ] & ~3;
+          if ( check_region( ioaddr, HP100_REGION_SIZE ) ) continue;
+          pci_read_config_word( pci_dev, PCI_COMMAND, &pci_command );
+          if ( !( pci_command & PCI_COMMAND_IO ) ) {
+#ifdef HP100_DEBUG
+            printk( "hp100: %s: PCI I/O Bit has not been set. Setting...\n", dev->name );
+#endif
+            pci_command |= PCI_COMMAND_IO;
+            pci_write_config_word( pci_dev, PCI_COMMAND, pci_command );
+          }
+          if ( !( pci_command & PCI_COMMAND_MASTER ) ) {
+#ifdef HP100_DEBUG
+            printk( "hp100: %s: PCI Master Bit has not been set. Setting...\n", dev->name );
+#endif
+            pci_command |= PCI_COMMAND_MASTER;
+            pci_write_config_word( pci_dev, PCI_COMMAND, pci_command );
+          }
+#ifdef HP100_DEBUG
+          printk( "hp100: %s: PCI adapter found at 0x%x\n", dev->name, ioaddr );
+#endif
+	  if ( hp100_probe1( dev, ioaddr, HP100_BUS_PCI, pci_dev ) == 0 )
+	    return 0;
+        }
+      }      
+#else /* old PCI interface */
       for ( pci_index = pci_start_index & 7; pci_index < 8; pci_index++ )
         {
           u_char pci_bus, pci_device_fn;
@@ -386,14 +459,8 @@
           break;
 
 	  __pci_found:
-
-#ifdef LINUX_2_1
-	  pdev = pci_find_slot(pci_bus, pci_device_fn);
-	  ioaddr = pdev->base_address[0];
-#else
           pcibios_read_config_dword( pci_bus, pci_device_fn,
                                      PCI_BASE_ADDRESS_0, &ioaddr );
-#endif
 
           ioaddr &= ~3;    /* remove I/O space marker in bit 0. */
 
@@ -425,6 +492,7 @@
 	  if ( hp100_probe1( dev, ioaddr, HP100_BUS_PCI, pci_bus, pci_device_fn ) == 0 )
 	    return 0;
         }
+#endif
     }
   if ( pci_start_index > 0 ) return -ENODEV;
 #endif /* CONFIG_PCI */
@@ -433,21 +501,33 @@
   for ( ioaddr = 0x1c38; EISA_bus && ioaddr < 0x10000; ioaddr += 0x400 )
     {
       if ( check_region( ioaddr, HP100_REGION_SIZE ) ) continue;
+#ifdef LINUX_2_1
+      if ( hp100_probe1( dev, ioaddr, HP100_BUS_EISA, NULL ) == 0 ) return 0;
+#else
       if ( hp100_probe1( dev, ioaddr, HP100_BUS_EISA, 0, 0 ) == 0 ) return 0;
+#endif
     }
 
   /* Third Probe all ISA possible port regions */
   for ( ioaddr = 0x100; ioaddr < 0x400; ioaddr += 0x20 )
     {
       if ( check_region( ioaddr, HP100_REGION_SIZE ) ) continue;
+#ifdef LINUX_2_1
+      if ( hp100_probe1( dev, ioaddr, HP100_BUS_ISA, NULL ) == 0 ) return 0;
+#else
       if ( hp100_probe1( dev, ioaddr, HP100_BUS_ISA, 0, 0 ) == 0 ) return 0;
+#endif
     }
 
   return -ENODEV;
 }
 
 
+#ifdef LINUX_2_1
+__initfunc(static int hp100_probe1( struct device *dev, int ioaddr, u_char bus, struct pci_dev *pci_dev ))
+#else
 __initfunc(static int hp100_probe1( struct device *dev, int ioaddr, u_char bus, u_char pci_bus, u_char pci_device_fn ))
+#endif
 {
   int i;
 
@@ -711,15 +791,19 @@
   /* Initialise the "private" data structure for this card. */
   if ( (dev->priv=kmalloc(sizeof(struct hp100_private), GFP_KERNEL)) == NULL)
     return -ENOMEM;
-  memset( dev->priv, 0, sizeof(struct hp100_private) );
 
   lp = (struct hp100_private *)dev->priv;
+  memset( lp, 0, sizeof( struct hp100_private ) );
   lp->id = eid;
   lp->chip = chip;
   lp->mode = local_mode;
-  lp->pci_bus = pci_bus;
   lp->bus = bus;
+#ifdef LINUX_2_1
+  lp->pci_dev = pci_dev;
+#else
+  lp->pci_bus = pci_bus;
   lp->pci_device_fn = pci_device_fn;
+#endif
   lp->priority_tx = hp100_priority_tx;
   lp->rx_ratio = hp100_rx_ratio;
   lp->mem_ptr_phys = mem_ptr_phys;
@@ -751,10 +835,18 @@
   dev->set_multicast_list = &hp100_set_multicast_list;
 
   /* Ask the card for which IRQ line it is configured */
-  hp100_page( HW_MAP );
-  dev->irq = hp100_inb( IRQ_CHANNEL ) & HP100_IRQMASK;
-  if ( dev->irq == 2 )
-    dev->irq = 9;
+#ifdef LINUX_2_1
+  if ( bus == HP100_BUS_PCI ) {
+    dev->irq = pci_dev->irq;
+  } else {
+#endif
+    hp100_page( HW_MAP );
+    dev->irq = hp100_inb( IRQ_CHANNEL ) & HP100_IRQMASK;
+    if ( dev->irq == 2 )
+      dev->irq = 9;
+#ifdef LINUX_2_1
+  }
+#endif
 
   if(lp->mode==1) /* busmaster */
     dev->dma=4; 
diff -u --recursive --new-file v2.1.96/linux/drivers/net/mace.c linux/drivers/net/mace.c
--- v2.1.96/linux/drivers/net/mace.c	Thu Feb 12 20:56:08 1998
+++ linux/drivers/net/mace.c	Tue Apr 14 17:33:59 1998
@@ -13,6 +13,7 @@
 #include <asm/prom.h>
 #include <asm/dbdma.h>
 #include <asm/io.h>
+#include <asm/pgtable.h>
 #include "mace.h"
 
 #define N_RX_RING	8
@@ -117,21 +118,22 @@
 
 	mp = (struct mace_data *) dev->priv;
 	dev->base_addr = maces->addrs[0].address;
-	mp->mace = (volatile struct mace *) maces->addrs[0].address;
-	dev->irq = maces->intrs[0];
+	mp->mace = (volatile struct mace *)
+		ioremap(maces->addrs[0].address, 0x1000);
+	dev->irq = maces->intrs[0].line;
 
 	if (request_irq(dev->irq, mace_interrupt, 0, "MACE", dev)) {
 	    printk(KERN_ERR "MACE: can't get irq %d\n", dev->irq);
 	    return -EAGAIN;
 	}
-	if (request_irq(maces->intrs[1], mace_txdma_intr, 0, "MACE-txdma",
+	if (request_irq(maces->intrs[1].line, mace_txdma_intr, 0, "MACE-txdma",
 			dev)) {
-	    printk(KERN_ERR "MACE: can't get irq %d\n", maces->intrs[1]);
+	    printk(KERN_ERR "MACE: can't get irq %d\n", maces->intrs[1].line);
 	    return -EAGAIN;
 	}
-	if (request_irq(maces->intrs[2], mace_rxdma_intr, 0, "MACE-rxdma",
+	if (request_irq(maces->intrs[2].line, mace_rxdma_intr, 0, "MACE-rxdma",
 			dev)) {
-	    printk(KERN_ERR "MACE: can't get irq %d\n", maces->intrs[2]);
+	    printk(KERN_ERR "MACE: can't get irq %d\n", maces->intrs[2].line);
 	    return -EAGAIN;
 	}
 
@@ -155,10 +157,12 @@
 
 	mp = (struct mace_data *) dev->priv;
 	mp->maccc = ENXMT | ENRCV;
-	mp->tx_dma = (volatile struct dbdma_regs *) maces->addrs[1].address;
-	mp->tx_dma_intr = maces->intrs[1];
-	mp->rx_dma = (volatile struct dbdma_regs *) maces->addrs[2].address;
-	mp->rx_dma_intr = maces->intrs[2];
+	mp->tx_dma = (volatile struct dbdma_regs *)
+		ioremap(maces->addrs[1].address, 0x1000);
+	mp->tx_dma_intr = maces->intrs[1].line;
+	mp->rx_dma = (volatile struct dbdma_regs *)
+		ioremap(maces->addrs[2].address, 0x1000);
+	mp->rx_dma_intr = maces->intrs[2].line;
 
 	mp->tx_cmds = (volatile struct dbdma_cmd *) DBDMA_ALIGN(mp + 1);
 	mp->rx_cmds = mp->tx_cmds + NCMDS_TX * N_TX_RING + 1;
@@ -169,8 +173,6 @@
 	init_timer(&mp->tx_timeout);
 	mp->timeout_active = 0;
 
-	mace_reset(dev);
-
 	dev->open = mace_open;
 	dev->stop = mace_close;
 	dev->hard_start_xmit = mace_xmit_start;
@@ -260,6 +262,9 @@
     struct sk_buff *skb;
     unsigned char *data;
 
+    /* reset the chip */
+    mace_reset(dev);
+
     /* initialize list of sk_buffs for receiving and set up recv dma */
     memset((char *)mp->rx_cmds, 0, N_RX_RING * sizeof(struct dbdma_cmd));
     cp = mp->rx_cmds;
@@ -410,6 +415,10 @@
 	++mp->tx_active;
 	mace_set_timeout(dev);
     }
+    if (++next >= N_TX_RING)
+	next = 0;
+    if (next == mp->tx_empty)
+	dev->tbusy = 1;
     restore_flags(flags);
 
     return 0;
@@ -520,6 +529,8 @@
 
     i = mp->tx_empty;
     while (mb->pr & XMTSV) {
+	del_timer(&mp->tx_timeout);
+	mp->timeout_active = 0;
 	/*
 	 * Clear any interrupt indication associated with this status
 	 * word.  This appears to unlatch any error indication from
@@ -533,8 +544,6 @@
 	    eieio();
 	    mp->tx_bad_runt = 0;
 	    mb->xmtfc = AUTO_PAD_XMIT;
-	    del_timer(&mp->tx_timeout);
-	    mp->timeout_active = 0;
 	    continue;
 	}
 	dstat = ld_le32(&td->status);
@@ -562,7 +571,8 @@
 	}
 	fs = mb->xmtfs;
 	if ((fs & XMTSV) == 0) {
-	    printk(KERN_ERR "mace: xmtfs not valid! (fs=%x xc=%d ds=%x)\n", fs, xcount, dstat);
+	    printk(KERN_ERR "mace: xmtfs not valid! (fs=%x xc=%d ds=%x)\n",
+		   fs, xcount, dstat);
 	}
 	cp = mp->tx_cmds + NCMDS_TX * i;
 	stat = ld_le16(&cp->xfer_status);
@@ -571,6 +581,7 @@
 	     * Check whether there were in fact 2 bytes written to
 	     * the transmit FIFO.
 	     */
+	    udelay(1);
 	    x = (mb->fifofc >> XMTFC_SH) & XMTFC_MASK;
 	    if (x != 0) {
 		/* there were two bytes with an end-of-packet indication */
@@ -579,17 +590,21 @@
 	    } else {
 		/*
 		 * Either there weren't the two bytes buffered up, or they
-		 * didn't have an end-of-packet indication.  Maybe we ought
-		 * to flush the transmit FIFO just in case (by setting the
+		 * didn't have an end-of-packet indication.
+		 * We flush the transmit FIFO just in case (by setting the
 		 * XMTFWU bit with the transmitter disabled).
 		 */
-		mb->xmtfc = AUTO_PAD_XMIT;
-		eieio();
+		out_8(&mb->maccc, mb->maccc & ~ENXMT);
+		out_8(&mb->fifocc, mb->fifocc | XMTFWU);
+		udelay(1);
+		out_8(&mb->maccc, mb->maccc | ENXMT);
+		out_8(&mb->xmtfc, AUTO_PAD_XMIT);
 	    }
 	}
 	/* dma should have finished */
 	if (i == mp->tx_fill) {
-	    printk(KERN_DEBUG "mace: tx ring ran out? (fs=%x xc=%d ds=%x)\n", fs, xcount, dstat);
+	    printk(KERN_DEBUG "mace: tx ring ran out? (fs=%x xc=%d ds=%x)\n",
+		   fs, xcount, dstat);
 	    continue;
 	}
 	/* Update stats */
@@ -607,11 +622,9 @@
 	    i = 0;
 	mace_last_fs = fs;
 	mace_last_xcount = xcount;
-	del_timer(&mp->tx_timeout);
-	mp->timeout_active = 0;
     }
 
-    if (i != mp->tx_empty && mp->tx_fullup) {
+    if (i != mp->tx_empty) {
 	mp->tx_fullup = 0;
 	dev->tbusy = 0;
 	mark_bh(NET_BH);
@@ -656,9 +669,6 @@
     mace_handle_misc_intrs(mp, mb->ir);
 
     cp = mp->tx_cmds + NCMDS_TX * mp->tx_empty;
-    printk(KERN_DEBUG "mace: tx dmastat=%x %x bad_runt=%d pr=%x fs=%x fc=%x\n",
-	   ld_le32(&td->status), ld_le16(&cp->xfer_status), mp->tx_bad_runt,
-	   mb->pr, mb->xmtfs, mb->fifofc);
 
     /* turn off both tx and rx and reset the chip */
     mb->maccc = 0;
@@ -685,11 +695,9 @@
 	    i = 0;
 	mp->tx_empty = i;
     }
-    if (mp->tx_fullup) {
-	mp->tx_fullup = 0;
-	dev->tbusy = 0;
-	mark_bh(NET_BH);
-    }
+    mp->tx_fullup = 0;
+    dev->tbusy = 0;
+    mark_bh(NET_BH);
     if (i != mp->tx_fill) {
 	cp = mp->tx_cmds + NCMDS_TX * i;
 	out_le16(&cp->xfer_status, 0);
diff -u --recursive --new-file v2.1.96/linux/drivers/net/mace.h linux/drivers/net/mace.h
--- v2.1.96/linux/drivers/net/mace.h	Sat Aug 16 09:53:08 1997
+++ linux/drivers/net/mace.h	Tue Apr 14 17:33:59 1998
@@ -164,7 +164,7 @@
 /* Bits in UTR */
 #define RTRE		0x80	/* reserved test register enable. DON'T SET. */
 #define RTRD		0x40	/* reserved test register disable.  Sticky */
-#define RPA		0x20	/* accept runt packets */
+#define RPAC		0x20	/* accept runt packets */
 #define FCOLL		0x10	/* force collision */
 #define RCVFCSE		0x08	/* receive FCS enable */
 #define LOOP_NONE	0x00	/* no loopback */
diff -u --recursive --new-file v2.1.96/linux/drivers/net/sunhme.c linux/drivers/net/sunhme.c
--- v2.1.96/linux/drivers/net/sunhme.c	Mon Apr  6 17:41:00 1998
+++ linux/drivers/net/sunhme.c	Tue Apr 14 17:44:22 1998
@@ -1408,27 +1408,15 @@
 {
 	int reset = 0;
 
-	printk("%s: Error interrupt for happy meal, status = %08lx\n",
-	       hp->dev->name, status);
-
-	if(status &
-	   (GREG_STAT_RCNTEXP|GREG_STAT_ACNTEXP|GREG_STAT_CCNTEXP|GREG_STAT_LCNTEXP)) {
-		/* Some stupid counter expired, we should be not
-		 * have interrupts for this enabled, but we check
-		 * for it anyways.
-		 */
-
-		printk("%s: Happy Meal counters expired [ ", hp->dev->name);
-		if(status & GREG_STAT_RCNTEXP)
-			printk("ReceiveFrame ");
-		if(status & GREG_STAT_ACNTEXP)
-			printk("AlignError ");
-		if(status & GREG_STAT_CCNTEXP)
-			printk("CrcError ");
-		if(status & GREG_STAT_LCNTEXP)
-			printk("LengthError ");
-		printk("]\n");
-	}
+	/* Only print messages for non-counter related interrupts. */
+	if(status & (GREG_STAT_RFIFOVF | GREG_STAT_STSTERR | GREG_STAT_TFIFO_UND |
+		     GREG_STAT_MAXPKTERR | GREG_STAT_NORXD | GREG_STAT_RXERR |
+		     GREG_STAT_RXPERR | GREG_STAT_RXTERR | GREG_STAT_EOPERR |
+		     GREG_STAT_MIFIRQ | GREG_STAT_TXEACK | GREG_STAT_TXLERR |
+		     GREG_STAT_TXPERR | GREG_STAT_TXTERR | GREG_STAT_SLVERR |
+		     GREG_STAT_SLVPERR))
+		printk("%s: Error interrupt for happy meal, status = %08lx\n",
+		       hp->dev->name, status);
 
 	if(status & GREG_STAT_RFIFOVF) {
 		/* The receive FIFO overflowwed, usually a DMA error. */
@@ -1436,11 +1424,6 @@
 		reset = 1;
 	}
 
-	if(status & GREG_STAT_CVCNTEXP) {
-		/* See above about counter expiration... */
-		printk("%s: Code Violation error counter expired.\n", hp->dev->name);
-	}
-
 	if(status & GREG_STAT_STSTERR) {
 		/* BigMAC SQE link test failed. */
 		printk("%s: Happy Meal BigMAC SQE test failed.\n", hp->dev->name);
@@ -1460,32 +1443,6 @@
 		 */
 		printk("%s: Happy Meal MAX Packet size error.\n", hp->dev->name);
 		reset = 1;
-	}
-
-	if(status & (GREG_STAT_NCNTEXP|GREG_STAT_ECNTEXP|GREG_STAT_LCCNTEXP|
-		     GREG_STAT_FCNTEXP)) {
-		/* More stupid error counters... */
-		printk("%s: Happy Meal counters expired [ ", hp->dev->name);
-		if(status & GREG_STAT_NCNTEXP)
-			printk("NormalCollision ");
-		if(status & GREG_STAT_ECNTEXP)
-			printk("ExcessCollision ");
-		if(status & GREG_STAT_LCCNTEXP)
-			printk("LateCollision ");
-		if(status & GREG_STAT_FCNTEXP)
-			printk("FirstCollision ");
-		printk("]\n");
-	}
-
-	if(status & GREG_STAT_DTIMEXP) {
-		/* Defer-timer expired.  Probably means the happy meal needed
-		 * to back off too much before it could transmit one frame.
-		 */
-#if 0		/* XXX This isn't worth reporting and is in fact a normal condition. */
-		printk("%s: Transmit defer timer expired, subnet congested?\n",
-		       hp->dev->name);
-		reset = 1;
-#endif
 	}
 
 	if(status & GREG_STAT_NORXD) {
diff -u --recursive --new-file v2.1.96/linux/drivers/net/sunlance.c linux/drivers/net/sunlance.c
--- v2.1.96/linux/drivers/net/sunlance.c	Thu Feb 12 20:56:09 1998
+++ linux/drivers/net/sunlance.c	Tue Apr 14 17:44:22 1998
@@ -1,4 +1,4 @@
-/* $Id: sunlance.c,v 1.69 1998/01/09 16:42:52 jj Exp $
+/* $Id: sunlance.c,v 1.74 1998/02/12 07:37:25 davem Exp $
  * lance.c: Linux/Sparc/Lance driver
  *
  *	Written 1995, 1996 by Miguel de Icaza
@@ -103,6 +103,9 @@
 #include <linux/etherdevice.h>
 #include <linux/skbuff.h>
 
+#include <asm/idprom.h>
+#include <asm/machines.h>
+
 /* Define: 2^4 Tx buffers and 2^4 Rx buffers */
 #ifndef LANCE_LOG_TX_BUFFERS
 #define LANCE_LOG_TX_BUFFERS 4
@@ -1037,7 +1040,7 @@
 						  "busmaster-regval",
 						  (LE_C3_BSWP | LE_C3_ACON |
 						   LE_C3_BCON));
-    
+
 	lp->ll = ll;
 	lp->name = lancestr;
 	lp->ledma = ledma;
@@ -1119,8 +1122,9 @@
 	dev->hard_start_xmit = &lance_start_xmit;
 	dev->get_stats = &lance_get_stats;
 	dev->set_multicast_list = &lance_set_multicast;
-    
+
 	dev->irq = (unsigned char) sdev->irqs [0].pri;
+
 	dev->dma = 0;
 	ether_setup (dev);
 
@@ -1144,6 +1148,31 @@
 	return 0;
 }
 
+#ifdef CONFIG_SUN4
+
+#include <asm/sun4paddr.h>
+
+/* Find all the lance cards on the system and initialize them */
+__initfunc(int sparc_lance_probe (struct device *dev))
+{
+	static struct linux_sbus_device sdev;
+	static int called = 0;
+
+	if(called)
+		return ENODEV;
+	called++;
+
+	if (idprom->id_machtype == (SM_SUN4|SM_4_330)) {
+		memset (&sdev, 0, sizeof(sdev));
+		sdev.reg_addrs[0].phys_addr = SUN4_300_ETH_PHYSADDR;
+		sdev.irqs[0].pri = 6;
+		return sparc_lance_init(dev, &sdev, 0, 0);
+	}
+	return ENODEV;
+}
+
+#else /* !CONFIG_SUN4 */
+
 /* Find all the lance cards on the system and initialize them */
 __initfunc(int sparc_lance_probe (struct device *dev))
 {
@@ -1152,10 +1181,11 @@
 	struct Linux_SBus_DMA *ledma = 0;
 	static int called = 0;
 	int cards = 0, v;
-    
+
 	if(called)
 		return ENODEV;
 	called++;
+
 	for_each_sbus (bus) {
 		for_each_sbusdev (sdev, bus) {
 			if (cards) dev = NULL;
@@ -1186,6 +1216,7 @@
 		return ENODEV;
 	return 0;
 }
+#endif /* !CONFIG_SUN4 */
 
 #ifdef MODULE
 
diff -u --recursive --new-file v2.1.96/linux/drivers/pci/pci.c linux/drivers/pci/pci.c
--- v2.1.96/linux/drivers/pci/pci.c	Fri Apr 10 13:03:48 1998
+++ linux/drivers/pci/pci.c	Fri Apr 17 21:58:48 1998
@@ -1,5 +1,5 @@
 /*
- *	$Id: pci.c,v 1.71 1998/03/30 11:14:35 mj Exp $
+ *	$Id: pci.c,v 1.79 1998/04/17 16:25:24 mj Exp $
  *
  *	PCI Bus Services
  *
@@ -17,44 +17,19 @@
 
 #include <asm/page.h>
 
+#undef DEBUG
+
+#ifdef DEBUG
+#define DBG(x...) printk(x)
+#else
+#define DBG(x...)
+#endif
+
 struct pci_bus pci_root;
 struct pci_dev *pci_devices = NULL;
 static struct pci_dev **pci_last_dev_p = &pci_devices;
 static int pci_reverse __initdata = 0;
 
-#undef DEBUG
-
-const char *pcibios_strerror(int error)
-{
-	static char buf[32];
-
-	switch (error) {
-		case PCIBIOS_SUCCESSFUL:
-		case PCIBIOS_BAD_VENDOR_ID:
-			return "SUCCESSFUL";
-
-		case PCIBIOS_FUNC_NOT_SUPPORTED:
-			return "FUNC_NOT_SUPPORTED";
-
-		case PCIBIOS_DEVICE_NOT_FOUND:
-			return "DEVICE_NOT_FOUND";
-
-		case PCIBIOS_BAD_REGISTER_NUMBER:
-			return "BAD_REGISTER_NUMBER";
-
-                case PCIBIOS_SET_FAILED:          
-			return "SET_FAILED";
-
-                case PCIBIOS_BUFFER_TOO_SMALL:    
-			return "BUFFER_TOO_SMALL";
-
-		default:
-			sprintf (buf, "PCI ERROR 0x%x", error);
-			return buf;
-	}
-}
-
-
 struct pci_dev *
 pci_find_slot(unsigned int bus, unsigned int devfn)
 {
@@ -93,6 +68,28 @@
 }
 
 
+void
+pci_set_master(struct pci_dev *dev)
+{
+	unsigned short cmd;
+	unsigned char lat;
+
+	pci_read_config_word(dev, PCI_COMMAND, &cmd);
+	if (! (cmd & PCI_COMMAND_MASTER)) {
+		printk("PCI: Enabling bus mastering for device %02x:%02x\n",
+			dev->bus->number, dev->devfn);
+		cmd |= PCI_COMMAND_MASTER;
+		pci_write_config_word(dev, PCI_COMMAND, cmd);
+	}
+	pci_read_config_byte(dev, PCI_LATENCY_TIMER, &lat);
+	if (lat < 16) {
+		printk("PCI: Increasing latency timer of device %02x:%02x to 64\n",
+			dev->bus->number, dev->devfn);
+		pci_write_config_byte(dev, PCI_LATENCY_TIMER, 64);
+	}
+}
+
+
 __initfunc(unsigned int pci_scan_bus(struct pci_bus *bus))
 {
 	unsigned int devfn, l, max, class;
@@ -101,10 +98,7 @@
 	struct pci_bus *child;
 	int reg;
 
-#ifdef DEBUG
-	printk("pci_scan_bus for bus %d\n", bus->number);
-#endif
-
+	DBG("pci_scan_bus for bus %d\n", bus->number);
 	max = bus->secondary;
 	for (devfn = 0; devfn < 0xff; ++devfn) {
 		if (PCI_FUNC(devfn) && !is_multi) {
@@ -161,6 +155,8 @@
 				pcibios_read_config_dword(bus->number, devfn, PCI_BASE_ADDRESS_0 + (reg << 2), &l);
 				dev->base_address[reg] = (l == 0xffffffff) ? 0 : l;
 			}
+			pcibios_read_config_dword(bus->number, devfn, PCI_ROM_ADDRESS, &l);
+			dev->rom_address = (l == 0xffffffff) ? 0 : l;
 			break;
 		case PCI_HEADER_TYPE_BRIDGE:		    /* bridge header */
 			if (class >> 8 != PCI_CLASS_BRIDGE_PCI)
@@ -169,6 +165,8 @@
 				pcibios_read_config_dword(bus->number, devfn, PCI_BASE_ADDRESS_0 + (reg << 2), &l);
 				dev->base_address[reg] = (l == 0xffffffff) ? 0 : l;
 			}
+			pcibios_read_config_dword(bus->number, devfn, PCI_ROM_ADDRESS1, &l);
+			dev->rom_address = (l == 0xffffffff) ? 0 : l;
 			break;
 		case PCI_HEADER_TYPE_CARDBUS:		    /* CardBus bridge header */
 			if (class >> 16 != PCI_BASE_CLASS_BRIDGE)
@@ -185,10 +183,7 @@
 			continue;
 		}
 
-#ifdef DEBUG
-		printk("PCI: %02x:%02x [%04x/%04x]\n",
-		       bus->number, dev->devfn, dev->vendor, dev->device);
-#endif
+		DBG("PCI: %02x:%02x [%04x/%04x]\n", bus->number, dev->devfn, dev->vendor, dev->device);
 
 		/*
 		 * Put it into the global PCI device chain. It's used to
@@ -209,16 +204,15 @@
 		dev->sibling = bus->devices;
 		bus->devices = dev;
 
+#if 0
 		/*
-		 * In case the latency timer value is less than 32,
-		 * which makes everything very sllooowww, set it to
-		 * 32. Pciutils should be used to fine-tune it later.
-		 * Note that we don't check if the device is a bus-master:
-		 * if it isn't, write to the latency timer should be ignored.
+		 * Setting of latency timer in case it was less than 32 was
+		 * a great idea, but it confused several broken devices. Grrr.
 		 */
 		pcibios_read_config_byte(bus->number, dev->devfn, PCI_LATENCY_TIMER, &tmp);
 		if (tmp < 32)
 			pcibios_write_config_byte(bus->number, dev->devfn, PCI_LATENCY_TIMER, 32);
+#endif
 
 		/*
 		 * If it's a bridge, scan the bus behind it.
@@ -303,9 +297,7 @@
 	 *
 	 * Return how far we've got finding sub-buses.
 	 */
-#ifdef DEBUG
-	printk("PCI: pci_scan_bus returning with max=%02x\n", max);
-#endif
+	DBG("PCI: pci_scan_bus returning with max=%02x\n", max);
 	return max;
 }
 
@@ -331,25 +323,19 @@
 	pci_quirks_init();
 #endif
 
-#ifdef CONFIG_PROC_FS
-	proc_bus_pci_init();
-#ifdef CONFIG_PCI_OLD_PROC
-	proc_old_pci_init();
-#endif
+#ifdef CONFIG_PROC
+	pci_proc_init();
 #endif
 }
 
 
 __initfunc(void pci_setup (char *str, int *ints))
 {
-	str = pcibios_setup(str);
 	while (str) {
 		char *k = strchr(str, ',');
 		if (k)
 			*k++ = 0;
-		if (*str) {
-			if (!(str = pcibios_setup(str)) || !*str)
-				continue;
+		if (*str && (str = pcibios_setup(str)) && *str) {
 			if (!strcmp(str, "reverse"))
 				pci_reverse = 1;
 			else printk(KERN_ERR "PCI: Unknown option `%s'\n", str);
diff -u --recursive --new-file v2.1.96/linux/drivers/pci/pcisyms.c linux/drivers/pci/pcisyms.c
--- v2.1.96/linux/drivers/pci/pcisyms.c	Mon Apr  6 17:41:00 1998
+++ linux/drivers/pci/pcisyms.c	Fri Apr 17 21:58:48 1998
@@ -1,5 +1,5 @@
 /*
- *	$Id: pcisyms.c,v 1.2 1998/02/16 10:35:57 mj Exp $
+ *	$Id: pcisyms.c,v 1.4 1998/04/17 16:34:19 mj Exp $
  *
  *	PCI Bus Services -- Exported Symbols
  *
@@ -16,11 +16,12 @@
 EXPORT_SYMBOL(pcibios_write_config_byte);
 EXPORT_SYMBOL(pcibios_write_config_word);
 EXPORT_SYMBOL(pcibios_write_config_dword);
-EXPORT_SYMBOL(pcibios_strerror);
 EXPORT_SYMBOL(pci_devices);
+EXPORT_SYMBOL(pci_root);
 EXPORT_SYMBOL(pci_find_class);
 EXPORT_SYMBOL(pci_find_device);
 EXPORT_SYMBOL(pci_find_slot);
+EXPORT_SYMBOL(pci_set_master);
 
 /* Backward compatibility */
 
diff -u --recursive --new-file v2.1.96/linux/drivers/pci/proc.c linux/drivers/pci/proc.c
--- v2.1.96/linux/drivers/pci/proc.c	Mon Apr  6 17:41:00 1998
+++ linux/drivers/pci/proc.c	Fri Apr 17 21:58:48 1998
@@ -1,5 +1,5 @@
 /*
- *	$Id: proc.c,v 1.8 1998/03/12 14:32:51 mj Exp $
+ *	$Id: proc.c,v 1.10 1998/04/16 20:48:30 mj Exp $
  *
  *	Procfs interface for the PCI bus.
  *
@@ -250,6 +250,13 @@
 						"\t%016lx",
 #endif
 					dev->base_address[i]);
+		len += sprintf(buf+len,
+#if BITS_PER_LONG == 32
+					"\t%08lx",
+#else
+					"\t%016lx",
+#endif
+			       dev->rom_address);
 		buf[len++] = '\n';
 		at += len;
 		if (at >= pos) {
@@ -298,7 +305,7 @@
 	}
 }
 
-__initfunc(void proc_bus_pci_init(void))
+__initfunc(void pci_proc_init(void))
 {
 	struct proc_dir_entry *proc_pci;
 
@@ -307,4 +314,8 @@
 	proc_pci = create_proc_entry("pci", S_IFDIR, proc_bus);
 	proc_register(proc_pci, &proc_pci_devices);
 	proc_bus_pci_add(&pci_root, proc_pci);
+
+#ifdef CONFIG_PCI_OLD_PROC
+	proc_old_pci_init();
+#endif
 }
diff -u --recursive --new-file v2.1.96/linux/drivers/sbus/char/Makefile linux/drivers/sbus/char/Makefile
--- v2.1.96/linux/drivers/sbus/char/Makefile	Mon Jan 12 15:15:45 1998
+++ linux/drivers/sbus/char/Makefile	Tue Apr 14 17:44:22 1998
@@ -68,6 +68,14 @@
   endif
 endif
 
+ifeq ($(CONFIG_ENVCTRL),y)
+O_OBJS += envctrl.o
+else
+  ifeq ($(CONFIG_ENVCTRL),m)
+  M_OBJS += envctrl.o
+  endif
+endif
+
 endif # eq($(CONFIG_PCI,y)
 
 ifeq ($(CONFIG_OBP_FLASH),y)
diff -u --recursive --new-file v2.1.96/linux/drivers/sbus/char/bwtwo.c linux/drivers/sbus/char/bwtwo.c
--- v2.1.96/linux/drivers/sbus/char/bwtwo.c	Wed Jul 16 20:37:21 1997
+++ linux/drivers/sbus/char/bwtwo.c	Tue Apr 14 17:44:22 1998
@@ -1,8 +1,9 @@
-/* $Id: bwtwo.c,v 1.18 1997/07/17 02:21:43 davem Exp $
+/* $Id: bwtwo.c,v 1.20 1998/03/10 20:18:22 jj Exp $
  * bwtwo.c: bwtwo console driver
  *
  * Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
  * Copyright (C) 1997 Eddie C. Dost   (ecd@skynet.be)
+ * Copyright (C) 1998 Pavel Machek    (pavel@ucw.cz)
  */
 
 #include <linux/kd.h>
@@ -95,7 +96,8 @@
 	if (r)
 		return -EAGAIN;
 
-	vma->vm_dentry = dget(file->f_dentry);
+	vma->vm_file = file;
+	file->f_count++;
 	return 0;
 }
 
@@ -156,15 +158,17 @@
 	fb->loadcmap = 0;
 	fb->ioctl = 0;
 	fb->reset = 0;
+#ifndef CONFIG_SUN4	
 	fb->blank = bwtwo_blank;
 	fb->unblank = bwtwo_unblank;
+#endif
 
 	fb->info.bwtwo.regs =
 		sparc_alloc_io (bwtwo + BWTWO_REGISTER_OFFSET,
 				0, sizeof (struct bwtwo_regs),
 		"bwtwo_regs", bw2_io, 0);
 
-	if (!prom_getbool(sbdp->prom_node, "width")) {
+	if (sbdp && !prom_getbool(sbdp->prom_node, "width")) {
 		/* Ugh, broken PROM didn't initialize us.
 		 * Let's deal with this ourselves.
 		 */
diff -u --recursive --new-file v2.1.96/linux/drivers/sbus/char/cgfourteen.c linux/drivers/sbus/char/cgfourteen.c
--- v2.1.96/linux/drivers/sbus/char/cgfourteen.c	Thu Sep  4 12:54:48 1997
+++ linux/drivers/sbus/char/cgfourteen.c	Tue Apr 14 17:44:22 1998
@@ -1,4 +1,4 @@
-/* $Id: cgfourteen.c,v 1.25 1997/08/20 07:38:36 davem Exp $
+/* $Id: cgfourteen.c,v 1.26 1998/03/10 20:18:23 jj Exp $
  * cgfourteen.c: Sun SparcStation console support.
  *
  * Copyright (C) 1995 Miguel de Icaza (miguel@nuclecu.unam.mx)
@@ -274,7 +274,8 @@
 		page += map_size;
 	}
 
-	vma->vm_dentry = dget(file->f_dentry);
+	vma->vm_file = file;
+	file->f_count++;
         return 0;
 }
 
diff -u --recursive --new-file v2.1.96/linux/drivers/sbus/char/cgsix.c linux/drivers/sbus/char/cgsix.c
--- v2.1.96/linux/drivers/sbus/char/cgsix.c	Thu Sep  4 12:54:48 1997
+++ linux/drivers/sbus/char/cgsix.c	Tue Apr 14 17:44:22 1998
@@ -1,4 +1,4 @@
-/* $Id: cgsix.c,v 1.37 1997/08/22 15:55:20 jj Exp $
+/* $Id: cgsix.c,v 1.39 1998/03/10 20:18:25 jj Exp $
  * cgsix.c: cgsix frame buffer driver
  *
  * Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
@@ -326,7 +326,8 @@
 		page += map_size;
 	}
 
-	vma->vm_dentry = dget(file->f_dentry);
+	vma->vm_file = file;
+	file->f_count++;
         return 0;
 }
 
@@ -395,11 +396,14 @@
 cg6_blank (fbinfo_t *fb)
 {
 	fb->info.cg6.thc->thc_misc &= ~CG6_THC_MISC_VIDEO;
+	/* This should put us in power-save */
+	fb->info.cg6.thc->thc_misc &= ~CG6_THC_MISC_SYNC_ENAB;
 }
 
 static void
 cg6_unblank (fbinfo_t *fb)
 {
+	fb->info.cg6.thc->thc_misc |= CG6_THC_MISC_SYNC_ENAB;
 	fb->info.cg6.thc->thc_misc |= CG6_THC_MISC_VIDEO;
 }
 
diff -u --recursive --new-file v2.1.96/linux/drivers/sbus/char/cgthree.c linux/drivers/sbus/char/cgthree.c
--- v2.1.96/linux/drivers/sbus/char/cgthree.c	Thu Sep  4 12:54:48 1997
+++ linux/drivers/sbus/char/cgthree.c	Tue Apr 14 17:44:22 1998
@@ -1,4 +1,4 @@
-/* $Id: cgthree.c,v 1.24 1997/08/20 07:38:37 davem Exp $
+/* $Id: cgthree.c,v 1.25 1998/03/10 20:18:27 jj Exp $
  * cgtree.c: cg3 frame buffer driver
  *
  * Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
@@ -133,7 +133,8 @@
 		page += map_size;
 	}
 
-	vma->vm_dentry = dget(file->f_dentry);
+	vma->vm_file = file;
+	file->f_count++;
         return 0;
 }
 
diff -u --recursive --new-file v2.1.96/linux/drivers/sbus/char/creator.c linux/drivers/sbus/char/creator.c
--- v2.1.96/linux/drivers/sbus/char/creator.c	Mon Jan 12 15:15:45 1998
+++ linux/drivers/sbus/char/creator.c	Tue Apr 14 17:44:22 1998
@@ -1,4 +1,4 @@
-/* $Id: creator.c,v 1.13 1997/10/17 04:14:40 davem Exp $
+/* $Id: creator.c,v 1.14 1998/03/10 20:18:32 jj Exp $
  * creator.c: Creator/Creator3D frame buffer driver
  *
  * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
@@ -244,7 +244,8 @@
 		page += map_size;
 	}
 
-	vma->vm_dentry = dget(file->f_dentry);
+	vma->vm_file = file;
+	file->f_count++;
         return 0;
 }
 
@@ -702,8 +703,8 @@
 	while (count-- > 0) {
 		ffb->by = boxes[1];
 		ffb->bx = boxes[0];
-		ffb->bw = boxes[2];
 		ffb->bh = boxes[3];
+		ffb->bw = boxes[2];
 		boxes += 4;
 	}
 	FFB_FILL_END
diff -u --recursive --new-file v2.1.96/linux/drivers/sbus/char/envctrl.c linux/drivers/sbus/char/envctrl.c
--- v2.1.96/linux/drivers/sbus/char/envctrl.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/sbus/char/envctrl.c	Tue Apr 14 17:44:22 1998
@@ -0,0 +1,241 @@
+/* $Id: envctrl.c,v 1.3 1998/04/10 08:42:24 jj Exp $
+ * envctrl.c: Temperature and Fan monitoring on Machines providing it.
+ *
+ * Copyright (C) 1998  Eddie C. Dost  (ecd@skynet.be)
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+
+#include <asm/ebus.h>
+
+#define PCF8584_ADDRESS	0x55
+
+#define CONTROL_PIN	0x80
+#define CONTROL_ES0	0x40
+#define CONTROL_ES1	0x20
+#define CONTROL_ES2	0x10
+#define CONTROL_ENI	0x08
+#define CONTROL_STA	0x04
+#define CONTROL_STO	0x02
+#define CONTROL_ACK	0x01
+
+#define STATUS_PIN	0x80
+#define STATUS_STS	0x20
+#define STATUS_BER	0x10
+#define STATUS_LRB	0x08
+#define STATUS_AD0	0x08
+#define STATUS_AAB	0x04
+#define STATUS_LAB	0x02
+#define STATUS_BB	0x01
+
+/*
+ * CLK Mode Register.
+ */
+#define BUS_CLK_90	0x00
+#define BUS_CLK_45	0x01
+#define BUS_CLK_11	0x02
+#define BUS_CLK_1_5	0x03
+
+#define CLK_3		0x00
+#define CLK_4_43	0x10
+#define CLK_6		0x14
+#define CLK_8		0x18
+#define CLK_12		0x1c
+
+
+#define I2C_WRITE	0x00
+#define I2C_READ	0x01
+
+struct pcf8584_reg
+{
+	__volatile__ unsigned char data;
+	__volatile__ unsigned char csr;
+};
+
+static struct pcf8584_reg *i2c;
+
+
+struct i2c_addr_map {
+	unsigned char addr;
+	unsigned char mask;
+	char *name;
+};
+
+static struct i2c_addr_map devmap[] = {
+	{ 0x38, 0x78, "PCF8574A" },
+	{ 0x20, 0x78, "TDA8444" },
+	{ 0x48, 0x78, "PCF8591" },
+};
+#define NR_DEVMAP (sizeof(devmap) / sizeof(devmap[0]))
+
+static int
+envctrl_read(unsigned char dev, char *buffer, int len)
+{
+	unsigned char dummy;
+	unsigned char stat;
+	int error = -ENODEV;
+	int count = -1;
+
+	if (len == 0)
+		return 0;
+
+	i2c->data = (dev << 1) | I2C_READ;
+
+	while (!(i2c->csr & STATUS_BB))
+		udelay(1);
+
+	i2c->csr = CONTROL_PIN | CONTROL_ES0 | CONTROL_STA | CONTROL_ACK;
+
+	do {
+		udelay(1);
+		while ((stat = i2c->csr) & STATUS_PIN)
+			udelay(1);
+
+		if (stat & STATUS_LRB)
+			goto stop;
+		error = 0;
+		if (count == (len - 2))
+			goto final;
+
+		if (++count > 0)
+			*buffer++ = i2c->data;
+		else
+			dummy = i2c->data;
+	} while (1);
+
+final:
+	i2c->csr = CONTROL_ES0;
+	if (++count > 0)
+		*buffer++ = i2c->data;
+	else
+		dummy = i2c->data;
+
+	udelay(1);
+	while ((stat = i2c->csr) & STATUS_PIN)
+		udelay(1);
+
+stop:
+	i2c->csr = CONTROL_PIN | CONTROL_ES0 | CONTROL_STO | CONTROL_ACK;
+	if (++count > 0)
+		*buffer++ = i2c->data;
+	else
+		dummy = i2c->data;
+
+	if (error)
+		return error;
+	return count;
+}
+
+static int
+envctrl_write(unsigned char dev, char *buffer, int len)
+{
+	int error = -ENODEV;
+	int count = 0;
+
+	while (!(i2c->csr & STATUS_BB))
+		udelay(1);
+
+	i2c->data = (dev << 1) | I2C_WRITE;
+	i2c->csr = CONTROL_PIN | CONTROL_ES0 | CONTROL_STA | CONTROL_ACK;
+
+	do {
+		unsigned char stat;
+
+		udelay(1);
+		while ((stat = i2c->csr) & STATUS_PIN)
+			udelay(1);
+
+		if (stat & STATUS_LRB)
+			goto stop;
+		error = count;
+		if (count == len)
+			goto stop;
+
+		i2c->data = *buffer++;
+		count++;
+	} while (1);
+
+stop:
+	i2c->csr = CONTROL_PIN | CONTROL_ES0 | CONTROL_STO | CONTROL_ACK;
+	return error;
+}
+
+__initfunc(static int scan_bus(void))
+{
+	unsigned char dev;
+	int count = 0;
+	int i;
+
+	/* scan */
+	for (dev = 1; dev < 128; dev++)
+		if (envctrl_write(dev, 0, 0) == 0) {
+			for (i = 0; i < NR_DEVMAP; i++)
+				if ((dev & devmap[i].mask) == devmap[i].addr)
+					break;
+			printk("envctrl: i2c device at %02x: %s\n", dev,
+			       i < NR_DEVMAP ? devmap[i].name : "unknown");
+{
+			unsigned char buf[4];
+			if (envctrl_read(dev, buf, 4) == 4)
+				printk("envctrl: read %02x %02x %02x %02x\n",
+			               buf[0], buf[1], buf[2], buf[3]);
+}
+			count++;
+		}
+	return count ? 0 : -ENODEV;
+}
+
+#ifdef MODULE
+int init_module(void)
+#else
+__initfunc(int envctrl_init(void))
+#endif
+{
+#ifdef CONFIG_PCI
+	struct linux_ebus *ebus;
+	struct linux_ebus_device *edev;
+
+	for_all_ebusdev(edev, ebus)
+		if (!strcmp(edev->prom_name, "SUNW,envctrl"))
+			break;
+
+	if (!edev)
+		return -ENODEV;
+
+	if (check_region(edev->base_address[0], sizeof(*i2c))) {
+		prom_printf("%s: Can't get region %lx, %d\n",
+			    __FUNCTION__, edev->base_address[0],
+			    sizeof(*i2c));
+		prom_halt();
+	}
+
+	request_region(edev->base_address[0],
+		       sizeof(*i2c), "i2c");
+
+	i2c = (struct pcf8584_reg *)edev->base_address[0];
+
+	i2c->csr = CONTROL_PIN;
+	i2c->data = PCF8584_ADDRESS;
+	i2c->csr = CONTROL_PIN | CONTROL_ES1;
+	i2c->data = CLK_4_43 | BUS_CLK_90;
+	i2c->csr = CONTROL_PIN | CONTROL_ES0 | CONTROL_ACK;
+	udelay(10000);
+
+	return scan_bus();
+#else
+	return -ENODEV;
+#endif
+}
+
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+}
+#endif
diff -u --recursive --new-file v2.1.96/linux/drivers/sbus/char/flash.c linux/drivers/sbus/char/flash.c
--- v2.1.96/linux/drivers/sbus/char/flash.c	Tue Mar 10 10:03:32 1998
+++ linux/drivers/sbus/char/flash.c	Tue Apr 14 17:44:22 1998
@@ -1,4 +1,4 @@
-/* $Id: flash.c,v 1.5 1997/11/01 10:22:13 ecd Exp $
+/* $Id: flash.c,v 1.7 1998/03/10 20:19:05 jj Exp $
  * flash.c: Allow mmap access to the OBP Flash, for OBP updates.
  *
  * Copyright (C) 1997  Eddie C. Dost  (ecd@skynet.be)
@@ -71,7 +71,9 @@
 
 	if (remap_page_range(vma->vm_start, addr, size, vma->vm_page_prot))
 		return -EAGAIN;
-	vma->vm_dentry = dget(file->f_dentry);
+		
+	vma->vm_file = file;
+	file->f_count++;
 	return 0;
 }
 
diff -u --recursive --new-file v2.1.96/linux/drivers/sbus/char/leo.c linux/drivers/sbus/char/leo.c
--- v2.1.96/linux/drivers/sbus/char/leo.c	Thu Sep  4 12:54:48 1997
+++ linux/drivers/sbus/char/leo.c	Tue Apr 14 17:44:22 1998
@@ -1,4 +1,4 @@
-/* $Id: leo.c,v 1.25 1997/08/22 17:33:58 jj Exp $
+/* $Id: leo.c,v 1.26 1998/03/10 20:18:29 jj Exp $
  * leo.c: SUNW,leo 24/8bit frame buffer driver
  *
  * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
@@ -224,7 +224,8 @@
 		page += map_size;
 	}
 
-	vma->vm_dentry = dget(file->f_dentry);
+	vma->vm_file = file;
+	file->f_count++;
         return 0;
 }
 
diff -u --recursive --new-file v2.1.96/linux/drivers/sbus/char/mach64.c linux/drivers/sbus/char/mach64.c
--- v2.1.96/linux/drivers/sbus/char/mach64.c	Mon Feb 23 18:12:06 1998
+++ linux/drivers/sbus/char/mach64.c	Tue Apr 14 17:44:22 1998
@@ -1,4 +1,4 @@
-/* $Id: mach64.c,v 1.11 1997/10/17 04:13:35 davem Exp $
+/* $Id: mach64.c,v 1.17 1998/04/06 06:42:23 davem Exp $
  * mach64.c: Ultra/PCI Mach64 console driver.
  *
  * Just about all of this is from the PPC/mac driver, see that for
@@ -12,7 +12,6 @@
 #include <linux/kernel.h>
 #include <linux/types.h>
 #include <linux/pci.h>
-#include <linux/bios32.h>
 #include <linux/string.h>
 #include <linux/tty.h>
 #include <linux/console.h>
@@ -81,8 +80,8 @@
 	if (vma->vm_offset & ~PAGE_MASK)
 		return -ENXIO;
 
-	if (vma->vm_offset == mach64_pci_iobase) {
-		addr = __pa(pcivga_iobase);
+	if (vma->vm_offset == (mach64_pci_iobase & PAGE_MASK)) {
+		addr = __pa((pcivga_iobase & PAGE_MASK));
 		size = PAGE_SIZE;
 	} else if (vma->vm_offset >= (mach64_pci_membase + 0x800000)) {
 		addr = __pa(pcivga_membase) - mach64_pci_membase
@@ -102,7 +101,8 @@
 	if (remap_page_range(vma->vm_start, addr, size, vma->vm_page_prot))
 		return -EAGAIN;
 
-	vma->vm_dentry = dget(file->f_dentry);
+	vma->vm_file = file;
+	file->f_count++;
 	return 0;
 }
 
@@ -123,6 +123,7 @@
 		pcivga_writeb(fb->color_map CM(i, 1), MACH64_REGOFF + DAC_DATA);
 		pcivga_writeb(fb->color_map CM(i, 2), MACH64_REGOFF + DAC_DATA);
 	}
+	mach64_idle();
 }
 
 static void
@@ -172,11 +173,11 @@
 	unsigned int tmp;
 
 	memset(&mach64, 0, sizeof(mach64));
-	for(pdev = pci_devices; pdev; pdev = pdev->next) {
-		if((pdev->vendor == PCI_VENDOR_ID_ATI) &&
-		   (pdev->device == PCI_DEVICE_ID_ATI_264VT))
-			break;
-	}
+
+	pdev = pci_find_device(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_264VT, 0);
+	if(!pdev)
+		pdev = pci_find_device(PCI_VENDOR_ID_ATI,
+				       PCI_DEVICE_ID_ATI_215GT, 0);
 	if(!pdev)
 		return -1;
 
@@ -222,15 +223,15 @@
 	fb->info.private = (void *)&mach64;
 	fb->base = pcivga_membase + MACH64_BE_FBOFF;
 
-	if ((pcivga_readl(MACH64_REGOFF + CONFIG_CHIP_ID)
-					& CFG_CHIP_TYPE) == MACH64_VT_ID)
-		mach64.flags |= MACH64_MASK_VT;
-
-	/*
-	 * Fix the PROM's idea of MEM_CNTL settings...
-	 */
-	tmp = pcivga_readl(MACH64_REGOFF + MEM_CNTL);
-	switch (tmp & 0xf) {
+	mach64.chip_type = pcivga_readl(MACH64_REGOFF + CONFIG_CHIP_ID)
+							& CFG_CHIP_TYPE;
+
+	if (mach64.chip_type == MACH64_VT_ID) {
+		/*
+		 * Fix the PROM's idea of MEM_CNTL settings...
+		 */
+		tmp = pcivga_readl(MACH64_REGOFF + MEM_CNTL);
+		switch (tmp & 0xf) {
 		case 3:
 			tmp = (tmp & ~(0xf)) | 2;
 			break;
@@ -245,11 +246,14 @@
 			break;
 		default:
 			break;
+		}
+		tmp &= ~(0x00f00000);
+		pcivga_writel(tmp, MACH64_REGOFF + MEM_CNTL);
 	}
-	tmp &= ~(0x00f00000);
-	pcivga_writel(tmp, MACH64_REGOFF + MEM_CNTL);
 
-	switch(tmp & MEM_SIZE_ALIAS) {
+	tmp = pcivga_readl(MACH64_REGOFF + MEM_CNTL);
+	if (mach64.chip_type != MACH64_GT_ID) {
+		switch(tmp & MEM_SIZE_ALIAS) {
 		case MEM_SIZE_512K:
 			mach64.total_vram = 0x80000;
 			break;
@@ -271,10 +275,35 @@
 		default:
 			mach64.total_vram = 0x80000;
 			break;
+		}
+	} else {
+		switch(tmp & MEM_SIZE_ALIAS_GTB) {
+		case MEM_SIZE_512K_GTB:
+			mach64.total_vram = 0x80000;
+			break;
+		case MEM_SIZE_1M_GTB:
+			mach64.total_vram = 0x100000;
+			break;
+		case MEM_SIZE_2M_GTB:
+			mach64.total_vram = 0x200000;
+			break;
+		case MEM_SIZE_4M_GTB:
+			mach64.total_vram = 0x400000;
+			break;
+		case MEM_SIZE_6M_GTB:
+			mach64.total_vram = 0x600000;
+			break;
+		case MEM_SIZE_8M_GTB:
+			mach64.total_vram = 0x800000;
+			break;
+		default:
+			mach64.total_vram = 0x80000;
+			break;
+		}
 	}
 
-	printk("mach64_init: total_vram[%08x] is_vt_chip[%d]\n",
-		mach64.total_vram, mach64.flags & MACH64_MASK_VT ? 1 : 0);
+	printk("mach64_init: chip_type[%04x], total_vram[%08x]\n",
+		mach64.chip_type, mach64.total_vram);
 
 #if 0
 	mach64_test(fb);
diff -u --recursive --new-file v2.1.96/linux/drivers/sbus/char/mach64.h linux/drivers/sbus/char/mach64.h
--- v2.1.96/linux/drivers/sbus/char/mach64.h	Mon Jan 12 15:15:45 1998
+++ linux/drivers/sbus/char/mach64.h	Tue Apr 14 17:44:22 1998
@@ -1,4 +1,4 @@
-/* $Id: mach64.h,v 1.4 1997/10/04 08:51:30 ecd Exp $
+/* $Id: mach64.h,v 1.5 1998/04/01 05:52:58 ecd Exp $
  * mach64.h: Ultra/PCI mach64 driver constants etc.
  *
  * Copyright 1997 David S. Miller (davem@caip.rutgers.edu)
@@ -9,12 +9,10 @@
 
 struct mach64_info {
 	unsigned int color_mode;
-	unsigned int flags;
+	unsigned int chip_type;
 	unsigned int total_vram;
 };
 
-/* The mach64_info flag bits. */
-#define MACH64_MASK_VT	0x00000001
 
 /* NON-GUI MEMORY MAPPED Registers - expressed in BYTE offsets */
 
@@ -375,15 +373,6 @@
 #define MEM_BNDRY_512K          0x00020000
 #define MEM_BNDRY_1M            0x00030000
 #define MEM_BNDRY_EN            0x00040000
-
-/* ATI PCI constants */
-#define PCI_ATI_VENDOR_ID	0x1002
-#define PCI_MACH64_GX		0x4758
-#define PCI_MACH64_CX		0x4358
-#define PCI_MACH64_CT		0x4354
-#define PCI_MACH64_ET		0x4554
-#define PCI_MACH64_VT		0x5654
-#define PCI_MACH64_GT		0x4754
 
 /* CONFIG_CHIP_ID register constants */
 #define CFG_CHIP_TYPE		0x0000FFFF
diff -u --recursive --new-file v2.1.96/linux/drivers/sbus/char/pcicons.c linux/drivers/sbus/char/pcicons.c
--- v2.1.96/linux/drivers/sbus/char/pcicons.c	Mon Jan 12 15:15:45 1998
+++ linux/drivers/sbus/char/pcicons.c	Tue Apr 14 17:44:22 1998
@@ -1,4 +1,4 @@
-/* $Id: pcicons.c,v 1.10 1997/10/04 08:52:57 ecd Exp $
+/* $Id: pcicons.c,v 1.13 1998/04/01 06:55:11 ecd Exp $
  * pcicons.c: PCI specific probing and console operations layer.
  *
  * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
@@ -42,6 +42,7 @@
 static int cursor_pos = -1;
 
 extern int serial_console;
+extern struct console vt_console_driver;
 
 static void pci_install_consops(void);
 static int (*fbuf_offset)(int);
@@ -191,24 +192,32 @@
 
 static void pci_get_scrmem(int currcons)
 {
+	struct vc_data *vcd = vc_cons[currcons].d;
+	
 	memcpyw((unsigned short *)vc_scrbuf[currcons],
-		(unsigned short *)origin, video_screen_size);
-	origin = video_mem_start = (unsigned long)vc_scrbuf[currcons];
-	scr_end = video_mem_end = video_mem_start + video_screen_size;
-	pos = origin + y * video_size_row + (x << 1);
+		(unsigned short *)vcd->vc_origin, video_screen_size);
+	vcd->vc_origin = vcd->vc_video_mem_start = 
+		(unsigned long)vc_scrbuf[currcons];
+	vcd->vc_scr_end = vcd->vc_video_mem_end = 
+		vcd->vc_video_mem_start + video_screen_size;
+	vcd->vc_pos = 
+		vcd->vc_origin + vcd->vc_y * video_size_row + (vcd->vc_x << 1);
 }
 
 static void pci_set_scrmem(int currcons, long offset)
 {
+	struct vc_data *vcd = vc_cons[currcons].d;
+	
 	if (video_mem_term - video_mem_base < offset + video_screen_size)
 		offset = 0;
 	memcpyw((unsigned short *)(video_mem_base + offset),
-		(unsigned short *) origin, video_screen_size);
-	video_mem_start = video_mem_base;
-	video_mem_end = video_mem_term;
-	origin = video_mem_base + offset;
-	scr_end = origin + video_screen_size;
-	pos = origin + y * video_size_row + (x << 1);
+		(unsigned short *) vcd->vc_origin, video_screen_size);
+	vcd->vc_video_mem_start = video_mem_base;
+	vcd->vc_video_mem_end = video_mem_term;
+	vcd->vc_origin = video_mem_base + offset;
+	vcd->vc_scr_end = vcd->vc_origin + video_screen_size;
+	vcd->vc_pos = 
+		vcd->vc_origin + vcd->vc_y * video_size_row + (vcd->vc_x << 1);
 }
 
 static void pci_invert_cursor(int cpos)
@@ -252,15 +261,17 @@
 	unsigned long flags;
 	int old_cursor;
 
-	if (currcons != fg_console || console_blanked || vcmode == KD_GRAPHICS)
+	if (currcons != fg_console || console_blanked || 
+	    vt_cons[currcons]->vc_mode == KD_GRAPHICS)
 		return;
 
 	save_flags(flags); cli();
-	if (!deccm) {
+	if (!vc_cons[currcons].d->vc_deccm) {
 		pci_hide_cursor();
 	} else {
 		old_cursor = cursor_pos;
-		cursor_pos = (pos - video_mem_base) >> 1;
+		cursor_pos = 
+			(vc_cons[currcons].d->vc_pos - video_mem_base) >> 1;
 		if (old_cursor != -1)
 			pci_invert_cursor(-1);
 		pci_invert_cursor(cursor_pos);
@@ -412,9 +423,12 @@
 {
 	fbinfo_t *fb = &fbinfo[n];
 
+#if 0
 	if (!n) {
 		pci_clear_screen();
-	} else if (fb->base) {
+	} else
+#endif
+	if (fb->base) {
 		memset((void *)fb->base,
 		       (fb->type.fb_depth == 1) ?
 		       ~(0) : reverse_color_table[0],
@@ -545,7 +559,7 @@
 				+ 10 * (ncpus - 1);
 
 	for (p = logo_banner; *p; p++, ush++) {
-		*ush = (attr << 8) + *p;
+		*ush = (vc_cons[currcons].d->vc_attr << 8) + *p;
 		pci_blitc(*ush, (unsigned long)ush);
 	}
 
@@ -553,6 +567,8 @@
 		ush = (unsigned short *)video_mem_base + i * video_num_columns;
 		memset(ush, 0xff, 20);
 	}
+
+	register_console(&vt_console_driver);
 }
 
 unsigned long pcivga_iobase = 0;
diff -u --recursive --new-file v2.1.96/linux/drivers/sbus/char/pcikbd.c linux/drivers/sbus/char/pcikbd.c
--- v2.1.96/linux/drivers/sbus/char/pcikbd.c	Tue Mar 10 10:03:32 1998
+++ linux/drivers/sbus/char/pcikbd.c	Tue Apr 14 17:44:22 1998
@@ -1,4 +1,4 @@
-/* $Id: pcikbd.c,v 1.12 1997/12/27 16:28:27 jj Exp $
+/* $Id: pcikbd.c,v 1.16 1998/04/01 04:12:40 davem Exp $
  * pcikbd.c: Ultra/AX PC keyboard support.
  *
  * Copyright (C) 1997  Eddie C. Dost  (ecd@skynet.be)
@@ -953,14 +953,18 @@
 		return -ENODEV;
 
 	len = prom_getproperty(node, "keyboard", prop, sizeof(prop));
-	if (len > 0)
-		kbnode = prom_pathtoinode(prop);
+	if (len > 0) {
+		prop[len] = 0;
+		kbnode = prom_finddevice(prop);
+	}
 	if (!kbnode)
 		return -ENODEV;
 
 	len = prom_getproperty(node, "mouse", prop, sizeof(prop));
-	if (len > 0)
-		msnode = prom_pathtoinode(prop);
+	if (len > 0) {
+		prop[len] = 0;
+		msnode = prom_finddevice(prop);
+	}
 	if (!msnode)
 		return -ENODEV;
 
@@ -969,6 +973,15 @@
 	 */
         node = prom_getchild(prom_root_node);
 	pnode = prom_searchsiblings(node, "pci");
+
+	/*
+	 * Check for SUNW,sabre on Ultra5/10/AXi.
+	 */
+	len = prom_getproperty(pnode, "model", prop, sizeof(prop));
+	if ((len > 0) && !strncmp(prop, "SUNW,sabre", len)) {
+		pnode = prom_getchild(pnode);
+		pnode = prom_searchsiblings(pnode, "pci");
+	}
 
 	/*
 	 * For each PCI bus...
diff -u --recursive --new-file v2.1.96/linux/drivers/sbus/char/sab82532.c linux/drivers/sbus/char/sab82532.c
--- v2.1.96/linux/drivers/sbus/char/sab82532.c	Mon Jan 12 15:15:45 1998
+++ linux/drivers/sbus/char/sab82532.c	Tue Apr 14 17:44:22 1998
@@ -1,4 +1,4 @@
-/* $Id: sab82532.c,v 1.13 1997/12/30 09:37:49 ecd Exp $
+/* $Id: sab82532.c,v 1.17 1998/04/01 06:55:12 ecd Exp $
  * sab82532.c: ASYNC Driver for the SIEMENS SAB82532 DUSCC.
  *
  * Copyright (C) 1997  Eddie C. Dost  (ecd@skynet.be)
@@ -46,9 +46,10 @@
 
 /* Set of debugging defines */
 #undef SERIAL_DEBUG_OPEN
-#undef SERIAL_DEBUG_INTR
 #undef SERIAL_DEBUG_FLOW
 #undef SERIAL_DEBUG_WAIT_UNTIL_SENT
+#undef SERIAL_DEBUG_SEND_BREAK
+#undef SERIAL_DEBUG_INTR
 
 static void change_speed(struct sab82532 *info);
 static void sab82532_wait_until_sent(struct tty_struct *tty, int timeout);
@@ -159,6 +160,47 @@
 
 #define NR_EBRG_VALUES	(sizeof(ebrg_table)/sizeof(struct ebrg_struct))
 
+#define SAB82532_MAX_TEC_DELAY	2000	/* 2 ms */
+
+static __inline__ void sab82532_tec_wait(struct sab82532 *info)
+{
+	int count = SAB82532_MAX_TEC_DELAY;
+	while ((info->regs->r.star & SAB82532_STAR_TEC) && --count)
+		udelay(1);
+}
+
+static __inline__ void sab82532_start_tx(struct sab82532 *info)
+{
+	unsigned long flags;
+	int i;
+
+	save_flags(flags); cli();
+
+	if (info->xmit_cnt <= 0)
+		goto out;
+
+	if (!(info->regs->r.star & SAB82532_STAR_XFW))
+		goto out;
+
+	info->all_sent = 0;
+	for (i = 0; i < info->xmit_fifo_size; i++) {
+		info->regs->w.xfifo[i] = info->xmit_buf[info->xmit_tail++];
+		info->xmit_tail &= (SERIAL_XMIT_SIZE - 1);
+		info->icount.tx++;
+		if (--info->xmit_cnt <= 0)
+			break;
+	}
+
+	/* Issue a Transmit Frame command. */
+	if (info->regs->r.star & SAB82532_STAR_CEC)
+		udelay(1);
+	info->regs->w.cmdr = SAB82532_CMDR_XF;
+
+out:
+	restore_flags(flags);
+}
+
+
 /*
  * ------------------------------------------------------------
  * sab82532_stop() and sab82532_start()
@@ -192,6 +234,7 @@
 	save_flags(flags); cli();
 	info->interrupt_mask1 &= ~(SAB82532_IMR1_XPR);
 	info->regs->w.imr1 = info->interrupt_mask1;
+	sab82532_start_tx(info);
 	restore_flags(flags);
 }
 
@@ -356,6 +399,11 @@
 {
 	int i;
 
+	if (stat->sreg.isr1 & SAB82532_ISR1_ALLS)
+		info->all_sent = 1;
+	if (!(info->regs->r.star & SAB82532_STAR_XFW))
+		return;
+
 	if (!info->tty) {
 		info->interrupt_mask1 |= SAB82532_IMR1_XPR;
 		info->regs->w.imr1 = info->interrupt_mask1;
@@ -364,8 +412,6 @@
 
 	if ((info->xmit_cnt <= 0) || info->tty->stopped ||
 	    info->tty->hw_stopped) {
-		if (stat->sreg.isr1 & SAB82532_ISR1_ALLS)
-			info->all_sent = 1;
 		info->interrupt_mask1 |= SAB82532_IMR1_XPR;
 		info->regs->w.imr1 = info->interrupt_mask1;
 		return;
@@ -494,6 +540,7 @@
 						     RS_EVENT_WRITE_WAKEUP);
 				info->interrupt_mask1 &= ~(SAB82532_IMR1_XPR);
 				info->regs->w.imr1 = info->interrupt_mask1;
+				sab82532_start_tx(info);
 			}
 		} else {
 			if (!(info->cts)) {
@@ -641,8 +688,7 @@
 	 */
 	if (info->regs->r.star & SAB82532_STAR_CEC)
 		udelay(1);
-	while (info->regs->r.star & SAB82532_STAR_TEC)
-		udelay(1);
+	sab82532_tec_wait(info);
 
 	/*
 	 * Clear the FIFO buffers.
@@ -939,8 +985,7 @@
 	save_flags(flags); cli();
 	if (info->regs->r.star & SAB82532_STAR_CEC)
 		udelay(1);
-	while (info->regs->r.star & SAB82532_STAR_TEC)
-		udelay(1);
+	sab82532_tec_wait(info);
 	info->regs->w.dafo = dafo;
 	info->regs->w.bgr = ebrg & 0xff;
 	info->regs->rw.ccr2 &= ~(0xc0);
@@ -996,6 +1041,7 @@
 	save_flags(flags); cli();
 	info->interrupt_mask1 &= ~(SAB82532_IMR1_XPR);
 	info->regs->w.imr1 = info->interrupt_mask1;
+	sab82532_start_tx(info);
 	restore_flags(flags);
 }
 
@@ -1044,10 +1090,10 @@
 	if (from_user)
 		up(&tmp_buf_sem);
 
-	if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped &&
-	    (info->interrupt_mask1 & SAB82532_IMR1_XPR)) {
+	if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped) {
 		info->interrupt_mask1 &= ~(SAB82532_IMR1_XPR);
 		info->regs->w.imr1 = info->interrupt_mask1;
+		sab82532_start_tx(info);
 	}
 
 	restore_flags(flags);
@@ -1098,13 +1144,15 @@
 static void sab82532_send_xchar(struct tty_struct *tty, char ch)
 {
 	struct sab82532 *info = (struct sab82532 *)tty->driver_data;
+	unsigned long flags;
 
 	if (serial_paranoia_check(info, tty->device, "sab82532_send_xchar"))
 		return;
 
-	while (info->regs->r.star & SAB82532_STAR_TEC)
-		udelay(1);
+	save_flags(flags); cli();
+	sab82532_tec_wait(info);
 	info->regs->w.tic = ch;
+	restore_flags(flags);
 }
 
 /*
@@ -1308,6 +1356,9 @@
 	if (!info->regs)
 		return;
 	info->regs->rw.dafo |= SAB82532_DAFO_XBRK;
+#ifdef SERIAL_DEBUG_SEND_BREAK
+	printk("begin_break: jiffies=%lu\n", jiffies);
+#endif
 }
 
 /*
@@ -1318,6 +1369,9 @@
 	if (!info->regs)
 		return;
 	info->regs->rw.dafo &= ~(SAB82532_DAFO_XBRK);
+#ifdef SERIAL_DEBUG_SEND_BREAK
+	printk("end_break: jiffies=%lu\n", jiffies);
+#endif
 }
 
 static int sab82532_ioctl(struct tty_struct *tty, struct file * file,
@@ -1850,6 +1904,7 @@
 #ifdef SERIAL_DEBUG_OPEN
 	printk("sab82532_open: count = %d\n", info->count);
 #endif
+
 	line = MINOR(tty->device) - tty->driver.minor_start;
 	if ((line < 0) || (line >= NR_PORTS))
 		return -ENODEV;
@@ -1991,7 +2046,7 @@
 	int i, len = 0;
 	off_t	begin = 0;
 
-	len += sprintf(page, "serinfo:1.0 driver:%s\n", "$Revision: 1.13 $");
+	len += sprintf(page, "serinfo:1.0 driver:%s\n", "$Revision: 1.17 $");
 	for (i = 0; i < NR_PORTS && len < 4000; i++) {
 		len += line_info(page + len, sab82532_table[i]);
 		if (len+begin > off+count)
@@ -2082,7 +2137,7 @@
 
 __initfunc(static inline void show_serial_version(void))
 {
-	char *revision = "$Revision: 1.13 $";
+	char *revision = "$Revision: 1.17 $";
 	char *version, *p;
 
 	version = strchr(revision, ' ');
@@ -2219,11 +2274,22 @@
 __initfunc(int sab82532_probe(unsigned long *memory_start))
 {
 	int node, enode, snode;
+	char model[32];
+	int len;
 
         node = prom_getchild(prom_root_node);
 	node = prom_searchsiblings(node, "pci");
 
 	/*
+	 * Check for SUNW,sabre on Ultra 5/10/AXi.
+	 */
+	len = prom_getproperty(node, "model", model, sizeof(model));
+	if ((len > 0) && !strncmp(model, "SUNW,sabre", len)) {
+        	node = prom_getchild(node);
+		node = prom_searchsiblings(node, "pci");
+	}
+
+	/*
 	 * For each PCI bus...
 	 */
 	while (node) {
@@ -2299,12 +2365,15 @@
 
 #ifdef CONFIG_SERIAL_CONSOLE
 
-static void
+static __inline__ void
 sab82532_console_putchar(struct sab82532 *info, char c)
 {
-	while (info->regs->r.star & SAB82532_STAR_TEC)
-		udelay(1);
+	unsigned long flags;
+
+	save_flags(flags); cli();
+	sab82532_tec_wait(info);
 	info->regs->w.tic = c;
+	restore_flags(flags);
 }
 
 static void
@@ -2320,8 +2389,7 @@
 			sab82532_console_putchar(info, '\r');
 		sab82532_console_putchar(info, *s++);
 	}
-	while (info->regs->r.star & SAB82532_STAR_TEC)
-		udelay(1);
+	sab82532_tec_wait(info);
 }
 
 static int
@@ -2440,8 +2508,7 @@
 	save_flags(flags); cli();
 	if (info->regs->r.star & SAB82532_STAR_CEC)
 		udelay(1);
-	while (info->regs->r.star & SAB82532_STAR_TEC)
-		udelay(1);
+	sab82532_tec_wait(info);
 	info->regs->w.dafo = dafo;
 	info->regs->w.bgr = ebrg & 0xff;
 	info->regs->rw.ccr2 &= ~(0xc0);
diff -u --recursive --new-file v2.1.96/linux/drivers/sbus/char/sbuscons.c linux/drivers/sbus/char/sbuscons.c
--- v2.1.96/linux/drivers/sbus/char/sbuscons.c	Mon Jan 12 15:15:45 1998
+++ linux/drivers/sbus/char/sbuscons.c	Tue Apr 14 17:44:22 1998
@@ -1,11 +1,11 @@
-/* $Id: sbuscons.c,v 1.10 1998/01/07 06:37:22 baccala Exp $
+/* $Id: sbuscons.c,v 1.15 1998/03/31 01:49:50 davem Exp $
  * sbuscons.c: Routines specific to SBUS frame buffer consoles.
  *
  * Copyright (C) 1995 Peter Zaitcev (zaitcev@lab.ipmce.su)
  * Copyright (C) 1995,1996,1997 David S. Miller (davem@caip.rutgers.edu)
  * Copyright (C) 1995, 1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
  * Copyright (C) 1996 Dave Redman (djhr@tadpole.co.uk)
- * Copyright (C) 1996 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1996,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
  * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be)
  *
  * Added font loading Nov/21, Miguel de Icaza (miguel@nuclecu.unam.mx)
@@ -72,6 +72,9 @@
 #include <asm/fbio.h>
 #include <asm/io.h>
 #include <asm/smp.h>
+#ifndef __sparc_v9__
+#include <asm/sun4paddr.h>
+#endif
 
 #include <linux/kbd_kern.h>
 #include <linux/vt_kern.h>
@@ -96,7 +99,6 @@
 
 static void sbus_install_consops(void);
 
-extern void register_console(void (*proc)(const char *));
 extern void console_print(const char *);
 extern void putconsxy(int, char *);
 extern unsigned char vga_font[];
@@ -277,14 +279,15 @@
 	int j, idx, oldpos;
 	unsigned long flags;
 
-	if (currcons != fg_console || console_blanked || vcmode == KD_GRAPHICS)
+	if (currcons != fg_console || console_blanked || 
+	    vt_cons[currcons]->vc_mode == KD_GRAPHICS)
 		return;
 
 	if (fbinfo[0].setcursor) {
-		if (!deccm)
+		if (!vc_cons[currcons].d->vc_deccm)
 			sbus_hide_cursor();
 		else {
-			idx = (pos - video_mem_base) >> 1;
+			idx = (vc_cons[currcons].d->vc_pos - video_mem_base) >> 1;
 			
 			sbus_hw_set_cursor(x_margin + ((idx % video_num_columns) << 3), y_margin + ((idx / video_num_columns) * CHAR_HEIGHT));
 		}
@@ -293,9 +296,9 @@
 
 	__save_and_cli(flags);
 
-	idx = (pos - video_mem_base) >> 1;
+	idx = (vc_cons[currcons].d->vc_pos - video_mem_base) >> 1;
 	oldpos = cursor_pos;
-	if (!deccm) {
+	if (!vc_cons[currcons].d->vc_deccm) {
 		sbus_hide_cursor ();
 		__restore_flags (flags);
 		return;
@@ -420,7 +423,7 @@
 
 	p = logo_banner;
 	for (; *p; p++, ush++) {
-		*ush = (attr << 8) + *p;
+		*ush = (vc_cons[currcons].d->vc_attr << 8) + *p;
 		sbus_blitc (*ush, (unsigned long) ush);
 	}
 	for (i = 0; i < 5; i++) {
@@ -435,24 +438,34 @@
  */
 static void sbus_get_scrmem(int currcons)
 {
+	struct vc_data *vcd = vc_cons[currcons].d;
+	
 	memcpyw((unsigned short *)vc_scrbuf[currcons],
-		(unsigned short *)origin, video_screen_size);
-	origin = video_mem_start = (unsigned long)vc_scrbuf[currcons];
-	scr_end = video_mem_end = video_mem_start + video_screen_size;
-	pos = origin + y*video_size_row + (x<<1);
+		(unsigned short *)vcd->vc_origin, 
+		video_screen_size);
+	vcd->vc_origin = vcd->vc_video_mem_start = 
+		(unsigned long)vc_scrbuf[currcons];
+	vcd->vc_scr_end = vcd->vc_video_mem_end = 
+		vcd->vc_video_mem_start + video_screen_size;
+	vcd->vc_pos = 
+		vcd->vc_origin + vcd->vc_y*video_size_row + (vcd->vc_x<<1);
 }
 
 static void sbus_set_scrmem(int currcons, long offset)
 {
+	struct vc_data *vcd = vc_cons[currcons].d;
+	
 	if (video_mem_term - video_mem_base < offset + video_screen_size)
 		offset = 0;
 	memcpyw((unsigned short *)(video_mem_base + offset),
-		(unsigned short *) origin, video_screen_size);
-	video_mem_start = video_mem_base;
-	video_mem_end = video_mem_term;
-	origin = video_mem_base + offset;
-	scr_end = origin + video_screen_size;
-	pos = origin + y*video_size_row + (x<<1);
+		(unsigned short *) vcd->vc_origin, 
+		video_screen_size);
+	vcd->vc_video_mem_start = video_mem_base;
+	vcd->vc_video_mem_end = video_mem_term;
+	vcd->vc_origin = video_mem_base + offset;
+	vcd->vc_scr_end = vcd->vc_origin + video_screen_size;
+	vcd->vc_pos = 
+		vcd->vc_origin + vcd->vc_y*video_size_row + (vcd->vc_x<<1);
 }
 
 /*
@@ -545,7 +558,20 @@
 	return 0;
 }
 
-static void sbus_clear_screen(void)
+#ifdef CONFIG_SUN4
+extern __inline__ void memset_screen(void *s, unsigned c, size_t n)
+{
+	unsigned *p = (unsigned *)s;
+	int i;
+	
+	for (i = n / 4; i > 0; i--)
+		*p++ = c;
+}
+#else
+#define memset_screen memset
+#endif
+
+void sbus_clear_screen(void)
 {
 	if (fbinfo[0].fill) {
 		int rects [4];
@@ -556,7 +582,7 @@
 		rects [3] = con_height;
 		(*fbinfo[0].fill)(reverse_color_table[0], 1, rects);
 	} else if (fbinfo[0].base && fbinfo[0].base_depth)
-		memset (con_fb_base,
+		memset_screen(con_fb_base,
 			(con_depth == 1) ? ~(0) : reverse_color_table[0],
 			(con_depth * con_height * con_width) / 8);
 	/* also clear out the "shadow" screen memory */
@@ -769,8 +795,7 @@
 	fbinfo [n].type.fb_height = prom_getintdefault(con_node, "height", 900);
 	fbinfo [n].type.fb_width  = prom_getintdefault(con_node, "width", 1152);
 	fbinfo [n].type.fb_depth  = (type == FBTYPE_SUN2BW) ? 1 : 8;
-	linebytes = prom_getint(con_node, "linebytes");
-	if (linebytes == -1) linebytes = fbinfo [n].type.fb_width;
+	linebytes = prom_getintdefault(con_node, "linebytes", fbinfo[n].type.fb_width * fbinfo[n].type.fb_depth / 8);
 	fbinfo [n].type.fb_size   = PAGE_ALIGN((linebytes) * (fbinfo [n].type.fb_height));
 	fbinfo [n].space = io;
 	fbinfo [n].blanked = 0;
@@ -925,12 +950,22 @@
 	u32 tmp;
 	u32 prom_console_node = 0;
 
-	if(SBus_chain == 0)
-		return -1;
-
-	sbdprom = 0;
-	switch(prom_vers) {
-	case PROM_V0:
+	if(SBus_chain == 0) {
+#ifdef CONFIG_SUN4
+		sparc_framebuffer_setup (1,0,FBTYPE_SUN2BW,NULL,SUN4_300_BWTWO_PHYSADDR,0,0,0);
+#else	
+		creator = creator_present();
+		if (!creator)
+			return -1;
+		sparc_framebuffer_setup (1, creator, FBTYPE_CREATOR,
+					 0, 0, 0, 0, prom_root_node);
+#endif
+	} else {
+		sbdprom = 0;
+		
+		switch(prom_vers) {
+		
+		case PROM_V0:
 		/* V0 proms are at sun4c only. Can skip many checks. */
 		con_type = FBTYPE_NOTYPE;
 		for_each_sbusdev(sbdp, SBus_chain) {
@@ -965,9 +1000,10 @@
 				break;
 		}
 		break;
-	case PROM_V2:
-	case PROM_V3:
-	case PROM_P1275:
+		
+		case PROM_V2:
+		case PROM_V3:
+		case PROM_P1275:
 		if (console_fb_path) {
 			char *q, c;
 
@@ -1073,8 +1109,10 @@
 						 prom_root_node);
 		}
 		break;
-	default:
+		
+		default:
 		return -1;
+		}
 	}
 	
 	if (fbinfo [0].type.fb_type == FBTYPE_NOTYPE) {
diff -u --recursive --new-file v2.1.96/linux/drivers/sbus/char/su.c linux/drivers/sbus/char/su.c
--- v2.1.96/linux/drivers/sbus/char/su.c	Mon Jan 12 15:15:45 1998
+++ linux/drivers/sbus/char/su.c	Tue Apr 14 17:44:22 1998
@@ -1,4 +1,4 @@
-/* $Id: su.c,v 1.4 1997/09/07 15:40:19 ecd Exp $
+/* $Id: su.c,v 1.8 1998/04/01 05:07:50 ecd Exp $
  * su.c: Small serial driver for keyboard/mouse interface on Ultra/AX
  *
  * Copyright (C) 1997  Eddie C. Dost  (ecd@skynet.be)
@@ -580,7 +580,7 @@
 __initfunc(int su_probe (unsigned long *memory_start))
 {
 	struct su_struct *info = su_table;
-        int node, enode, sunode;
+        int node, enode, tnode, sunode;
 	int kbnode = 0, msnode = 0;
 	int devices = 0;
 	char prop[128];
@@ -595,14 +595,18 @@
 		return -ENODEV;
 
 	len = prom_getproperty(node, "keyboard", prop, sizeof(prop));
-	if (len > 0)
-		kbnode = prom_pathtoinode(prop);
+	if (len > 0) {
+		prop[len] = 0;
+		kbnode = prom_finddevice(prop);
+	}
 	if (!kbnode)
 		return -ENODEV;
 
 	len = prom_getproperty(node, "mouse", prop, sizeof(prop));
-	if (len > 0)
-		msnode = prom_pathtoinode(prop);
+	if (len > 0) {
+		prop[len] = 0;
+		msnode = prom_finddevice(prop);
+	}
 	if (!msnode)
 		return -ENODEV;
 
@@ -613,6 +617,15 @@
 	node = prom_searchsiblings(node, "pci");
 
 	/*
+	 * Check for SUNW,sabre on Ultra 5/10/AXi.
+	 */
+	len = prom_getproperty(node, "model", prop, sizeof(prop));
+	if ((len > 0) && !strncmp(prop, "SUNW,sabre", len)) {
+        	node = prom_getchild(node);
+		node = prom_searchsiblings(node, "pci");
+	}
+
+	/*
 	 * For each PCI bus...
 	 */
 	while (node) {
@@ -624,7 +637,10 @@
 		 */
 		while (enode) {
 			sunode = prom_getchild(enode);
-			sunode = prom_searchsiblings(sunode, "su");
+			tnode = prom_searchsiblings(sunode, "su");
+			if (!tnode)
+				tnode = prom_searchsiblings(sunode, "su_pnp");
+			sunode = tnode;
 
 			/*
 			 * For each 'su' on this EBus...
@@ -651,7 +667,11 @@
 					goto found;
 
 				sunode = prom_getsibling(sunode);
-				sunode = prom_searchsiblings(sunode, "su");
+				tnode = prom_searchsiblings(sunode, "su");
+				if (!tnode)
+					tnode = prom_searchsiblings(sunode,
+								    "su_pnp");
+				sunode = tnode;
 			}
 			enode = prom_getsibling(enode);
 			enode = prom_searchsiblings(enode, "ebus");
diff -u --recursive --new-file v2.1.96/linux/drivers/sbus/char/suncons.c linux/drivers/sbus/char/suncons.c
--- v2.1.96/linux/drivers/sbus/char/suncons.c	Mon Jan 12 15:15:45 1998
+++ linux/drivers/sbus/char/suncons.c	Tue Apr 14 17:44:22 1998
@@ -1,4 +1,4 @@
-/* $Id: suncons.c,v 1.77 1997/12/19 07:32:59 ecd Exp $
+/* $Id: suncons.c,v 1.79 1998/01/30 10:59:23 jj Exp $
  * suncons.c: Sparc platform console generic layer.
  *
  * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
@@ -28,6 +28,9 @@
 int fbinfos;
 unsigned int linux_logo_colors __initdata = LINUX_LOGO_COLORS;
 char logo_banner[] __initdata = linux_logo_banner;
+#ifdef CONFIG_PCI
+static int cons_type __initdata = 0;
+#endif
 
 extern struct console vt_console_driver;
 
@@ -344,6 +347,7 @@
 
 	if(sbus_console_probe()) {
 #ifdef CONFIG_PCI
+		cons_type = 1;
 		pci_console_inithook();
 		return memory_start;
 #else
@@ -363,6 +367,11 @@
 	/* Nothing to do in this case. */
 	if (!con_is_present())
 		return memory_start;
+		
+	if (!cons_type) {
+		/* Some console was already found on SBUS or UPA */
+		return memory_start;
+	}
 
 	if(pci_console_probe()) {
 		prom_printf("Could not probe PCI console, bailing out...\n");
diff -u --recursive --new-file v2.1.96/linux/drivers/sbus/char/sunkbd.c linux/drivers/sbus/char/sunkbd.c
--- v2.1.96/linux/drivers/sbus/char/sunkbd.c	Tue Mar 10 10:03:32 1998
+++ linux/drivers/sbus/char/sunkbd.c	Tue Apr 14 17:44:22 1998
@@ -37,7 +37,6 @@
 
 #ifdef CONFIG_PCI
 #include <linux/pci.h>
-#include <linux/bios32.h>
 #include <asm/pbm.h>
 #include <asm/ebus.h>
 #endif
diff -u --recursive --new-file v2.1.96/linux/drivers/sbus/char/tcx.c linux/drivers/sbus/char/tcx.c
--- v2.1.96/linux/drivers/sbus/char/tcx.c	Thu Sep  4 12:54:49 1997
+++ linux/drivers/sbus/char/tcx.c	Tue Apr 14 17:44:22 1998
@@ -1,4 +1,4 @@
-/* $Id: tcx.c,v 1.20 1997/08/22 15:55:14 jj Exp $
+/* $Id: tcx.c,v 1.22 1998/03/10 20:18:47 jj Exp $
  * tcx.c: SUNW,tcx 24/8bit frame buffer driver
  *
  * Copyright (C) 1996 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
@@ -33,6 +33,8 @@
 /* THC definitions */
 #define TCX_THC_MISC_REV_SHIFT       16
 #define TCX_THC_MISC_REV_MASK        15
+#define TCX_THC_MISC_VSYNC_DIS       (1 << 25)
+#define TCX_THC_MISC_HSYNC_DIS       (1 << 24)
 #define TCX_THC_MISC_RESET           (1 << 12)
 #define TCX_THC_MISC_VIDEO           (1 << 10)
 #define TCX_THC_MISC_SYNC            (1 << 9)
@@ -174,7 +176,8 @@
 		page += map_size;
 	}
 
-	vma->vm_dentry = dget(file->f_dentry);
+	vma->vm_file = file;
+	file->f_count++;
         return 0;
 }
 
@@ -246,11 +249,16 @@
 tcx_blank (fbinfo_t *fb)
 {
 	fb->info.tcx.thc->thc_misc &= ~TCX_THC_MISC_VIDEO;
+	/* This should put us in power-save */
+        fb->info.tcx.thc->thc_misc |= TCX_THC_MISC_VSYNC_DIS;
+        fb->info.tcx.thc->thc_misc |= TCX_THC_MISC_HSYNC_DIS;
 }
 
 static void
 tcx_unblank (fbinfo_t *fb)
 {
+        fb->info.tcx.thc->thc_misc &= ~TCX_THC_MISC_VSYNC_DIS;
+        fb->info.tcx.thc->thc_misc &= ~TCX_THC_MISC_HSYNC_DIS;
 	fb->info.tcx.thc->thc_misc |= TCX_THC_MISC_VIDEO;
 }
 
diff -u --recursive --new-file v2.1.96/linux/drivers/sbus/char/vfc_dev.c linux/drivers/sbus/char/vfc_dev.c
--- v2.1.96/linux/drivers/sbus/char/vfc_dev.c	Tue Feb 17 13:12:46 1998
+++ linux/drivers/sbus/char/vfc_dev.c	Tue Apr 14 17:44:22 1998
@@ -582,7 +582,8 @@
 	if(ret)
 		return -EAGAIN;
 
-	vma->vm_dentry = dget(file->f_dentry);
+	vma->vm_file = file;
+	file->f_count++;
 	return 0;
 }
 
diff -u --recursive --new-file v2.1.96/linux/drivers/sbus/char/weitek.c linux/drivers/sbus/char/weitek.c
--- v2.1.96/linux/drivers/sbus/char/weitek.c	Thu Sep  4 12:54:49 1997
+++ linux/drivers/sbus/char/weitek.c	Tue Apr 14 17:44:22 1998
@@ -1,4 +1,4 @@
-/* $Id: weitek.c,v 1.15 1997/07/22 06:14:11 davem Exp $
+/* $Id: weitek.c,v 1.16 1998/03/10 20:18:54 jj Exp $
  * weitek.c: Tadpole P9100/P9000 console driver
  *
  * Copyright (C) 1996 David Redman (djhr@tadpole.co.uk)
@@ -84,7 +84,8 @@
 		page += map_size;
 	}
 
-	vma->vm_dentry = dget(file->f_dentry);
+	vma->vm_file = file;
+	file->f_count++;
 	return 0;
 }
 #endif
diff -u --recursive --new-file v2.1.96/linux/drivers/sbus/char/zs.c linux/drivers/sbus/char/zs.c
--- v2.1.96/linux/drivers/sbus/char/zs.c	Mon Jan 12 15:15:45 1998
+++ linux/drivers/sbus/char/zs.c	Tue Apr 14 17:44:22 1998
@@ -1,4 +1,4 @@
-/* $Id: zs.c,v 1.15 1997/12/22 16:09:34 jj Exp $
+/* $Id: zs.c,v 1.20 1998/02/25 23:51:57 ecd Exp $
  * zs.c: Zilog serial port driver for the Sparc.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -37,7 +37,7 @@
 #ifdef __sparc_v9__
 #include <asm/fhc.h>
 #ifdef CONFIG_PCI
-#include <linux/bios32.h>
+#include <linux/pci.h>
 #endif
 #endif
 
@@ -70,7 +70,12 @@
 #ifdef CONFIG_SERIAL_CONSOLE
 static struct console zs_console;
 static int zs_console_init(void);
-#endif
+
+/*
+ * Define this to get the zs_fair_output() functionality.
+ */
+#undef SERIAL_CONSOLE_FAIR_OUTPUT
+#endif /* CONFIG_SERIAL_CONSOLE */
 
 static unsigned char kgdb_regs[16] = {
 	0, 0, 0,                     /* write 0, 1, 2 */
@@ -256,14 +261,14 @@
 	restore_flags(flags);
 }
 
+#define ZS_PUT_CHAR_MAX_DELAY	2000	/* 10 ms */
+
 static inline void zs_put_char(struct sun_zschannel *channel, char ch)
 {
-	int loops = 0;
+	int loops = ZS_PUT_CHAR_MAX_DELAY;
 
-	while((channel->control & Tx_BUF_EMP) == 0 && loops < 10000) {
-		loops++;
+	while((channel->control & Tx_BUF_EMP) == 0 && --loops)
 		udelay(5);
-	}
 	channel->data = ch;
 	udelay(5);
 }
@@ -408,6 +413,10 @@
 extern void breakpoint(void);  /* For the KGDB frame character */
 #endif
 
+#ifdef CONFIG_MAGIC_SYSRQ
+static int serial_sysrq;
+#endif
+
 static _INLINE_ void receive_chars(struct sun_serial *info, struct pt_regs *regs)
 {
 	struct tty_struct *tty = info->tty;
@@ -447,8 +456,6 @@
 		}
 		if(info->is_cons) {
 #ifdef CONFIG_MAGIC_SYSRQ
-			static int serial_sysrq;
-
 			if (!ch) {
 				serial_sysrq = 1;
 				return;
@@ -566,8 +573,13 @@
 	 * 'break asserted' status change interrupt, call
 	 * the boot prom.
 	 */
-	if((status & BRK_ABRT) && info->break_abort)
+	if((status & BRK_ABRT) && info->break_abort) {
+#ifdef CONFIG_MAGIC_SYSRQ
+		serial_sysrq = 1;
+#else
 		batten_down_hatches();
+#endif
+	}
 
 	/* XXX Whee, put in a buffer somewhere, the status information
 	 * XXX whee whee whee... Where does the information go...
@@ -1037,12 +1049,12 @@
 	if (serial_paranoia_check(info, tty->device, "zs_flush_chars"))
 		return;
 
+	save_flags(flags); cli();
 	if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||
 	    !info->xmit_buf)
-		return;
+		goto out;
 
 	/* Enable transmitter */
-	save_flags(flags); cli();
 	info->curregs[1] |= TxINT_ENAB|EXT_INT_ENAB;
 	write_zsreg(info->zs_channel, 1, info->curregs[1]);
 	info->curregs[5] |= TxENAB;
@@ -1059,6 +1071,7 @@
 	info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1);
 	info->xmit_cnt--;
 
+out:
 	restore_flags(flags);
 }
 
@@ -1100,21 +1113,21 @@
 		total += c;
 	}
 
+	cli();		
 	if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped) {
 		/* Enable transmitter */
 		info->curregs[1] |= TxINT_ENAB|EXT_INT_ENAB;
 		write_zsreg(info->zs_channel, 1, info->curregs[1]);
 		info->curregs[5] |= TxENAB;
 		write_zsreg(info->zs_channel, 5, info->curregs[5]);
-	}
 #if 1
-	if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped) {
 		zs_put_char(info->zs_channel,
 			    info->xmit_buf[info->xmit_tail++]);
 		info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1);
 		info->xmit_cnt--;
-	}
 #endif
+	}
+
 	restore_flags(flags);
 	return total;
 }
@@ -1819,7 +1832,7 @@
 
 static void show_serial_version(void)
 {
-	char *revision = "$Revision: 1.15 $";
+	char *revision = "$Revision: 1.20 $";
 	char *version, *p;
 
 	version = strchr(revision, ' ');
@@ -2220,7 +2233,7 @@
 #endif
 
 #ifdef CONFIG_PCI
-	if (pcibios_present())
+	if (pci_present())
 		return 0;
 #endif
 
@@ -2514,6 +2527,7 @@
 static void
 zs_console_putchar(struct sun_serial *info, char ch)
 {
+	int loops = ZS_PUT_CHAR_MAX_DELAY;
 	unsigned long flags;
 
 	if(!info->zs_channel)
@@ -2521,9 +2535,12 @@
 
 	save_flags(flags); cli();
 	zs_put_char(info->zs_channel, ch);
+	while (!(read_zsreg(info->zs_channel, R1) & ALL_SNT) && --loops)
+		udelay(5);
 	restore_flags(flags);
 }
 
+#ifdef SERIAL_CONSOLE_FAIR_OUTPUT
 /*
  * Fair output driver allows a process to speak.
  */
@@ -2557,6 +2574,7 @@
 	restore_flags(flags);
 	return;
 }
+#endif
 
 /*
  * zs_console_write is registered for printk.
@@ -2574,9 +2592,10 @@
 			zs_console_putchar(info, '\r');
 		zs_console_putchar(info, *s);
 	}
-
+#ifdef SERIAL_CONSOLE_FAIR_OUTPUT
 	/* Comment this if you want to have a strict interrupt-driven output */
 	zs_fair_output(info);
+#endif
 }
 
 static int
diff -u --recursive --new-file v2.1.96/linux/drivers/sbus/dvma.c linux/drivers/sbus/dvma.c
--- v2.1.96/linux/drivers/sbus/dvma.c	Mon Apr 14 16:28:15 1997
+++ linux/drivers/sbus/dvma.c	Tue Apr 14 17:44:22 1998
@@ -16,7 +16,7 @@
 struct Linux_SBus_DMA *dma_chain;
 
 /* Print out the current values in the DMA control registers */
-static __inline__ void
+extern __inline__ void
 dump_dma_regs(struct sparc_dma_registers *dregs)
 {
 	printk("DMA CONTROL<%08lx>  ADDR<%08lx> CNT<%08lx> TEST<%08lx>\n",
@@ -27,6 +27,50 @@
 	return;
 }
 
+__initfunc(void
+init_one_dvma(struct Linux_SBus_DMA *dma, int num_dma))
+{
+	printk("dma%d: ", num_dma);
+	
+	dma->next = 0;
+	dma->running=0;      /* No transfers going on as of yet */
+	dma->allocated=0;    /* No one has allocated us yet */
+	switch((dma->regs->cond_reg)&DMA_DEVICE_ID) {
+	case DMA_VERS0:
+		dma->revision=dvmarev0;
+		printk("Revision 0 ");
+		break;
+	case DMA_ESCV1:
+		dma->revision=dvmaesc1;
+		printk("ESC Revision 1 ");
+		break;
+	case DMA_VERS1:
+		dma->revision=dvmarev1;
+		printk("Revision 1 ");
+		break;
+	case DMA_VERS2:
+		dma->revision=dvmarev2;
+		printk("Revision 2 ");
+		break;
+	case DMA_VERHME:
+		dma->revision=dvmahme;
+		printk("HME DVMA gate array ");
+		break;
+	case DMA_VERSPLUS:
+		dma->revision=dvmarevplus;
+		printk("Revision 1 PLUS ");
+		break;
+	default:
+		printk("unknown dma version %x",
+		       (dma->regs->cond_reg)&DMA_DEVICE_ID);
+		dma->allocated = 1;
+		break;
+	}
+	printk("\n");
+#if 0 /* Clutters up the screen */
+	dump_dma_regs(dma->regs);
+#endif
+}
 
 /* Probe this SBus DMA module(s) */
 __initfunc(unsigned long
@@ -40,11 +84,11 @@
 	for_each_sbusdev(this_dev, sbus) {
 		int hme = 0;
 
-		if(!strcmp(this_dev->prom_name, "SUNW,fas")) {
+		if(!strcmp(this_dev->prom_name, "SUNW,fas"))
 			hme = 1;
-		} else if(strcmp(this_dev->prom_name, "dma") &&
-			  strcmp(this_dev->prom_name, "ledma") &&
-			  strcmp(this_dev->prom_name, "espdma"))
+		else if(strcmp(this_dev->prom_name, "dma") &&
+			strcmp(this_dev->prom_name, "ledma") &&
+			strcmp(this_dev->prom_name, "espdma"))
 			continue;
 
 		/* Found one... */
@@ -62,10 +106,6 @@
 			/* We're the first in line */
 			dma_chain=dma;
 		}
-		dma->next = 0;
-
-		printk("dma%d: ", num_dma);
-		num_dma++;
 
 		/* The constant PAGE_SIZE that is passed to sparc_alloc_io makes the
 		 * routine only alloc 1 page, that was what the original code did
@@ -82,45 +122,42 @@
 					dma->SBus_dev->reg_addrs[0].which_io, 0x0);
 
 		dma->node = dma->SBus_dev->prom_node;
-		dma->running=0;      /* No transfers going on as of yet */
-		dma->allocated=0;    /* No one has allocated us yet */
-		switch((dma->regs->cond_reg)&DMA_DEVICE_ID) {
-		case DMA_VERS0:
-			dma->revision=dvmarev0;
-			printk("Revision 0 ");
-			break;
-		case DMA_ESCV1:
-			dma->revision=dvmaesc1;
-			printk("ESC Revision 1 ");
-			break;
-		case DMA_VERS1:
-			dma->revision=dvmarev1;
-			printk("Revision 1 ");
-			break;
-		case DMA_VERS2:
-			dma->revision=dvmarev2;
-			printk("Revision 2 ");
-			break;
-		case DMA_VERHME:
-			dma->revision=dvmahme;
-			printk("HME DVMA gate array ");
-			break;
-		case DMA_VERSPLUS:
-			dma->revision=dvmarevplus;
-			printk("Revision 1 PLUS ");
-			break;
-		default:
-			printk("unknown dma version %x",
-			       (dma->regs->cond_reg)&DMA_DEVICE_ID);
-			dma->allocated = 1;
-			break;
-		}
-		printk("\n");
-#if 0 /* Clutters up the screen */
-		dump_dma_regs(dma->regs);
-#endif
+		
+		init_one_dvma(dma, num_dma++);
+		
 	};  /* while(this_dev) */
 
 	return memory_start;
 }
 
+#ifdef CONFIG_SUN4
+
+#include <asm/sun4paddr.h>
+
+__initfunc(unsigned long
+sun4_dvma_init(unsigned long memory_start))
+{
+	struct Linux_SBus_DMA *dma;
+	struct Linux_SBus_DMA *dchain;
+
+	dma = (struct Linux_SBus_DMA *) memory_start;
+	memory_start += sizeof(struct Linux_SBus_DMA);
+
+	/* No SBUS */
+	dma->SBus_dev = 0x0;
+
+	/* Only one DMA device */
+	dma_chain=dma;
+
+	dma->regs = (struct sparc_dma_registers *)
+		sparc_alloc_io (SUN4_300_DMA_PHYSADDR, 0,
+				PAGE_SIZE, "dma", 0x0, 0x0);
+
+	/* No prom node */
+	dma->node = 0x0;
+
+	init_one_dvma(dma, 0);
+	return memory_start;
+}
+
+#endif
diff -u --recursive --new-file v2.1.96/linux/drivers/sbus/sbus.c linux/drivers/sbus/sbus.c
--- v2.1.96/linux/drivers/sbus/sbus.c	Mon Jan 12 15:15:45 1998
+++ linux/drivers/sbus/sbus.c	Tue Apr 14 17:44:22 1998
@@ -186,12 +186,10 @@
 				unsigned long memend, struct linux_sbus *sbus);
 extern unsigned long iounit_init(int sbi_node, int iounit_node, unsigned long memstart,
 				unsigned long memend, struct linux_sbus *sbus);
+unsigned long sun4_init(unsigned long memory_start, unsigned long memory_end);
 #ifdef CONFIG_SUN_OPENPROMIO
 extern int openprom_init(void);
 #endif
-#ifdef CONFIG_SUN_MOSTEK_RTC
-extern int rtc_init(void);
-#endif
 #ifdef CONFIG_SUN_AUXIO
 extern void auxio_probe(void);
 #endif
@@ -234,8 +232,6 @@
 	return memory_start;
 }
 
-/* #define E3000_DEBUG */
-
 __initfunc(unsigned long
 sbus_init(unsigned long memory_start, unsigned long memory_end))
 {
@@ -244,10 +240,11 @@
 	struct linux_sbus *sbus;
 	struct linux_sbus_device *this_dev;
 	int num_sbus = 0;  /* How many did we find? */
-
-#ifdef E3000_DEBUG
-	prom_printf("sbus_init: Radek, record following output for me. -DaveM\n");
+	
+#ifdef CONFIG_SUN4
+	return sun4_init(memory_start, memory_end);
 #endif
+
 	memory_start = ((memory_start + 7) & (~7));
 
 	topnd = prom_getchild(prom_root_node);
@@ -280,9 +277,6 @@
 		}
 	}
 
-#ifdef E3000_DEBUG
-	prom_printf("sbus_init: 1st sbus node(%x)\n", nd);
-#endif
 	/* Ok, we've found the first one, allocate first SBus struct
 	 * and place in chain.
 	 */
@@ -299,9 +293,6 @@
 	/* Loop until we find no more SBUS's */
 	while(this_sbus) {
 		/* IOMMU hides inside SBUS/SYSIO prom node on Ultra. */
-#ifdef E3000_DEBUG
-		prom_printf("sbus%d: [ii()", num_sbus);
-#endif
 		if(sparc_cpu_model == sun4u)
 			memory_start = iommu_init(this_sbus,
 						  memory_start, memory_end,
@@ -312,9 +303,6 @@
 						   memory_start, memory_end,
 						   sbus);
 #endif						   
-#ifdef E3000_DEBUG
-		prom_printf("1");
-#endif
 		printk("sbus%d: ", num_sbus);
 		sbus_clock = prom_getint(this_sbus, "clock-frequency");
 		if(sbus_clock==-1) sbus_clock = (25*1000*1000);
@@ -334,15 +322,9 @@
 		}
 #endif
 		
-#ifdef E3000_DEBUG
-		prom_printf("psri()");
-#endif
 		prom_sbus_ranges_init (iommund, sbus);
 
 		sbus_devs = prom_getchild(this_sbus);
-#ifdef E3000_DEBUG
-		prom_printf("chld(%x)", sbus_devs);
-#endif
 
 		sbus->devices = (struct linux_sbus_device *) memory_start;
 		memory_start += sizeof(struct linux_sbus_device);
@@ -350,9 +332,6 @@
 		this_dev = sbus->devices;
 		this_dev->next = 0;
 
-#ifdef E3000_DEBUG
-		prom_printf("fsd()");
-#endif
 		fill_sbus_device(sbus_devs, this_dev);
 		this_dev->my_bus = sbus;
 
@@ -362,14 +341,9 @@
 			this_dev->child = (struct linux_sbus_device *) memory_start;
 			memory_start += sizeof(struct linux_sbus_device);
 			/* Fill it */
-#ifdef E3000_DEBUG
-			prom_printf("fsd(chld)");
-#endif
+
 			fill_sbus_device(prom_getchild(sbus_devs), this_dev->child);
 			this_dev->child->my_bus = sbus;
-#ifdef E3000_DEBUG
-			prom_printf("sdcs()");
-#endif
 			memory_start = sbus_do_child_siblings(memory_start,
 							      prom_getchild(sbus_devs),
 							      this_dev->child,
@@ -378,9 +352,6 @@
 			this_dev->child = 0;
 		}
 
-#ifdef E3000_DEBUG
-		prom_printf("2");
-#endif
 		while((sbus_devs = prom_getsibling(sbus_devs)) != 0) {
 			/* Allocate device node */
 			this_dev->next = (struct linux_sbus_device *) memory_start;
@@ -389,9 +360,6 @@
 			this_dev->next=0;
 
 			/* Fill it */
-#ifdef E3000_DEBUG
-			prom_printf("fsd()");
-#endif
 			fill_sbus_device(sbus_devs, this_dev);
 			this_dev->my_bus = sbus;
 
@@ -403,15 +371,9 @@
 				memory_start += sizeof(struct linux_sbus_device);
 
 				/* Fill it */
-#ifdef E3000_DEBUG
-				prom_printf("fsd()");
-#endif
 				fill_sbus_device(prom_getchild(sbus_devs),
 						 this_dev->child);
 				this_dev->child->my_bus = sbus;
-#ifdef E3000_DEBUG
-				prom_printf("sdcs()");
-#endif
 				memory_start = sbus_do_child_siblings(
 						     memory_start,
 						     prom_getchild(sbus_devs),
@@ -422,26 +384,14 @@
 			}
 		}
 
-#ifdef E3000_DEBUG
-		prom_printf("di()");
-#endif
 		memory_start = dvma_init(sbus, memory_start);
 
 		num_sbus++;
-#ifdef E3000_DEBUG
-		prom_printf("3, off to next sbus\n");
-#endif
 		if(sparc_cpu_model == sun4u) {
 			this_sbus = prom_getsibling(this_sbus);
-#ifdef E3000_DEBUG
-			prom_printf("sbus_init: sibling(%x), ", this_sbus);
-#endif
 			if(!this_sbus)
 				break;
 			this_sbus = prom_searchsiblings(this_sbus, "sbus");
-#ifdef E3000_DEBUG
-			prom_printf("next sbus node(%x),", this_sbus);
-#endif
 		} else if(sparc_cpu_model == sun4d) {
 			iommund = prom_getsibling(iommund);
 			if(!iommund) break;
@@ -454,9 +404,6 @@
 			this_sbus = prom_searchsiblings(this_sbus, "sbus");
 		}
 		if(this_sbus) {
-#ifdef E3000_DEBUG
-			prom_printf(" scanning another sbus\n");
-#endif
 			sbus->next = (struct linux_sbus *) memory_start;
 			memory_start += sizeof(struct linux_sbus);
 			sbus = sbus->next;
@@ -471,19 +418,10 @@
 		memory_start = sun4d_init_sbi_irq(memory_start);
 	}
 	
-#ifdef E3000_DEBUG
-	prom_printf("sbus_init: No more sbus's, calling sun_console_init()\n");
-#endif
 	memory_start = sun_console_init(memory_start); /* whee... */
-#ifdef E3000_DEBUG
-	prom_printf("sbus_init: back from sun_console_init()\n");
-#endif
 #ifdef CONFIG_SUN_OPENPROMIO
 	openprom_init();
 #endif
-#ifdef CONFIG_SUN_MOSTEK_RTC
-	rtc_init();
-#endif
 #ifdef CONFIG_SUN_BPP
 	bpp_init();
 #endif
@@ -505,3 +443,20 @@
 #endif
 	return memory_start;
 }
+
+#ifdef CONFIG_SUN4
+
+extern unsigned long sun4_dvma_init(unsigned long);
+
+__initfunc(unsigned long
+sun4_init(unsigned long memory_start, unsigned long memory_end))
+{
+	memory_start = ((memory_start + 7) & (~7));
+	
+	memory_start = sun_console_init(memory_start);
+	
+	memory_start = sun4_dvma_init(memory_start);
+	
+	return memory_start;
+}
+#endif
diff -u --recursive --new-file v2.1.96/linux/drivers/scsi/53c7,8xx.c linux/drivers/scsi/53c7,8xx.c
--- v2.1.96/linux/drivers/scsi/53c7,8xx.c	Tue Apr 14 14:29:20 1998
+++ linux/drivers/scsi/53c7,8xx.c	Fri Apr 17 21:58:48 1998
@@ -5143,8 +5143,8 @@
 		pci_status &= ~PCI_STATUS_PARITY;
 	    }
 	} else {
-	    printk ("scsi%d : couldn't read status register : %s\n",
-		host->host_no, pcibios_strerror (tmp));
+	    printk ("scsi%d : couldn't read status register : error %d\n",
+		host->host_no, tmp);
 	    retry = NEVER;
 	}
     }
diff -u --recursive --new-file v2.1.96/linux/drivers/scsi/BusLogic.c linux/drivers/scsi/BusLogic.c
--- v2.1.96/linux/drivers/scsi/BusLogic.c	Fri Apr 10 13:03:48 1998
+++ linux/drivers/scsi/BusLogic.c	Thu Apr 16 16:24:16 1998
@@ -2,7 +2,7 @@
 
   Linux Driver for BusLogic MultiMaster and FlashPoint SCSI Host Adapters
 
-  Copyright 1995 by Leonard N. Zubkoff <lnz@dandelion.com>
+  Copyright 1995-1998 by Leonard N. Zubkoff <lnz@dandelion.com>
 
   This program is free software; you may redistribute and/or modify it under
   the terms of the GNU General Public License Version 2 as published by the
@@ -26,8 +26,8 @@
 */
 
 
-#define BusLogic_DriverVersion		"2.0.12"
-#define BusLogic_DriverDate		"29 March 1998"
+#define BusLogic_DriverVersion		"2.1.13"
+#define BusLogic_DriverDate		"17 April 1998"
 
 
 #include <linux/version.h>
@@ -41,13 +41,9 @@
 #include <linux/sched.h>
 #include <linux/stat.h>
 #include <linux/pci.h>
-#include <linux/bios32.h>
-
-#include <linux/blk.h>
-
 #include <asm/dma.h>
 #include <asm/io.h>
-#include <asm/irq.h>
+#include <asm/spinlock.h>
 #include <asm/system.h>
 #include "scsi.h"
 #include "hosts.h"
@@ -115,15 +111,6 @@
 
 
 /*
-  BusLogic_RegisteredHostAdapters is an array of linked lists of all the
-  registered BusLogic Host Adapters, indexed by IRQ Channel.
-*/
-
-static BusLogic_HostAdapter_T
-  *BusLogic_RegisteredHostAdapters[NR_IRQS] =	{ NULL };
-
-
-/*
   BusLogic_ProbeInfoCount is the number of entries in BusLogic_ProbeInfoList.
 */
 
@@ -153,16 +140,6 @@
 
 
 /*
-  BusLogic_FirstCompletedCCB and BusLogic_LastCompletedCCB are pointers
-  to the first and last CCBs that are queued for completion processing.
-*/
-
-static BusLogic_CCB_T
-  *BusLogic_FirstCompletedCCB =			NULL,
-  *BusLogic_LastCompletedCCB =			NULL;
-
-
-/*
   BusLogic_ProcDirectoryEntry is the BusLogic /proc/scsi directory entry.
 */
 
@@ -181,7 +158,7 @@
   BusLogic_Announce("***** BusLogic SCSI Driver Version "
 		    BusLogic_DriverVersion " of "
 		    BusLogic_DriverDate " *****\n", HostAdapter);
-  BusLogic_Announce("Copyright 1995 by Leonard N. Zubkoff "
+  BusLogic_Announce("Copyright 1995-1998 by Leonard N. Zubkoff "
 		    "<lnz@dandelion.com>\n", HostAdapter);
 }
 
@@ -206,7 +183,7 @@
 
 static void BusLogic_RegisterHostAdapter(BusLogic_HostAdapter_T *HostAdapter)
 {
-  HostAdapter->NextAll = NULL;
+  HostAdapter->Next = NULL;
   if (BusLogic_FirstRegisteredHostAdapter == NULL)
     {
       BusLogic_FirstRegisteredHostAdapter = HostAdapter;
@@ -214,20 +191,9 @@
     }
   else
     {
-      BusLogic_LastRegisteredHostAdapter->NextAll = HostAdapter;
+      BusLogic_LastRegisteredHostAdapter->Next = HostAdapter;
       BusLogic_LastRegisteredHostAdapter = HostAdapter;
     }
-  HostAdapter->Next = NULL;
-  if (BusLogic_RegisteredHostAdapters[HostAdapter->IRQ_Channel] != NULL)
-    {
-      BusLogic_HostAdapter_T *LastHostAdapter =
-	BusLogic_RegisteredHostAdapters[HostAdapter->IRQ_Channel];
-      BusLogic_HostAdapter_T *NextHostAdapter;
-      while ((NextHostAdapter = LastHostAdapter->Next) != NULL)
-	LastHostAdapter = NextHostAdapter;
-      LastHostAdapter->Next = HostAdapter;
-    }
-  else BusLogic_RegisteredHostAdapters[HostAdapter->IRQ_Channel] = HostAdapter;
 }
 
 
@@ -241,7 +207,7 @@
   if (HostAdapter == BusLogic_FirstRegisteredHostAdapter)
     {
       BusLogic_FirstRegisteredHostAdapter =
-	BusLogic_FirstRegisteredHostAdapter->NextAll;
+	BusLogic_FirstRegisteredHostAdapter->Next;
       if (HostAdapter == BusLogic_LastRegisteredHostAdapter)
 	BusLogic_LastRegisteredHostAdapter = NULL;
     }
@@ -250,24 +216,11 @@
       BusLogic_HostAdapter_T *PreviousHostAdapter =
 	BusLogic_FirstRegisteredHostAdapter;
       while (PreviousHostAdapter != NULL &&
-	     PreviousHostAdapter->NextAll != HostAdapter)
-	PreviousHostAdapter = PreviousHostAdapter->NextAll;
-      if (PreviousHostAdapter != NULL)
-	PreviousHostAdapter->NextAll = HostAdapter->NextAll;
-    }
-  HostAdapter->NextAll = NULL;
-  if (BusLogic_RegisteredHostAdapters[HostAdapter->IRQ_Channel] != HostAdapter)
-    {
-      BusLogic_HostAdapter_T *PreviousHostAdapter =
-	BusLogic_RegisteredHostAdapters[HostAdapter->IRQ_Channel];
-      while (PreviousHostAdapter != NULL &&
 	     PreviousHostAdapter->Next != HostAdapter)
 	PreviousHostAdapter = PreviousHostAdapter->Next;
       if (PreviousHostAdapter != NULL)
 	PreviousHostAdapter->Next = HostAdapter->Next;
     }
-  else BusLogic_RegisteredHostAdapters[HostAdapter->IRQ_Channel] =
-	 HostAdapter->Next;
   HostAdapter->Next = NULL;
 }
 
@@ -469,7 +422,7 @@
   unsigned char *ReplyPointer = (unsigned char *) ReplyData;
   BusLogic_StatusRegister_T StatusRegister;
   BusLogic_InterruptRegister_T InterruptRegister;
-  unsigned long ProcessorFlags = 0;
+  ProcessorFlags_T ProcessorFlags = 0;
   int ReplyBytes = 0, Result;
   long TimeoutCounter;
   /*
@@ -797,12 +750,7 @@
   boolean ForceBusDeviceScanningOrder = false;
   boolean ForceBusDeviceScanningOrderChecked = false;
   boolean StandardAddressSeen[6];
-  unsigned char Bus, DeviceFunction;
-  unsigned int BaseAddress0, BaseAddress1;
-  unsigned char IRQ_Channel;
-  BusLogic_IO_Address_T IO_Address;
-  BusLogic_PCI_Address_T PCI_Address;
-  unsigned short Index = 0;
+  PCI_Device_T *PCI_Device = NULL;
   int i;
   if (BusLogic_ProbeInfoCount >= BusLogic_MaxHostAdapters) return 0;
   BusLogic_ProbeInfoCount++;
@@ -821,147 +769,148 @@
     particular standard ISA I/O Address need not be probed.
   */
   PrimaryProbeInfo->IO_Address = 0;
-  while (pcibios_find_device(PCI_VENDOR_ID_BUSLOGIC,
-			     PCI_DEVICE_ID_BUSLOGIC_MULTIMASTER,
-			     Index++, &Bus, &DeviceFunction) == 0)
-    if (pcibios_read_config_dword(Bus, DeviceFunction,
-				  PCI_BASE_ADDRESS_0, &BaseAddress0) == 0 &&
-	pcibios_read_config_dword(Bus, DeviceFunction,
-				  PCI_BASE_ADDRESS_1, &BaseAddress1) == 0 &&
-	pcibios_read_config_byte(Bus, DeviceFunction,
-				 PCI_INTERRUPT_LINE, &IRQ_Channel) == 0)
-      {
-	BusLogic_HostAdapter_T *HostAdapter = PrototypeHostAdapter;
-	BusLogic_PCIHostAdapterInformation_T PCIHostAdapterInformation;
-	BusLogic_ModifyIOAddressRequest_T ModifyIOAddressRequest;
-	unsigned char Device = DeviceFunction >> 3;
-	IO_Address = BaseAddress0 & PCI_BASE_ADDRESS_IO_MASK;
-	PCI_Address = BaseAddress1 & PCI_BASE_ADDRESS_MEM_MASK;
-	if ((BaseAddress0 & PCI_BASE_ADDRESS_SPACE)
-	    != PCI_BASE_ADDRESS_SPACE_IO)
-	  {
-	    BusLogic_Error("BusLogic: Base Address0 0x%X not I/O for "
-			   "MultiMaster Host Adapter\n", NULL, BaseAddress0);
-	    BusLogic_Error("at PCI Bus %d Device %d I/O Address 0x%X\n",
-			   NULL, Bus, Device, IO_Address);
-	    continue;
-	  }
-	if ((BaseAddress1 & PCI_BASE_ADDRESS_SPACE)
-	    != PCI_BASE_ADDRESS_SPACE_MEMORY)
-	  {
-	    BusLogic_Error("BusLogic: Base Address1 0x%X not Memory for "
-			   "MultiMaster Host Adapter\n", NULL, BaseAddress1);
-	    BusLogic_Error("at PCI Bus %d Device %d PCI Address 0x%X\n",
-			   NULL, Bus, Device, PCI_Address);
-	    continue;
-	  }
-	if (IRQ_Channel == 0 || IRQ_Channel >= NR_IRQS)
-	  {
-	    BusLogic_Error("BusLogic: IRQ Channel %d illegal for "
-			   "MultiMaster Host Adapter\n", NULL, IRQ_Channel);
-	    BusLogic_Error("at PCI Bus %d Device %d I/O Address 0x%X\n",
-			   NULL, Bus, Device, IO_Address);
-	    continue;
-	  }
-	if (BusLogic_GlobalOptions.TraceProbe)
-	  {
-	    BusLogic_Notice("BusLogic: PCI MultiMaster Host Adapter "
-			    "detected at\n", NULL);
-	    BusLogic_Notice("BusLogic: PCI Bus %d Device %d I/O Address "
-			    "0x%X PCI Address 0x%X\n", NULL,
-			    Bus, Device, IO_Address, PCI_Address);
-	  }
-	/*
-	  Issue the Inquire PCI Host Adapter Information command to determine
-	  the ISA Compatible I/O Port.  If the ISA Compatible I/O Port is
-	  known and enabled, note that the particular Standard ISA I/O
-	  Address should not be probed.
-	*/
-	HostAdapter->IO_Address = IO_Address;
-	if (BusLogic_Command(HostAdapter,
-			     BusLogic_InquirePCIHostAdapterInformation,
-			     NULL, 0, &PCIHostAdapterInformation,
-			     sizeof(PCIHostAdapterInformation))
-	    == sizeof(PCIHostAdapterInformation))
-	  {
-	    if (PCIHostAdapterInformation.ISACompatibleIOPort < 6)
-	      StandardAddressSeen[PCIHostAdapterInformation
-				  .ISACompatibleIOPort] = true;
-	  }
-	else PCIHostAdapterInformation.ISACompatibleIOPort =
-	       BusLogic_IO_Disable;
-	/*
-	  Issue the Modify I/O Address command to disable the ISA Compatible
-	  I/O Port.
-	*/
-	ModifyIOAddressRequest = BusLogic_IO_Disable;
-	BusLogic_Command(HostAdapter, BusLogic_ModifyIOAddress,
-			 &ModifyIOAddressRequest,
-			 sizeof(ModifyIOAddressRequest), NULL, 0);
-	/*
-	  For the first MultiMaster Host Adapter enumerated, issue the Fetch
-	  Host Adapter Local RAM command to read byte 45 of the AutoSCSI area,
-	  for the setting of the "Use Bus And Device # For PCI Scanning Seq."
-	  option.  Issue the Inquire Board ID command since this option is
-	  only valid for the BT-948/958/958D.
-	*/
-	if (!ForceBusDeviceScanningOrderChecked)
-	  {
-	    BusLogic_FetchHostAdapterLocalRAMRequest_T
-	      FetchHostAdapterLocalRAMRequest;
-	    BusLogic_AutoSCSIByte45_T AutoSCSIByte45;
-	    BusLogic_BoardID_T BoardID;
-	    FetchHostAdapterLocalRAMRequest.ByteOffset =
-	      BusLogic_AutoSCSI_BaseOffset + 45;
-	    FetchHostAdapterLocalRAMRequest.ByteCount =
-	      sizeof(AutoSCSIByte45);
-	    BusLogic_Command(HostAdapter,
-			     BusLogic_FetchHostAdapterLocalRAM,
-			     &FetchHostAdapterLocalRAMRequest,
-			     sizeof(FetchHostAdapterLocalRAMRequest),
-			     &AutoSCSIByte45, sizeof(AutoSCSIByte45));
-	    BusLogic_Command(HostAdapter, BusLogic_InquireBoardID,
-			     NULL, 0, &BoardID, sizeof(BoardID));
-	    if (BoardID.FirmwareVersion1stDigit == '5')
-	      ForceBusDeviceScanningOrder =
-		AutoSCSIByte45.ForceBusDeviceScanningOrder;
-	    ForceBusDeviceScanningOrderChecked = true;
-	  }
-	/*
-	  Determine whether this MultiMaster Host Adapter has its ISA
-	  Compatible I/O Port enabled and is assigned the Primary I/O Address.
-	  If it does, then it is the Primary MultiMaster Host Adapter and must
-	  be recognized first.  If it does not, then it is added to the list
-	  for probing after any Primary MultiMaster Host Adapter is probed.
-	*/
-	if (PCIHostAdapterInformation.ISACompatibleIOPort == BusLogic_IO_330)
-	  {
-	    PrimaryProbeInfo->HostAdapterType = BusLogic_MultiMaster;
-	    PrimaryProbeInfo->HostAdapterBusType = BusLogic_PCI_Bus;
-	    PrimaryProbeInfo->IO_Address = IO_Address;
-	    PrimaryProbeInfo->PCI_Address = PCI_Address;
-	    PrimaryProbeInfo->Bus = Bus;
-	    PrimaryProbeInfo->Device = Device;
-	    PrimaryProbeInfo->IRQ_Channel = IRQ_Channel;
-	    PCIMultiMasterCount++;
-	  }
-	else if (BusLogic_ProbeInfoCount < BusLogic_MaxHostAdapters)
-	  {
-	    BusLogic_ProbeInfo_T *ProbeInfo =
-	      &BusLogic_ProbeInfoList[BusLogic_ProbeInfoCount++];
-	    ProbeInfo->HostAdapterType = BusLogic_MultiMaster;
-	    ProbeInfo->HostAdapterBusType = BusLogic_PCI_Bus;
-	    ProbeInfo->IO_Address = IO_Address;
-	    ProbeInfo->PCI_Address = PCI_Address;
-	    ProbeInfo->Bus = Bus;
-	    ProbeInfo->Device = Device;
-	    ProbeInfo->IRQ_Channel = IRQ_Channel;
-	    NonPrimaryPCIMultiMasterCount++;
-	    PCIMultiMasterCount++;
-	  }
-	else BusLogic_Warning("BusLogic: Too many Host Adapters "
-			      "detected\n", NULL);
-      }
+  while ((PCI_Device = pci_find_device(PCI_VENDOR_ID_BUSLOGIC,
+				       PCI_DEVICE_ID_BUSLOGIC_MULTIMASTER,
+				       PCI_Device)) != NULL)
+    {
+      BusLogic_HostAdapter_T *HostAdapter = PrototypeHostAdapter;
+      BusLogic_PCIHostAdapterInformation_T PCIHostAdapterInformation;
+      BusLogic_ModifyIOAddressRequest_T ModifyIOAddressRequest;
+      unsigned char Bus = PCI_Device->bus->number;
+      unsigned char Device = PCI_Device->devfn >> 3;
+      unsigned int IRQ_Channel = PCI_Device->irq;
+      unsigned long BaseAddress0 = PCI_Device->base_address[0];
+      unsigned long BaseAddress1 = PCI_Device->base_address[1];
+      BusLogic_IO_Address_T IO_Address =
+	BaseAddress0 & PCI_BASE_ADDRESS_IO_MASK;
+      BusLogic_PCI_Address_T PCI_Address =
+	BaseAddress1 & PCI_BASE_ADDRESS_MEM_MASK;
+      if ((BaseAddress0 & PCI_BASE_ADDRESS_SPACE)
+	  != PCI_BASE_ADDRESS_SPACE_IO)
+	{
+	  BusLogic_Error("BusLogic: Base Address0 0x%X not I/O for "
+			 "MultiMaster Host Adapter\n", NULL, BaseAddress0);
+	  BusLogic_Error("at PCI Bus %d Device %d I/O Address 0x%X\n",
+			 NULL, Bus, Device, IO_Address);
+	  continue;
+	}
+      if ((BaseAddress1 & PCI_BASE_ADDRESS_SPACE)
+	  != PCI_BASE_ADDRESS_SPACE_MEMORY)
+	{
+	  BusLogic_Error("BusLogic: Base Address1 0x%X not Memory for "
+			 "MultiMaster Host Adapter\n", NULL, BaseAddress1);
+	  BusLogic_Error("at PCI Bus %d Device %d PCI Address 0x%X\n",
+			 NULL, Bus, Device, PCI_Address);
+	  continue;
+	}
+      if (IRQ_Channel == 0)
+	{
+	  BusLogic_Error("BusLogic: IRQ Channel %d illegal for "
+			 "MultiMaster Host Adapter\n", NULL, IRQ_Channel);
+	  BusLogic_Error("at PCI Bus %d Device %d I/O Address 0x%X\n",
+			 NULL, Bus, Device, IO_Address);
+	  continue;
+	}
+      if (BusLogic_GlobalOptions.TraceProbe)
+	{
+	  BusLogic_Notice("BusLogic: PCI MultiMaster Host Adapter "
+			  "detected at\n", NULL);
+	  BusLogic_Notice("BusLogic: PCI Bus %d Device %d I/O Address "
+			  "0x%X PCI Address 0x%X\n", NULL,
+			  Bus, Device, IO_Address, PCI_Address);
+	}
+      /*
+	Issue the Inquire PCI Host Adapter Information command to determine
+	the ISA Compatible I/O Port.  If the ISA Compatible I/O Port is
+	known and enabled, note that the particular Standard ISA I/O
+	Address should not be probed.
+      */
+      HostAdapter->IO_Address = IO_Address;
+      BusLogic_InterruptReset(HostAdapter);
+      if (BusLogic_Command(HostAdapter,
+			   BusLogic_InquirePCIHostAdapterInformation,
+			   NULL, 0, &PCIHostAdapterInformation,
+			   sizeof(PCIHostAdapterInformation))
+	  == sizeof(PCIHostAdapterInformation))
+	{
+	  if (PCIHostAdapterInformation.ISACompatibleIOPort < 6)
+	    StandardAddressSeen[PCIHostAdapterInformation
+				.ISACompatibleIOPort] = true;
+	}
+      else PCIHostAdapterInformation.ISACompatibleIOPort =
+	     BusLogic_IO_Disable;
+      /*
+	Issue the Modify I/O Address command to disable the ISA Compatible
+	I/O Port.
+      */
+      ModifyIOAddressRequest = BusLogic_IO_Disable;
+      BusLogic_Command(HostAdapter, BusLogic_ModifyIOAddress,
+		       &ModifyIOAddressRequest,
+		       sizeof(ModifyIOAddressRequest), NULL, 0);
+      /*
+	For the first MultiMaster Host Adapter enumerated, issue the Fetch
+	Host Adapter Local RAM command to read byte 45 of the AutoSCSI area,
+	for the setting of the "Use Bus And Device # For PCI Scanning Seq."
+	option.  Issue the Inquire Board ID command since this option is
+	only valid for the BT-948/958/958D.
+      */
+      if (!ForceBusDeviceScanningOrderChecked)
+	{
+	  BusLogic_FetchHostAdapterLocalRAMRequest_T
+	    FetchHostAdapterLocalRAMRequest;
+	  BusLogic_AutoSCSIByte45_T AutoSCSIByte45;
+	  BusLogic_BoardID_T BoardID;
+	  FetchHostAdapterLocalRAMRequest.ByteOffset =
+	    BusLogic_AutoSCSI_BaseOffset + 45;
+	  FetchHostAdapterLocalRAMRequest.ByteCount =
+	    sizeof(AutoSCSIByte45);
+	  BusLogic_Command(HostAdapter,
+			   BusLogic_FetchHostAdapterLocalRAM,
+			   &FetchHostAdapterLocalRAMRequest,
+			   sizeof(FetchHostAdapterLocalRAMRequest),
+			   &AutoSCSIByte45, sizeof(AutoSCSIByte45));
+	  BusLogic_Command(HostAdapter, BusLogic_InquireBoardID,
+			   NULL, 0, &BoardID, sizeof(BoardID));
+	  if (BoardID.FirmwareVersion1stDigit == '5')
+	    ForceBusDeviceScanningOrder =
+	      AutoSCSIByte45.ForceBusDeviceScanningOrder;
+	  ForceBusDeviceScanningOrderChecked = true;
+	}
+      /*
+	Determine whether this MultiMaster Host Adapter has its ISA
+	Compatible I/O Port enabled and is assigned the Primary I/O Address.
+	If it does, then it is the Primary MultiMaster Host Adapter and must
+	be recognized first.  If it does not, then it is added to the list
+	for probing after any Primary MultiMaster Host Adapter is probed.
+      */
+      if (PCIHostAdapterInformation.ISACompatibleIOPort == BusLogic_IO_330)
+	{
+	  PrimaryProbeInfo->HostAdapterType = BusLogic_MultiMaster;
+	  PrimaryProbeInfo->HostAdapterBusType = BusLogic_PCI_Bus;
+	  PrimaryProbeInfo->IO_Address = IO_Address;
+	  PrimaryProbeInfo->PCI_Address = PCI_Address;
+	  PrimaryProbeInfo->Bus = Bus;
+	  PrimaryProbeInfo->Device = Device;
+	  PrimaryProbeInfo->IRQ_Channel = IRQ_Channel;
+	  PCIMultiMasterCount++;
+	}
+      else if (BusLogic_ProbeInfoCount < BusLogic_MaxHostAdapters)
+	{
+	  BusLogic_ProbeInfo_T *ProbeInfo =
+	    &BusLogic_ProbeInfoList[BusLogic_ProbeInfoCount++];
+	  ProbeInfo->HostAdapterType = BusLogic_MultiMaster;
+	  ProbeInfo->HostAdapterBusType = BusLogic_PCI_Bus;
+	  ProbeInfo->IO_Address = IO_Address;
+	  ProbeInfo->PCI_Address = PCI_Address;
+	  ProbeInfo->Bus = Bus;
+	  ProbeInfo->Device = Device;
+	  ProbeInfo->IRQ_Channel = IRQ_Channel;
+	  NonPrimaryPCIMultiMasterCount++;
+	  PCIMultiMasterCount++;
+	}
+      else BusLogic_Warning("BusLogic: Too many Host Adapters "
+			    "detected\n", NULL);
+    }
   /*
     If the AutoSCSI "Use Bus And Device # For PCI Scanning Seq." option is ON
     for the first enumerated MultiMaster Host Adapter, and if that host adapter
@@ -1027,34 +976,32 @@
     Iterate over the older non-compliant MultiMaster PCI Host Adapters,
     noting the PCI bus location and assigned IRQ Channel.
   */
-  Index = 0;
-  while (pcibios_find_device(PCI_VENDOR_ID_BUSLOGIC,
-			     PCI_DEVICE_ID_BUSLOGIC_MULTIMASTER_NC,
-			     Index++, &Bus, &DeviceFunction) == 0)
-    if (pcibios_read_config_dword(Bus, DeviceFunction,
-				  PCI_BASE_ADDRESS_0, &BaseAddress0) == 0 &&
-	pcibios_read_config_byte(Bus, DeviceFunction,
-				 PCI_INTERRUPT_LINE, &IRQ_Channel) == 0)
-      {
-	unsigned char Device = DeviceFunction >> 3;
-	IO_Address = BaseAddress0 & PCI_BASE_ADDRESS_IO_MASK;
-	if (IO_Address == 0 || IRQ_Channel == 0 || IRQ_Channel >= NR_IRQS)
-	  continue;
-	for (i = 0; i < BusLogic_ProbeInfoCount; i++)
-	  {
-	    BusLogic_ProbeInfo_T *ProbeInfo = &BusLogic_ProbeInfoList[i];
-	    if (ProbeInfo->IO_Address == IO_Address &&
-		ProbeInfo->HostAdapterType == BusLogic_MultiMaster)
-	      {
-		ProbeInfo->HostAdapterBusType = BusLogic_PCI_Bus;
-		ProbeInfo->PCI_Address = 0;
-		ProbeInfo->Bus = Bus;
-		ProbeInfo->Device = Device;
-		ProbeInfo->IRQ_Channel = IRQ_Channel;
-		break;
-	      }
-	  }
-      }
+  PCI_Device = NULL;
+  while ((PCI_Device = pci_find_device(PCI_VENDOR_ID_BUSLOGIC,
+				       PCI_DEVICE_ID_BUSLOGIC_MULTIMASTER_NC,
+				       PCI_Device)) != NULL)
+    {
+      unsigned char Bus = PCI_Device->bus->number;
+      unsigned char Device = PCI_Device->devfn >> 3;
+      unsigned int IRQ_Channel = PCI_Device->irq;
+      BusLogic_IO_Address_T IO_Address =
+	PCI_Device->base_address[0] & PCI_BASE_ADDRESS_IO_MASK;
+      if (IO_Address == 0 || IRQ_Channel == 0) continue;
+      for (i = 0; i < BusLogic_ProbeInfoCount; i++)
+	{
+	  BusLogic_ProbeInfo_T *ProbeInfo = &BusLogic_ProbeInfoList[i];
+	  if (ProbeInfo->IO_Address == IO_Address &&
+	      ProbeInfo->HostAdapterType == BusLogic_MultiMaster)
+	    {
+	      ProbeInfo->HostAdapterBusType = BusLogic_PCI_Bus;
+	      ProbeInfo->PCI_Address = 0;
+	      ProbeInfo->Bus = Bus;
+	      ProbeInfo->Device = Device;
+	      ProbeInfo->IRQ_Channel = IRQ_Channel;
+	      break;
+	    }
+	}
+    }
   return PCIMultiMasterCount;
 }
 
@@ -1070,87 +1017,82 @@
 						  *PrototypeHostAdapter)
 {
   int FlashPointIndex = BusLogic_ProbeInfoCount, FlashPointCount = 0;
-  unsigned char Bus, DeviceFunction;
-  unsigned int BaseAddress0, BaseAddress1;
-  unsigned char IRQ_Channel;
-  BusLogic_IO_Address_T IO_Address;
-  BusLogic_PCI_Address_T PCI_Address;
-  unsigned short Index = 0;
+  PCI_Device_T *PCI_Device = NULL;
   /*
     Interrogate PCI Configuration Space for any FlashPoint Host Adapters.
   */
-  while (pcibios_find_device(PCI_VENDOR_ID_BUSLOGIC,
-			     PCI_DEVICE_ID_BUSLOGIC_FLASHPOINT,
-			     Index++, &Bus, &DeviceFunction) == 0)
-    if (pcibios_read_config_dword(Bus, DeviceFunction,
-				  PCI_BASE_ADDRESS_0, &BaseAddress0) == 0 &&
-	pcibios_read_config_dword(Bus, DeviceFunction,
-				  PCI_BASE_ADDRESS_1, &BaseAddress1) == 0 &&
-	pcibios_read_config_byte(Bus, DeviceFunction,
-				 PCI_INTERRUPT_LINE, &IRQ_Channel) == 0)
-      {
-	unsigned char Device = DeviceFunction >> 3;
-	IO_Address = BaseAddress0 & PCI_BASE_ADDRESS_IO_MASK;
-	PCI_Address = BaseAddress1 & PCI_BASE_ADDRESS_MEM_MASK;
+  while ((PCI_Device = pci_find_device(PCI_VENDOR_ID_BUSLOGIC,
+				       PCI_DEVICE_ID_BUSLOGIC_FLASHPOINT,
+				       PCI_Device)) != NULL)
+    {
+      unsigned char Bus = PCI_Device->bus->number;
+      unsigned char Device = PCI_Device->devfn >> 3;
+      unsigned int IRQ_Channel = PCI_Device->irq;
+      unsigned long BaseAddress0 = PCI_Device->base_address[0];
+      unsigned long BaseAddress1 = PCI_Device->base_address[1];
+      BusLogic_IO_Address_T IO_Address =
+	BaseAddress0 & PCI_BASE_ADDRESS_IO_MASK;
+      BusLogic_PCI_Address_T PCI_Address =
+	BaseAddress1 & PCI_BASE_ADDRESS_MEM_MASK;
 #ifndef CONFIG_SCSI_OMIT_FLASHPOINT
-	if ((BaseAddress0 & PCI_BASE_ADDRESS_SPACE)
-	    != PCI_BASE_ADDRESS_SPACE_IO)
-	  {
-	    BusLogic_Error("BusLogic: Base Address0 0x%X not I/O for "
-			   "FlashPoint Host Adapter\n", NULL, BaseAddress0);
-	    BusLogic_Error("at PCI Bus %d Device %d I/O Address 0x%X\n",
-			   NULL, Bus, Device, IO_Address);
-	    continue;
-	  }
-	if ((BaseAddress1 & PCI_BASE_ADDRESS_SPACE)
-	    != PCI_BASE_ADDRESS_SPACE_MEMORY)
-	  {
-	    BusLogic_Error("BusLogic: Base Address1 0x%X not Memory for "
-			   "FlashPoint Host Adapter\n", NULL, BaseAddress1);
-	    BusLogic_Error("at PCI Bus %d Device %d PCI Address 0x%X\n",
-			   NULL, Bus, Device, PCI_Address);
-	    continue;
-	  }
-	if (IRQ_Channel == 0 || IRQ_Channel >= NR_IRQS)
-	  {
-	    BusLogic_Error("BusLogic: IRQ Channel %d illegal for "
-			   "FlashPoint Host Adapter\n", NULL, IRQ_Channel);
-	    BusLogic_Error("at PCI Bus %d Device %d I/O Address 0x%X\n",
-			   NULL, Bus, Device, IO_Address);
-	    continue;
-	  }
-	if (BusLogic_GlobalOptions.TraceProbe)
-	  {
-	    BusLogic_Notice("BusLogic: FlashPoint Host Adapter "
-			    "detected at\n", NULL);
-	    BusLogic_Notice("BusLogic: PCI Bus %d Device %d I/O Address "
-			    "0x%X PCI Address 0x%X\n", NULL,
-			    Bus, Device, IO_Address, PCI_Address);
-	  }
-	if (BusLogic_ProbeInfoCount < BusLogic_MaxHostAdapters)
-	  {
-	    BusLogic_ProbeInfo_T *ProbeInfo =
-	      &BusLogic_ProbeInfoList[BusLogic_ProbeInfoCount++];
-	    ProbeInfo->HostAdapterType = BusLogic_FlashPoint;
-	    ProbeInfo->HostAdapterBusType = BusLogic_PCI_Bus;
-	    ProbeInfo->IO_Address = IO_Address;
-	    ProbeInfo->PCI_Address = PCI_Address;
-	    ProbeInfo->Bus = Bus;
-	    ProbeInfo->Device = Device;
-	    ProbeInfo->IRQ_Channel = IRQ_Channel;
-	    FlashPointCount++;
-	  }
-	else BusLogic_Warning("BusLogic: Too many Host Adapters "
-			      "detected\n", NULL);
+      if ((BaseAddress0 & PCI_BASE_ADDRESS_SPACE)
+	  != PCI_BASE_ADDRESS_SPACE_IO)
+	{
+	  BusLogic_Error("BusLogic: Base Address0 0x%X not I/O for "
+			 "FlashPoint Host Adapter\n", NULL, BaseAddress0);
+	  BusLogic_Error("at PCI Bus %d Device %d I/O Address 0x%X\n",
+			 NULL, Bus, Device, IO_Address);
+	  continue;
+	}
+      if ((BaseAddress1 & PCI_BASE_ADDRESS_SPACE)
+	  != PCI_BASE_ADDRESS_SPACE_MEMORY)
+	{
+	  BusLogic_Error("BusLogic: Base Address1 0x%X not Memory for "
+			 "FlashPoint Host Adapter\n", NULL, BaseAddress1);
+	  BusLogic_Error("at PCI Bus %d Device %d PCI Address 0x%X\n",
+			 NULL, Bus, Device, PCI_Address);
+	  continue;
+	}
+      if (IRQ_Channel == 0)
+	{
+	  BusLogic_Error("BusLogic: IRQ Channel %d illegal for "
+			 "FlashPoint Host Adapter\n", NULL, IRQ_Channel);
+	  BusLogic_Error("at PCI Bus %d Device %d I/O Address 0x%X\n",
+			 NULL, Bus, Device, IO_Address);
+	  continue;
+	}
+      if (BusLogic_GlobalOptions.TraceProbe)
+	{
+	  BusLogic_Notice("BusLogic: FlashPoint Host Adapter "
+			  "detected at\n", NULL);
+	  BusLogic_Notice("BusLogic: PCI Bus %d Device %d I/O Address "
+			  "0x%X PCI Address 0x%X\n", NULL,
+			  Bus, Device, IO_Address, PCI_Address);
+	}
+      if (BusLogic_ProbeInfoCount < BusLogic_MaxHostAdapters)
+	{
+	  BusLogic_ProbeInfo_T *ProbeInfo =
+	    &BusLogic_ProbeInfoList[BusLogic_ProbeInfoCount++];
+	  ProbeInfo->HostAdapterType = BusLogic_FlashPoint;
+	  ProbeInfo->HostAdapterBusType = BusLogic_PCI_Bus;
+	  ProbeInfo->IO_Address = IO_Address;
+	  ProbeInfo->PCI_Address = PCI_Address;
+	  ProbeInfo->Bus = Bus;
+	  ProbeInfo->Device = Device;
+	  ProbeInfo->IRQ_Channel = IRQ_Channel;
+	  FlashPointCount++;
+	}
+      else BusLogic_Warning("BusLogic: Too many Host Adapters "
+			    "detected\n", NULL);
 #else
-	BusLogic_Error("BusLogic: FlashPoint Host Adapter detected at "
-		       "PCI Bus %d Device %d\n", NULL, Bus, Device);
-	BusLogic_Error("BusLogic: I/O Address 0x%X PCI Address 0x%X, "
-		       "but FlashPoint\n", NULL, IO_Address, PCI_Address);
-	BusLogic_Error("BusLogic: support was omitted in this kernel "
-		       "configuration.\n", NULL);
+      BusLogic_Error("BusLogic: FlashPoint Host Adapter detected at "
+		     "PCI Bus %d Device %d\n", NULL, Bus, Device);
+      BusLogic_Error("BusLogic: I/O Address 0x%X PCI Address 0x%X, "
+		     "but FlashPoint\n", NULL, IO_Address, PCI_Address);
+      BusLogic_Error("BusLogic: support was omitted in this kernel "
+		     "configuration.\n", NULL);
 #endif
-      }
+    }
   /*
     The FlashPoint BIOS will scan for FlashPoint Host Adapters in the order of
     increasing PCI Bus and Device Number, so sort the probe information into
@@ -1482,12 +1424,11 @@
 
 /*
   BusLogic_CheckHostAdapter checks to be sure this really is a BusLogic
-  Host Adapter.  It also determines the IRQ Channel for non-PCI Host Adapters.
+  Host Adapter.
 */
 
 static boolean BusLogic_CheckHostAdapter(BusLogic_HostAdapter_T *HostAdapter)
 {
-  BusLogic_Configuration_T Configuration;
   BusLogic_ExtendedSetupInformation_T ExtendedSetupInformation;
   BusLogic_RequestedReplyLength_T RequestedReplyLength;
   boolean Result = true;
@@ -1496,31 +1437,6 @@
   */
   if (BusLogic_FlashPointHostAdapterP(HostAdapter)) return true;
   /*
-    Issue the Inquire Configuration command if the IRQ Channel is unknown.
-  */
-  if (HostAdapter->IRQ_Channel == 0)
-    {
-      if (BusLogic_Command(HostAdapter, BusLogic_InquireConfiguration,
-			   NULL, 0, &Configuration, sizeof(Configuration))
-	  == sizeof(Configuration))
-	{
-	  if (Configuration.IRQ_Channel9)
-	    HostAdapter->IRQ_Channel = 9;
-	  else if (Configuration.IRQ_Channel10)
-	    HostAdapter->IRQ_Channel = 10;
-	  else if (Configuration.IRQ_Channel11)
-	    HostAdapter->IRQ_Channel = 11;
-	  else if (Configuration.IRQ_Channel12)
-	    HostAdapter->IRQ_Channel = 12;
-	  else if (Configuration.IRQ_Channel14)
-	    HostAdapter->IRQ_Channel = 14;
-	  else if (Configuration.IRQ_Channel15)
-	    HostAdapter->IRQ_Channel = 15;
-	  else Result = false;
-	}
-      else Result = false;
-    }
-  /*
     Issue the Inquire Extended Setup Information command.  Only genuine
     BusLogic Host Adapters and true clones support this command.  Adaptec 1542C
     series Host Adapters that respond to the Geometry Register I/O port will
@@ -1746,11 +1662,27 @@
   */
   HostAdapter->SCSI_ID = Configuration.HostAdapterID;
   /*
-    Determine the Bus Type and save it in the Host Adapter structure,
-    and determine and save the DMA Channel for ISA Host Adapters.
+    Determine the Bus Type and save it in the Host Adapter structure, determine
+    and save the IRQ Channel if necessary, and determine and save the DMA
+    Channel for ISA Host Adapters.
   */
   HostAdapter->HostAdapterBusType =
     BusLogic_HostAdapterBusTypes[HostAdapter->ModelName[3] - '4'];
+  if (HostAdapter->IRQ_Channel == 0)
+    {
+      if (Configuration.IRQ_Channel9)
+	HostAdapter->IRQ_Channel = 9;
+      else if (Configuration.IRQ_Channel10)
+	HostAdapter->IRQ_Channel = 10;
+      else if (Configuration.IRQ_Channel11)
+	HostAdapter->IRQ_Channel = 11;
+      else if (Configuration.IRQ_Channel12)
+	HostAdapter->IRQ_Channel = 12;
+      else if (Configuration.IRQ_Channel14)
+	HostAdapter->IRQ_Channel = 14;
+      else if (Configuration.IRQ_Channel15)
+	HostAdapter->IRQ_Channel = 15;
+    }
   if (HostAdapter->HostAdapterBusType == BusLogic_ISA_Bus)
     {
       if (Configuration.DMA_Channel5)
@@ -1893,7 +1825,7 @@
   HostAdapter->MaxTargetDevices = (HostAdapter->HostWideSCSI ? 16 : 8);
   HostAdapter->MaxLogicalUnits = (HostAdapter->ExtendedLUNSupport ? 32 : 8);
   /*
-    Select appropriate values for the Mailbox Count, Driver Queue Depth, 
+    Select appropriate values for the Mailbox Count, Driver Queue Depth,
     Initial CCBs, and Incremental CCBs variables based on whether or not Strict
     Round Robin Mode is supported.  If Strict Round Robin Mode is supported,
     then there is no performance degradation in using the maximum possible
@@ -1985,12 +1917,10 @@
   */
 Common:
   /*
-    Initialize the Host Adapter Full Model Name and Interrupt Label fields
-    from the Model Name.
+    Initialize the Host Adapter Full Model Name from the Model Name.
   */
   strcpy(HostAdapter->FullModelName, "BusLogic ");
   strcat(HostAdapter->FullModelName, HostAdapter->ModelName);
-  strcpy(HostAdapter->InterruptLabel, HostAdapter->FullModelName);
   /*
     Select an appropriate value for the Tagged Queue Depth either from a
     BusLogic Driver Options specification, or based on whether this Host
@@ -2308,8 +2238,6 @@
 
 static boolean BusLogic_AcquireResources(BusLogic_HostAdapter_T *HostAdapter)
 {
-  BusLogic_HostAdapter_T *FirstHostAdapter =
-    BusLogic_RegisteredHostAdapters[HostAdapter->IRQ_Channel];
   if (HostAdapter->IRQ_Channel == 0)
     {
       BusLogic_Error("NO LEGAL INTERRUPT CHANNEL ASSIGNED - DETACHING\n",
@@ -2317,24 +2245,14 @@
       return false;
     }
   /*
-    Acquire exclusive or shared access to the IRQ Channel if necessary.
+    Acquire shared access to the IRQ Channel.
   */
-  if (FirstHostAdapter->Next == NULL)
+  if (request_irq(HostAdapter->IRQ_Channel, BusLogic_InterruptHandler,
+		  SA_SHIRQ, HostAdapter->FullModelName, HostAdapter) < 0)
     {
-      if (request_irq(HostAdapter->IRQ_Channel, BusLogic_InterruptHandler,
-		      SA_INTERRUPT | SA_SHIRQ,
-		      HostAdapter->InterruptLabel, NULL) < 0)
-	{
-	  BusLogic_Error("UNABLE TO ACQUIRE IRQ CHANNEL %d - DETACHING\n",
-			 HostAdapter, HostAdapter->IRQ_Channel);
-	  return false;
-	}
-    }
-  else if (strlen(FirstHostAdapter->InterruptLabel) + 11
-	   < sizeof(FirstHostAdapter->InterruptLabel))
-    {
-      strcat(FirstHostAdapter->InterruptLabel, " + ");
-      strcat(FirstHostAdapter->InterruptLabel, HostAdapter->ModelName);
+      BusLogic_Error("UNABLE TO ACQUIRE IRQ CHANNEL %d - DETACHING\n",
+		     HostAdapter, HostAdapter->IRQ_Channel);
+      return false;
     }
   HostAdapter->IRQ_ChannelAcquired = true;
   /*
@@ -2367,14 +2285,11 @@
 
 static void BusLogic_ReleaseResources(BusLogic_HostAdapter_T *HostAdapter)
 {
-  BusLogic_HostAdapter_T *FirstHostAdapter =
-    BusLogic_RegisteredHostAdapters[HostAdapter->IRQ_Channel];
   /*
-    Release exclusive or shared access to the IRQ Channel.
+    Release shared access to the IRQ Channel.
   */
   if (HostAdapter->IRQ_ChannelAcquired)
-    if (FirstHostAdapter->Next == NULL)
-      free_irq(HostAdapter->IRQ_Channel, NULL);
+    free_irq(HostAdapter->IRQ_Channel, HostAdapter);
   /*
     Release exclusive access to the DMA Channel.
   */
@@ -2397,6 +2312,12 @@
   BusLogic_SetCCBFormatRequest_T SetCCBFormatRequest;
   int TargetID;
   /*
+    Initialize the pointers to the first and last CCBs that are queued for
+    completion processing.
+  */
+  HostAdapter->FirstCompletedCCB = NULL;
+  HostAdapter->LastCompletedCCB = NULL;
+  /*
     Initialize the Bus Device Reset Pending CCB, Tagged Queuing Active,
     Command Successful Flag, Active Commands, and Commands Since Reset
     for each Target Device.
@@ -2776,7 +2697,7 @@
   if (HostAdapter == BusLogic_LastRegisteredHostAdapter)
     for (HostAdapter = BusLogic_FirstRegisteredHostAdapter;
 	 HostAdapter != NULL;
-	 HostAdapter = HostAdapter->NextAll)
+	 HostAdapter = HostAdapter->Next)
       BusLogic_ReportTargetDeviceInfo(HostAdapter);
 }
 
@@ -2878,9 +2799,7 @@
       Host->select_queue_depths = BusLogic_SelectQueueDepths;
       /*
 	Add Host Adapter to the end of the list of registered BusLogic
-	Host Adapters.  In order for Command Complete Interrupts to be
-	properly dismissed by BusLogic_InterruptHandler, the Host Adapter
-	must be registered.
+	Host Adapters.
       */
       BusLogic_RegisterHostAdapter(HostAdapter);
       /*
@@ -2973,19 +2892,20 @@
 
 static void BusLogic_QueueCompletedCCB(BusLogic_CCB_T *CCB)
 {
+  BusLogic_HostAdapter_T *HostAdapter = CCB->HostAdapter;
   CCB->Status = BusLogic_CCB_Completed;
   CCB->Next = NULL;
-  if (BusLogic_FirstCompletedCCB == NULL)
+  if (HostAdapter->FirstCompletedCCB == NULL)
     {
-      BusLogic_FirstCompletedCCB = CCB;
-      BusLogic_LastCompletedCCB = CCB;
+      HostAdapter->FirstCompletedCCB = CCB;
+      HostAdapter->LastCompletedCCB = CCB;
     }
   else
     {
-      BusLogic_LastCompletedCCB->Next = CCB;
-      BusLogic_LastCompletedCCB = CCB;
+      HostAdapter->LastCompletedCCB->Next = CCB;
+      HostAdapter->LastCompletedCCB = CCB;
     }
-  CCB->HostAdapter->ActiveCommands[CCB->TargetID]--;
+  HostAdapter->ActiveCommands[CCB->TargetID]--;
 }
 
 
@@ -3108,25 +3028,23 @@
 
 
 /*
-  BusLogic_ProcessCompletedCCBs iterates over the completed CCBs setting
-  the SCSI Command Result Codes, deallocating the CCBs, and calling the
-  SCSI Subsystem Completion Routines.  Interrupts should already have been
-  disabled by the caller.
+  BusLogic_ProcessCompletedCCBs iterates over the completed CCBs for Host
+  Adapter setting the SCSI Command Result Codes, deallocating the CCBs, and
+  calling the SCSI Subsystem Completion Routines.  The Host Adapter's Lock
+  should already have been acquired by the caller.
 */
 
-static void BusLogic_ProcessCompletedCCBs(void)
+static void BusLogic_ProcessCompletedCCBs(BusLogic_HostAdapter_T *HostAdapter)
 {
-  static boolean ProcessCompletedCCBsActive = false;
-  if (ProcessCompletedCCBsActive) return;
-  ProcessCompletedCCBsActive = true;
-  while (BusLogic_FirstCompletedCCB != NULL)
+  if (HostAdapter->ProcessCompletedCCBsActive) return;
+  HostAdapter->ProcessCompletedCCBsActive = true;
+  while (HostAdapter->FirstCompletedCCB != NULL)
     {
-      BusLogic_CCB_T *CCB = BusLogic_FirstCompletedCCB;
+      BusLogic_CCB_T *CCB = HostAdapter->FirstCompletedCCB;
       SCSI_Command_T *Command = CCB->Command;
-      BusLogic_HostAdapter_T *HostAdapter = CCB->HostAdapter;
-      BusLogic_FirstCompletedCCB = CCB->Next;
-      if (BusLogic_FirstCompletedCCB == NULL)
-	BusLogic_LastCompletedCCB = NULL;
+      HostAdapter->FirstCompletedCCB = CCB->Next;
+      if (HostAdapter->FirstCompletedCCB == NULL)
+	HostAdapter->LastCompletedCCB = NULL;
       /*
 	Process the Completed CCB.
       */
@@ -3262,7 +3180,7 @@
 	  Command->scsi_done(Command);
 	}
     }
-  ProcessCompletedCCBsActive = false;
+  HostAdapter->ProcessCompletedCCBsActive = false;
 }
 
 
@@ -3270,129 +3188,91 @@
   BusLogic_InterruptHandler handles hardware interrupts from BusLogic Host
   Adapters.
 */
-static void do_BusLogic_InterruptHandler(int IRQ_Channel,
+
+static void BusLogic_InterruptHandler(int IRQ_Channel,
 				      void *DeviceIdentifier,
 				      Registers_T *InterruptRegisters)
 {
-  BusLogic_HostAdapter_T *FirstHostAdapter =
-    BusLogic_RegisteredHostAdapters[IRQ_Channel];
-  boolean HostAdapterResetRequired = false;
-  BusLogic_HostAdapter_T *HostAdapter;
-  BusLogic_Lock_T Lock;
+  BusLogic_HostAdapter_T *HostAdapter =
+    (BusLogic_HostAdapter_T *) DeviceIdentifier;
+  ProcessorFlags_T ProcessorFlags;
   /*
-    Iterate over the installed BusLogic Host Adapters accepting any Incoming
-    Mailbox entries and saving the completed CCBs for processing.  This
-    interrupt handler is installed as a fast interrupt, so interrupts are
-    disabled when the interrupt handler is entered.
+    Acquire exclusive access to Host Adapter.
   */
-  for (HostAdapter = FirstHostAdapter;
-       HostAdapter != NULL;
-       HostAdapter = HostAdapter->Next)
+  BusLogic_AcquireHostAdapterLockIH(HostAdapter, &ProcessorFlags);
+  /*
+    Handle Interrupts appropriately for each Host Adapter type.
+  */
+  if (BusLogic_MultiMasterHostAdapterP(HostAdapter))
     {
+      BusLogic_InterruptRegister_T InterruptRegister;
       /*
-	Acquire exclusive access to Host Adapter.
+	Read the Host Adapter Interrupt Register.
       */
-      BusLogic_AcquireHostAdapterLockID(HostAdapter, &Lock);
-      /*
-	Handle Interrupts appropriately for each Host Adapter type.
-      */
-      if (BusLogic_MultiMasterHostAdapterP(HostAdapter))
+      InterruptRegister.All = BusLogic_ReadInterruptRegister(HostAdapter);
+      if (InterruptRegister.Bits.InterruptValid)
 	{
-	  BusLogic_InterruptRegister_T InterruptRegister;
 	  /*
-	    Read the Host Adapter Interrupt Register.
+	    Acknowledge the interrupt and reset the Host Adapter
+	    Interrupt Register.
 	  */
-	  InterruptRegister.All = BusLogic_ReadInterruptRegister(HostAdapter);
-	  if (InterruptRegister.Bits.InterruptValid)
-	    {
-	      /*
-		Acknowledge the interrupt and reset the Host Adapter
-		Interrupt Register.
-	      */
-	      BusLogic_InterruptReset(HostAdapter);
-	      /*
-		Process valid External SCSI Bus Reset and Incoming Mailbox
-		Loaded Interrupts.  Command Complete Interrupts are noted,
-		and Outgoing Mailbox Available Interrupts are ignored, as
-		they are never enabled.
-	      */
-	      if (InterruptRegister.Bits.ExternalBusReset)
-		{
-		  HostAdapter->HostAdapterExternalReset = true;
-		  HostAdapterResetRequired = true;
-		}
-	      else if (InterruptRegister.Bits.IncomingMailboxLoaded)
-		BusLogic_ScanIncomingMailboxes(HostAdapter);
-	      else if (InterruptRegister.Bits.CommandComplete)
-		HostAdapter->HostAdapterCommandCompleted = true;
-	    }
-	}
-      else
-	{
+	  BusLogic_InterruptReset(HostAdapter);
 	  /*
-	    Check if there is a pending interrupt for this Host Adapter.
+	    Process valid External SCSI Bus Reset and Incoming Mailbox
+	    Loaded Interrupts.  Command Complete Interrupts are noted,
+	    and Outgoing Mailbox Available Interrupts are ignored, as
+	    they are never enabled.
 	  */
-	  if (FlashPoint_InterruptPending(HostAdapter->CardHandle))
-	    switch (FlashPoint_HandleInterrupt(HostAdapter->CardHandle))
-	      {
-	      case FlashPoint_NormalInterrupt:
-		break;
-	      case FlashPoint_ExternalBusReset:
-		HostAdapter->HostAdapterExternalReset = true;
-		HostAdapterResetRequired = true;
-		break;
-	      case FlashPoint_InternalError:
-		BusLogic_Warning("Internal FlashPoint Error detected"
-				 " - Resetting Host Adapter\n", HostAdapter);
-		HostAdapter->HostAdapterInternalError = true;
-		HostAdapterResetRequired = true;
-		break;
-	      }
+	  if (InterruptRegister.Bits.ExternalBusReset)
+	    HostAdapter->HostAdapterExternalReset = true;
+	  else if (InterruptRegister.Bits.IncomingMailboxLoaded)
+	    BusLogic_ScanIncomingMailboxes(HostAdapter);
+	  else if (InterruptRegister.Bits.CommandComplete)
+	    HostAdapter->HostAdapterCommandCompleted = true;
 	}
+    }
+  else
+    {
       /*
-	Release exclusive access to Host Adapter.
+	Check if there is a pending interrupt for this Host Adapter.
       */
-      BusLogic_ReleaseHostAdapterLockID(HostAdapter, &Lock);
+      if (FlashPoint_InterruptPending(HostAdapter->CardHandle))
+	switch (FlashPoint_HandleInterrupt(HostAdapter->CardHandle))
+	  {
+	  case FlashPoint_NormalInterrupt:
+	    break;
+	  case FlashPoint_ExternalBusReset:
+	    HostAdapter->HostAdapterExternalReset = true;
+	    break;
+	  case FlashPoint_InternalError:
+	    BusLogic_Warning("Internal FlashPoint Error detected"
+			     " - Resetting Host Adapter\n", HostAdapter);
+	    HostAdapter->HostAdapterInternalError = true;
+	    break;
+	  }
     }
   /*
     Process any completed CCBs.
   */
-  if (BusLogic_FirstCompletedCCB != NULL)
-    BusLogic_ProcessCompletedCCBs();
+  if (HostAdapter->FirstCompletedCCB != NULL)
+    BusLogic_ProcessCompletedCCBs(HostAdapter);
   /*
-    Iterate over the Host Adapters performing any requested
-    Host Adapter Resets.
+    Reset the Host Adapter if requested.
   */
-  if (HostAdapterResetRequired)
-    for (HostAdapter = FirstHostAdapter;
-	 HostAdapter != NULL;
-	 HostAdapter = HostAdapter->Next)
-      if (HostAdapter->HostAdapterExternalReset ||
-	  HostAdapter->HostAdapterInternalError)
-	{
-	  BusLogic_ResetHostAdapter(HostAdapter, NULL, 0);
-	  HostAdapter->HostAdapterExternalReset = false;
-	  HostAdapter->HostAdapterInternalError = false;
-	  scsi_mark_host_reset(HostAdapter->SCSI_Host);
-	}
+  if (HostAdapter->HostAdapterExternalReset ||
+      HostAdapter->HostAdapterInternalError)
+    {
+      BusLogic_ResetHostAdapter(HostAdapter, NULL, 0);
+      HostAdapter->HostAdapterExternalReset = false;
+      HostAdapter->HostAdapterInternalError = false;
+      scsi_mark_host_reset(HostAdapter->SCSI_Host);
+    }
+  /*
+    Release exclusive access to Host Adapter.
+  */
+  BusLogic_ReleaseHostAdapterLockIH(HostAdapter, &ProcessorFlags);
 }
 
-/*
- * This is the low-level interrupt handler:
- * we get the io request lock here to guarantee
- * that all of this is atomic wrt the setup
- * functions.
- */
-static void BusLogic_InterruptHandler(int IRQ_Channel,
-				      void *DeviceIdentifier,
-				      Registers_T *InterruptRegisters)
-{
-	unsigned long flags;
-
-	spin_lock_irqsave(&io_request_lock, flags);
-	do_BusLogic_InterruptHandler(IRQ_Channel, DeviceIdentifier, InterruptRegisters);
-	spin_unlock_irqrestore(&io_request_lock, flags);
-}
 
 /*
   BusLogic_WriteOutgoingMailbox places CCB and Action Code into an Outgoing
@@ -3454,7 +3334,7 @@
   void *BufferPointer = Command->request_buffer;
   int BufferLength = Command->request_bufflen;
   int SegmentCount = Command->use_sg;
-  BusLogic_Lock_T Lock;
+  ProcessorFlags_T ProcessorFlags;
   BusLogic_CCB_T *CCB;
   /*
     SCSI REQUEST_SENSE commands will be executed automatically by the Host
@@ -3470,7 +3350,7 @@
   /*
     Acquire exclusive access to Host Adapter.
   */
-  BusLogic_AcquireHostAdapterLock(HostAdapter, &Lock);
+  BusLogic_AcquireHostAdapterLock(HostAdapter, &ProcessorFlags);
   /*
     Allocate a CCB from the Host Adapter's free list.  In the unlikely event
     that there are none available and memory allocation fails, wait 1 second
@@ -3651,13 +3531,13 @@
 	been called, or it may still be pending.
       */
       if (CCB->Status == BusLogic_CCB_Completed)
-	BusLogic_ProcessCompletedCCBs();
+	BusLogic_ProcessCompletedCCBs(HostAdapter);
     }
   /*
     Release exclusive access to Host Adapter.
   */
 Done:
-  BusLogic_ReleaseHostAdapterLock(HostAdapter, &Lock);
+  BusLogic_ReleaseHostAdapterLock(HostAdapter, &ProcessorFlags);
   return 0;
 }
 
@@ -3671,7 +3551,7 @@
   BusLogic_HostAdapter_T *HostAdapter =
     (BusLogic_HostAdapter_T *) Command->host->hostdata;
   int TargetID = Command->target;
-  BusLogic_Lock_T Lock;
+  ProcessorFlags_T ProcessorFlags;
   BusLogic_CCB_T *CCB;
   int Result;
   BusLogic_IncrementErrorCounter(
@@ -3679,7 +3559,7 @@
   /*
     Acquire exclusive access to Host Adapter.
   */
-  BusLogic_AcquireHostAdapterLock(HostAdapter, &Lock);
+  BusLogic_AcquireHostAdapterLock(HostAdapter, &ProcessorFlags);
   /*
     If this Command has already completed, then no Abort is necessary.
   */
@@ -3772,7 +3652,7 @@
       Result = SCSI_ABORT_PENDING;
       if (CCB->Status == BusLogic_CCB_Completed)
 	{
-	  BusLogic_ProcessCompletedCCBs();
+	  BusLogic_ProcessCompletedCCBs(HostAdapter);
 	  Result = SCSI_ABORT_SUCCESS;
 	}
     }
@@ -3780,7 +3660,7 @@
     Release exclusive access to Host Adapter.
   */
 Done:
-  BusLogic_ReleaseHostAdapterLock(HostAdapter, &Lock);
+  BusLogic_ReleaseHostAdapterLock(HostAdapter, &ProcessorFlags);
   return Result;
 }
 
@@ -3794,7 +3674,7 @@
 				     SCSI_Command_T *Command,
 				     unsigned int ResetFlags)
 {
-  BusLogic_Lock_T Lock;
+  ProcessorFlags_T ProcessorFlags;
   BusLogic_CCB_T *CCB;
   int TargetID, Result;
   boolean HardReset;
@@ -3818,7 +3698,7 @@
   /*
     Acquire exclusive access to Host Adapter.
   */
-  BusLogic_AcquireHostAdapterLock(HostAdapter, &Lock);
+  BusLogic_AcquireHostAdapterLock(HostAdapter, &ProcessorFlags);
   /*
     If this is an Asynchronous Reset and this Command has already completed,
     then no Reset is necessary.
@@ -3941,7 +3821,7 @@
     Release exclusive access to Host Adapter.
   */
 Done:
-  BusLogic_ReleaseHostAdapterLock(HostAdapter, &Lock);
+  BusLogic_ReleaseHostAdapterLock(HostAdapter, &ProcessorFlags);
   return Result;
 }
 
@@ -3957,14 +3837,14 @@
 {
   int TargetID = Command->target;
   BusLogic_CCB_T *CCB, *XCCB;
-  BusLogic_Lock_T Lock;
+  ProcessorFlags_T ProcessorFlags;
   int Result = -1;
   BusLogic_IncrementErrorCounter(
     &HostAdapter->TargetStatistics[TargetID].BusDeviceResetsRequested);
   /*
     Acquire exclusive access to Host Adapter.
   */
-  BusLogic_AcquireHostAdapterLock(HostAdapter, &Lock);
+  BusLogic_AcquireHostAdapterLock(HostAdapter, &ProcessorFlags);
   /*
     If this is an Asynchronous Reset and this Command has already completed,
     then no Reset is necessary.
@@ -4116,7 +3996,7 @@
   if (BusLogic_FlashPointHostAdapterP(HostAdapter))
     if (CCB->Status == BusLogic_CCB_Completed)
       {
-	BusLogic_ProcessCompletedCCBs();
+	BusLogic_ProcessCompletedCCBs(HostAdapter);
 	Result = SCSI_RESET_SUCCESS;
       }
   /*
@@ -4129,7 +4009,7 @@
   /*
     Release exclusive access to Host Adapter.
   */
-  BusLogic_ReleaseHostAdapterLock(HostAdapter, &Lock);
+  BusLogic_ReleaseHostAdapterLock(HostAdapter, &ProcessorFlags);
   return Result;
 }
 
@@ -4325,7 +4205,7 @@
   if (WriteFlag) return 0;
   for (HostAdapter = BusLogic_FirstRegisteredHostAdapter;
        HostAdapter != NULL;
-       HostAdapter = HostAdapter->NextAll)
+       HostAdapter = HostAdapter->Next)
     if (HostAdapter->HostNumber == HostNumber) break;
   if (HostAdapter == NULL)
     {
diff -u --recursive --new-file v2.1.96/linux/drivers/scsi/BusLogic.h linux/drivers/scsi/BusLogic.h
--- v2.1.96/linux/drivers/scsi/BusLogic.h	Wed Apr  1 20:11:52 1998
+++ linux/drivers/scsi/BusLogic.h	Thu Apr 16 16:24:16 1998
@@ -2,7 +2,7 @@
 
   Linux Driver for BusLogic MultiMaster and FlashPoint SCSI Host Adapters
 
-  Copyright 1995 by Leonard N. Zubkoff <lnz@dandelion.com>
+  Copyright 1995-1998 by Leonard N. Zubkoff <lnz@dandelion.com>
 
   This program is free software; you may redistribute and/or modify it under
   the terms of the GNU General Public License Version 2 as published by the
@@ -36,8 +36,10 @@
 
 typedef kdev_t KernelDevice_T;
 typedef struct proc_dir_entry PROC_DirectoryEntry_T;
+typedef unsigned long ProcessorFlags_T;
 typedef struct pt_regs Registers_T;
 typedef struct partition PartitionTable_T;
+typedef struct pci_dev PCI_Device_T;
 typedef Scsi_Host_Template SCSI_Host_Template_T;
 typedef struct Scsi_Host SCSI_Host_T;
 typedef struct scsi_device SCSI_Device_T;
@@ -966,16 +968,6 @@
 
 
 /*
-  Define the Lock data structure.  Until a true symmetric multiprocessing
-  kernel with fine grained locking is available, acquiring the lock is
-  implemented as saving the processor flags and disabling interrupts, and
-  releasing the lock restores the saved processor flags.
-*/
-
-typedef unsigned long BusLogic_Lock_T;
-
-
-/*
   Define the Outgoing Mailbox Action Codes.
 */
 
@@ -1374,7 +1366,6 @@
   unsigned char ModelName[9];
   unsigned char FirmwareVersion[6];
   unsigned char FullModelName[18];
-  unsigned char InterruptLabel[68];
   unsigned char Bus;
   unsigned char Device;
   unsigned char IRQ_Channel;
@@ -1401,7 +1392,8 @@
   boolean HostAdapterInitialized:1;
   boolean HostAdapterExternalReset:1;
   boolean HostAdapterInternalError:1;
-  volatile boolean HostAdapterCommandCompleted:1;
+  boolean ProcessCompletedCCBsActive;
+  volatile boolean HostAdapterCommandCompleted;
   unsigned short HostAdapterScatterGatherLimit;
   unsigned short DriverScatterGatherLimit;
   unsigned short MaxTargetDevices;
@@ -1430,9 +1422,10 @@
   FlashPoint_Info_T FlashPointInfo;
   FlashPoint_CardHandle_T CardHandle;
   struct BusLogic_HostAdapter *Next;
-  struct BusLogic_HostAdapter *NextAll;
   BusLogic_CCB_T *All_CCBs;
   BusLogic_CCB_T *Free_CCBs;
+  BusLogic_CCB_T *FirstCompletedCCB;
+  BusLogic_CCB_T *LastCompletedCCB;
   BusLogic_CCB_T *BusDeviceResetPendingCCB[BusLogic_MaxTargetDevices];
   BusLogic_ErrorRecoveryStrategy_T
     ErrorRecoveryStrategy[BusLogic_MaxTargetDevices];
@@ -1514,10 +1507,8 @@
 
 static inline
 void BusLogic_AcquireHostAdapterLock(BusLogic_HostAdapter_T *HostAdapter,
-				     BusLogic_Lock_T *Lock)
+				     ProcessorFlags_T *ProcessorFlags)
 {
-  save_flags(*Lock);
-  cli();
 }
 
 
@@ -1527,33 +1518,36 @@
 
 static inline
 void BusLogic_ReleaseHostAdapterLock(BusLogic_HostAdapter_T *HostAdapter,
-				     BusLogic_Lock_T *Lock)
+				     ProcessorFlags_T *ProcessorFlags)
 {
-  restore_flags(*Lock);
 }
 
 
 /*
-  BusLogic_AcquireHostAdapterLockID acquires exclusive access to Host Adapter,
-  but is only called when interrupts are disabled.
+  BusLogic_AcquireHostAdapterLockIH acquires exclusive access to Host Adapter,
+  but is only called from the interrupt handler.
 */
 
 static inline
-void BusLogic_AcquireHostAdapterLockID(BusLogic_HostAdapter_T *HostAdapter,
-				       BusLogic_Lock_T *Lock)
+void BusLogic_AcquireHostAdapterLockIH(BusLogic_HostAdapter_T *HostAdapter,
+				       ProcessorFlags_T *ProcessorFlags)
 {
+  extern spinlock_t io_request_lock;
+  spin_lock_irqsave(&io_request_lock, *ProcessorFlags);
 }
 
 
 /*
-  BusLogic_ReleaseHostAdapterLockID releases exclusive access to Host Adapter,
-  but is only called when interrupts are disabled.
+  BusLogic_ReleaseHostAdapterLockIH releases exclusive access to Host Adapter,
+  but is only called from the interrupt handler.
 */
 
 static inline
-void BusLogic_ReleaseHostAdapterLockID(BusLogic_HostAdapter_T *HostAdapter,
-				       BusLogic_Lock_T *Lock)
+void BusLogic_ReleaseHostAdapterLockIH(BusLogic_HostAdapter_T *HostAdapter,
+				       ProcessorFlags_T *ProcessorFlags)
 {
+  extern spinlock_t io_request_lock;
+  spin_unlock_irqrestore(&io_request_lock, *ProcessorFlags);
 }
 
 
@@ -1685,8 +1679,8 @@
 
 /*
   Virtual_to_32Bit_Virtual maps between Kernel Virtual Addresses and
-  32 Bit Kernel Virtual Addresses.  This avoids compilation warnings
-  on 64 Bit architectures.
+  32 bit Kernel Virtual Addresses.  This avoids compilation warnings
+  on 64 bit architectures.
 */
 
 static inline
@@ -1748,17 +1742,6 @@
   else Index = (Amount < 256*1024 ? 8 : 9);
   CommandSizeBuckets[Index]++;
 }
-
-
-/*
-  Define compatibility macros between Linux 2.0 and Linux 2.1.
-*/
-
-#if LINUX_VERSION_CODE < 0x20100
-
-#define MODULE_PARM(Variable, Type)
-
-#endif
 
 
 /*
diff -u --recursive --new-file v2.1.96/linux/drivers/scsi/advansys.c linux/drivers/scsi/advansys.c
--- v2.1.96/linux/drivers/scsi/advansys.c	Tue Apr 14 14:29:21 1998
+++ linux/drivers/scsi/advansys.c	Fri Apr 17 22:06:22 1998
@@ -598,7 +598,7 @@
      Erik Ratcliffe <erik@caldera.com> has done testing of the
      AdvanSys driver in the Caldera releases.
 
-     Rik van Riel <H.H.vanRiel@fys.ruu.nl> provided a patch to
+     Rik van Riel <H.H.vanRiel@phys.uu.nl> provided a patch to
      AscWaitTixISRDone() which he found necessary to make the
      driver work with a SCSI-1 disk.
 
diff -u --recursive --new-file v2.1.96/linux/drivers/scsi/fdomain.c linux/drivers/scsi/fdomain.c
--- v2.1.96/linux/drivers/scsi/fdomain.c	Tue Apr 14 14:29:22 1998
+++ linux/drivers/scsi/fdomain.c	Fri Apr 17 21:56:31 1998
@@ -400,7 +400,7 @@
 static int               FIFO_Size = 0x2000; /* 8k FIFO for
 						pre-tmc18c30 chips */
 
-extern void              fdomain_16x0_intr( int irq, void *dev_id,
+extern void              do_fdomain_16x0_intr( int irq, void *dev_id,
 					    struct pt_regs * regs );
 
 static unsigned long addresses[] = {
@@ -775,7 +775,7 @@
 	   PCI_DEVICE_ID_FD_36C70 );
 #endif 
 
-   if ((pdev = pci_find_device(PCI_VENDOR_ID, PCI_DEVICE_ID, pdev)) == NULL)
+   if ((pdev = pci_find_device(PCI_VENDOR_ID_FD, PCI_DEVICE_ID_FD_36C70, pdev)) == NULL)
      return 0;
        
 #if DEBUG_DETECT
diff -u --recursive --new-file v2.1.96/linux/drivers/scsi/gdth.c linux/drivers/scsi/gdth.c
--- v2.1.96/linux/drivers/scsi/gdth.c	Tue Apr 14 14:29:22 1998
+++ linux/drivers/scsi/gdth.c	Fri Apr 17 21:58:48 1998
@@ -73,7 +73,7 @@
  * Initial revision
  *
  *
- * $Id: gdth.c,v 1.3 1998/02/25 23:52:32 ecd Exp $ 
+ * $Id: gdth.c,v 1.4 1998/04/15 14:35:26 mj Exp $ 
  ************************************************************************/
 
 #ifdef MODULE
@@ -473,8 +473,7 @@
 	if ((error = pcibios_read_config_dword(pcistr->bus,pcistr->device_fn,
                                            PCI_ROM_ADDRESS,
                                            (int *) &pcistr->bios))) {
-		printk("GDT-PCI: error %s reading configuration space",
-		       pcibios_strerror(error));
+		printk("GDT-PCI: error %d reading configuration space", error);
 		return -1;
 		}
 	pcistr->irq = pdev->irq;
@@ -499,8 +498,7 @@
                                            GDTH_BASEP&pcistr->bios)) ||
         (error = pcibios_read_config_byte(pcistr->bus,pcistr->device_fn,
                                           PCI_INTERRUPT_LINE,&pcistr->irq))) {
-        printk("GDT-PCI: error %s reading configuration space",
-               pcibios_strerror(error));
+        printk("GDT-PCI: error %d reading configuration space", error);
         return -1;
     }
 #endif
diff -u --recursive --new-file v2.1.96/linux/drivers/scsi/hosts.c linux/drivers/scsi/hosts.c
--- v2.1.96/linux/drivers/scsi/hosts.c	Fri Jan 30 11:28:08 1998
+++ linux/drivers/scsi/hosts.c	Tue Apr 14 17:44:22 1998
@@ -209,6 +209,10 @@
 #include "psi240i.h"
 #endif
 
+#ifdef CONFIG_SCSI_PLUTO
+#include "pluto.h"
+#endif
+
 #ifdef CONFIG_SCSI_DEBUG
 #include "scsi_debug.h"
 #endif
@@ -381,6 +385,9 @@
 #endif
 #ifdef CONFIG_SCSI_MAC53C94
     SCSI_MAC53C94,
+#endif
+#ifdef CONFIG_SCSI_PLUTO
+    PLUTO,
 #endif
 #ifdef CONFIG_SCSI_DEBUG
     SCSI_DEBUG,
diff -u --recursive --new-file v2.1.96/linux/drivers/scsi/hosts.h linux/drivers/scsi/hosts.h
--- v2.1.96/linux/drivers/scsi/hosts.h	Tue Apr 14 14:29:22 1998
+++ linux/drivers/scsi/hosts.h	Tue Apr 14 17:44:22 1998
@@ -466,8 +466,15 @@
  * we need to leave extra room in some of the data structures.	Doing a
  * realloc to enlarge the structures would be riddled with race conditions,
  * so until a better solution is discovered, we use this crude approach
+ *
+ * Even bigger hack for SparcSTORAGE arrays. Those are at least 6 disks, but
+ * usually up to 30 disks, so everyone would need to change this. -jj
  */
-#define SD_EXTRA_DEVS 2
+#ifdef CONFIG_SCSI_PLUTO_MODULE
+#define SD_EXTRA_DEVS 40
+#else
+#define SD_EXTRA_DEVS 4
+#endif
 #define ST_EXTRA_DEVS 2
 #define SR_EXTRA_DEVS 2
 #define SG_EXTRA_DEVS (SD_EXTRA_DEVS + SR_EXTRA_DEVS + ST_EXTRA_DEVS)
diff -u --recursive --new-file v2.1.96/linux/drivers/scsi/pluto.c linux/drivers/scsi/pluto.c
--- v2.1.96/linux/drivers/scsi/pluto.c	Fri Jan 23 18:10:32 1998
+++ linux/drivers/scsi/pluto.c	Tue Apr 14 17:44:22 1998
@@ -1,6 +1,7 @@
 /* pluto.c: SparcSTORAGE Array SCSI host adapter driver.
  *
  * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ *
  */
 
 #include <linux/kernel.h>
@@ -12,6 +13,10 @@
 #include <linux/proc_fs.h>
 #include <linux/stat.h>
 #include <linux/init.h>
+#include <linux/config.h>
+#ifdef CONFIG_KERNELD
+#include <linux/kerneld.h>
+#endif
 
 #include <asm/irq.h>
 
@@ -45,9 +50,9 @@
 	Scsi_Cmnd cmd;
 	char inquiry[256];
 	fc_channel *fc;
-} *fcs __initdata;
-static int fcscount __initdata;
-static atomic_t fcss __initdata;
+} *fcs __initdata = { 0 };
+static int fcscount __initdata = 0;
+static atomic_t fcss __initdata = ATOMIC_INIT(0);
 static struct timer_list fc_timer __initdata = { 0 };
 struct semaphore fc_sem __initdata = MUTEX_LOCKED;
 
@@ -98,8 +103,16 @@
 	for_each_online_fc_channel(fc)
 		fcscount++;
 	PLND(("%d channels online\n", fcscount))
-	if (!fcscount)
-		return 0;
+	if (!fcscount) {
+#if defined(MODULE) && defined(CONFIG_FC4_SOC_MODULE) && defined(CONFIG_KERNELD)
+		request_module("soc");
+		
+		for_each_online_fc_channel(fc)
+			fcscount++;
+		if (!fcscount)
+#endif
+			return 0;
+	}
 	fcs = (struct ctrl_inquiry *) scsi_init_malloc (sizeof (struct ctrl_inquiry) * fcscount, GFP_DMA);
 	if (!fcs) {
 		printk ("PLUTO: Not enough memory to probe\n");
@@ -213,6 +226,8 @@
 				
 				nplutos++;
 				
+				if (fc->module) __MOD_INC_USE_COUNT(fc->module);
+				
 				pluto = (struct pluto *)host->hostdata;
 				
 				host->max_id = inq->targets;
@@ -256,9 +271,13 @@
 {
 	struct pluto *pluto = (struct pluto *)host->hostdata;
 	fc_channel *fc = pluto->fc;
+
+	if (fc->module) __MOD_DEC_USE_COUNT(fc->module);
 	
 	fc->fcp_register(fc, TYPE_SCSI_FCP, 1);
+	PLND((" releasing pluto.\n"));
 	kfree (fc->ages);
+	PLND(("released pluto!\n"));
 	return 0;
 }
 
@@ -288,7 +307,7 @@
 static int pluto_encode_addr(Scsi_Cmnd *SCpnt, u16 *addr)
 {
 	PLND(("encode addr %d %d %d\n", SCpnt->channel, SCpnt->target, SCpnt->cmnd[1] & 0xe0))
-	/* We don't support LUNs */
+	/* We don't support LUNs - neither does SSA :) */
 	if (SCpnt->cmnd[1] & 0xe0) return -EINVAL;
 	if (!SCpnt->channel) {
 		if (SCpnt->target) return -EINVAL;
diff -u --recursive --new-file v2.1.96/linux/drivers/scsi/pluto.h linux/drivers/scsi/pluto.h
--- v2.1.96/linux/drivers/scsi/pluto.h	Fri Jan 23 18:10:32 1998
+++ linux/drivers/scsi/pluto.h	Tue Apr 14 17:44:22 1998
@@ -43,17 +43,23 @@
 const char * pluto_info(struct Scsi_Host *);
 
 #define PLUTO {							\
+	name:			"Sparc Storage Array 100/200",	\
 	detect:			pluto_detect,			\
 	release:		pluto_release,			\
 	info:			pluto_info,			\
 	queuecommand:		fcp_scsi_queuecommand,		\
-	abort:			fcp_scsi_abort,			\
-	reset:			fcp_scsi_reset,			\
 	can_queue:		PLUTO_CAN_QUEUE,		\
 	this_id:		-1,				\
 	sg_tablesize:		1,				\
 	cmd_per_lun:		1,				\
-	use_clustering:		ENABLE_CLUSTERING		\
+	use_clustering:		ENABLE_CLUSTERING,		\
+	use_new_eh_code:	FCP_SCSI_USE_NEW_EH_CODE,	\
+	abort:			fcp_old_abort,			\
+	eh_abort_handler:	fcp_scsi_abort,			\
+	eh_device_reset_handler:fcp_scsi_dev_reset,		\
+	eh_bus_reset_handler:	fcp_scsi_bus_reset,		\
+	eh_host_reset_handler:	fcp_scsi_host_reset,		\
 }	
 
 #endif /* !(_PLUTO_H) */
+
diff -u --recursive --new-file v2.1.96/linux/drivers/scsi/qlogicpti.c linux/drivers/scsi/qlogicpti.c
--- v2.1.96/linux/drivers/scsi/qlogicpti.c	Tue Apr 14 14:29:23 1998
+++ linux/drivers/scsi/qlogicpti.c	Tue Apr 14 17:44:22 1998
@@ -422,36 +422,37 @@
 
 	/* Load the firmware. */
 #if !defined(MODULE) && !defined(__sparc_v9__)
-	dvma_addr = (unsigned long) mmu_lockarea((char *)&risc_code01[0],
-						 (sizeof(u_short) * risc_code_length01));
-	param[0] = MBOX_LOAD_RAM;
-	param[1] = risc_code_addr01;
-	param[2] = (dvma_addr >> 16);
-	param[3] = (dvma_addr & 0xffff);
-	param[4] = (sizeof(u_short) * risc_code_length01);
-	if(qlogicpti_mbox_command(qpti, param, 1) ||
-	   (param[0] != MBOX_COMMAND_COMPLETE)) {
-		printk(KERN_EMERG "qlogicpti%d: Firmware dload failed, I'm bolixed!\n",
-		       qpti->qpti_id);
-		restore_flags(flags);
-		return 1;
-	}
-	mmu_unlockarea((char *)dvma_addr, (sizeof(u_short) * risc_code_length01));
-#else
-	/* We need to do it this slow way always on Ultra. */
-	for(i = 0; i < risc_code_length01; i++) {
-		param[0] = MBOX_WRITE_RAM_WORD;
-		param[1] = risc_code_addr01 + i;
-		param[2] = risc_code01[i];
+	if (sparc_cpu_model != sun4d) {
+		dvma_addr = (unsigned long) mmu_lockarea((char *)&risc_code01[0],
+							 (sizeof(u_short) * risc_code_length01));
+		param[0] = MBOX_LOAD_RAM;
+		param[1] = risc_code_addr01;
+		param[2] = (dvma_addr >> 16);
+		param[3] = (dvma_addr & 0xffff);
+		param[4] = (sizeof(u_short) * risc_code_length01);
 		if(qlogicpti_mbox_command(qpti, param, 1) ||
-		   param[0] != MBOX_COMMAND_COMPLETE) {
-			printk("qlogicpti%d: Firmware dload failed, I'm bolixed!\n",
+		   (param[0] != MBOX_COMMAND_COMPLETE)) {
+			printk(KERN_EMERG "qlogicpti%d: Firmware dload failed, I'm bolixed!\n",
 			       qpti->qpti_id);
 			restore_flags(flags);
 			return 1;
 		}
-	}
+		mmu_unlockarea((char *)dvma_addr, (sizeof(u_short) * risc_code_length01));
+	} else
 #endif
+	/* We need to do it this slow way always on Ultra, SS[12]000. */
+		for(i = 0; i < risc_code_length01; i++) {
+			param[0] = MBOX_WRITE_RAM_WORD;
+			param[1] = risc_code_addr01 + i;
+			param[2] = risc_code01[i];
+			if(qlogicpti_mbox_command(qpti, param, 1) ||
+			   param[0] != MBOX_COMMAND_COMPLETE) {
+				printk("qlogicpti%d: Firmware dload failed, I'm bolixed!\n",
+				       qpti->qpti_id);
+				restore_flags(flags);
+				return 1;
+			}
+		}
 
 	/* Reset the ISP again. */
 	qregs->hcctrl = HCCTRL_RESET;
@@ -584,8 +585,13 @@
 
 	tpnt->proc_dir = &proc_scsi_qlogicpti;
 	qptichain = 0;
-	if(!SBus_chain)
+	if(!SBus_chain) {
+#ifdef __sparc_v9__
+		return 0; /* Could be a PCI-only machine. */
+#else
 		panic("No SBUS in qlogicpti_detect()");
+#endif
+	}
 	for_each_sbus(sbus) {
 		for_each_sbusdev(sbdev_iter, sbus) {
 			qpti_dev = sbdev_iter;
@@ -778,8 +784,9 @@
 			nqptis_in_use++;
 		}
 	}
-	printk("QPTI: Total of %d PTI Qlogic/ISP hosts found, %d actually in use.\n",
-	       nqptis, nqptis_in_use);
+	if (nqptis)
+		printk("QPTI: Total of %d PTI Qlogic/ISP hosts found, %d actually in use.\n",
+		       nqptis, nqptis_in_use);
 	qptis_running = nqptis_in_use;
 	return nqptis;
 }
@@ -931,7 +938,9 @@
 
 static inline void update_can_queue(struct Scsi_Host *host, u_int in_ptr, u_int out_ptr)
 {
-	int num_free = QLOGICISP_REQ_QUEUE_LEN - REQ_QUEUE_DEPTH(in_ptr, out_ptr);
+	/* Temporary workaround until bug is found and fixed (one bug has been found
+	   already, but fixing it makes things even worse) -jj */
+	int num_free = QLOGICISP_REQ_QUEUE_LEN - REQ_QUEUE_DEPTH(in_ptr, out_ptr) - 64;
 	host->can_queue = host->host_busy + num_free;
 	host->sg_tablesize = QLOGICISP_MAX_SG(num_free);
 }
@@ -1256,4 +1265,6 @@
 Scsi_Host_Template driver_template = QLOGICPTI;
 
 #include "scsi_module.c"
+
+EXPORT_NO_SYMBOLS;
 #endif /* MODULE */
diff -u --recursive --new-file v2.1.96/linux/fs/binfmt_aout.c linux/fs/binfmt_aout.c
--- v2.1.96/linux/fs/binfmt_aout.c	Wed Apr  1 20:11:53 1998
+++ linux/fs/binfmt_aout.c	Tue Apr 14 17:44:23 1998
@@ -349,7 +349,7 @@
 		return retval;
 
 	/* OK, This is the point of no return */
-#ifdef __sparc__
+#if defined(__sparc__) && !defined(__sparc_v9__)
 	memcpy(&current->tss.core_exec, &ex, sizeof(struct exec));
 #endif
 
diff -u --recursive --new-file v2.1.96/linux/fs/proc/inode.c linux/fs/proc/inode.c
--- v2.1.96/linux/fs/proc/inode.c	Mon Apr  6 17:41:01 1998
+++ linux/fs/proc/inode.c	Tue Apr 14 17:44:23 1998
@@ -70,6 +70,13 @@
 static void proc_delete_inode(struct inode *inode)
 {
 	struct proc_dir_entry *de = inode->u.generic_ip;
+
+#if defined(CONFIG_SUN_OPENPROMFS) || defined(CONFIG_SUN_OPENPROMFS_MODULE)
+	if ((inode->i_ino >= PROC_OPENPROM_FIRST) &&
+	    (inode->i_ino <  PROC_OPENPROM_FIRST + PROC_NOPENPROM))
+		return;
+#endif	
+
 	if (de) {
 		/*
 		 * Call the fill_inode hook to release module counts.
diff -u --recursive --new-file v2.1.96/linux/fs/proc/openpromfs.c linux/fs/proc/openpromfs.c
--- v2.1.96/linux/fs/proc/openpromfs.c	Fri Jan 16 20:37:34 1998
+++ linux/fs/proc/openpromfs.c	Tue Apr 14 17:44:23 1998
@@ -1,4 +1,4 @@
-/* $Id: openpromfs.c,v 1.21 1997/08/19 02:05:48 davem Exp $
+/* $Id: openpromfs.c,v 1.26 1998/01/28 09:55:32 ecd Exp $
  * openpromfs.c: /proc/openprom handling routines
  *
  * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
@@ -55,13 +55,14 @@
 #define NODEP2INO(no) (no + PROC_OPENPROM_FIRST + last_node)
 
 static int openpromfs_create (struct inode *, struct dentry *, int);
-static int openpromfs_readdir(struct inode *, struct file *, void *, filldir_t);
+static int openpromfs_readdir(struct file *, void *, filldir_t);
 static int openpromfs_lookup(struct inode *, struct dentry *dentry);
 static int openpromfs_unlink (struct inode *, struct dentry *dentry);
 
-static long nodenum_read(struct inode *inode, struct file *file,
-			 char *buf, unsigned long count)
+static ssize_t nodenum_read(struct file *file, char *buf,
+			    size_t count, loff_t *ppos)
 {
+	struct inode *inode = file->f_dentry->d_inode;
 	char buffer[10];
 	
 	if (count < 0 || !inode->u.generic_ip)
@@ -76,9 +77,10 @@
 	return count;
 }
 
-static long property_read(struct inode *inode, struct file *filp,
-			  char *buf, unsigned long count)
+static ssize_t property_read(struct file *filp, char *buf,
+			     size_t count, loff_t *ppos)
 {
+	struct inode *inode = filp->f_dentry->d_inode;
 	int i, j, k;
 	u32 node;
 	char *p;
@@ -212,8 +214,8 @@
 	return count;
 }
 
-static long property_write(struct inode *inode, struct file *filp,
-			   const char *buf, unsigned long count)
+static ssize_t property_write(struct file *filp, const char *buf,
+			      size_t count, loff_t *ppos)
 {
 	int i, j, k;
 	char *p;
@@ -224,7 +226,7 @@
 	if (filp->f_pos >= 0xffffff)
 		return -EINVAL;
 	if (!filp->private_data) {
-		i = property_read (inode, filp, NULL, 0);
+		i = property_read (filp, NULL, 0, 0);
 		if (i)
 			return i;
 	}
@@ -741,9 +743,9 @@
 	return 0;
 }
 
-static int openpromfs_readdir(struct inode * inode, struct file * filp,
-	void * dirent, filldir_t filldir)
+static int openpromfs_readdir(struct file * filp, void * dirent, filldir_t filldir)
 {
+	struct inode *inode = filp->f_dentry->d_inode;
 	unsigned int ino;
 	u32 n;
 	int i, j;
@@ -1044,10 +1046,6 @@
 int init_module (void)
 #endif
 {
-#ifndef __sparc_v9__
-	if (!romvec->pv_romvers)
-		return RET(ENODEV);
-#endif
 	nodes = (openpromfs_node *)__get_free_pages(GFP_KERNEL, 0);
 	if (!nodes) {
 		printk (KERN_WARNING "/proc/openprom: can't get free page\n");
diff -u --recursive --new-file v2.1.96/linux/fs/proc/proc_devtree.c linux/fs/proc/proc_devtree.c
--- v2.1.96/linux/fs/proc/proc_devtree.c	Sun Jan  4 00:53:41 1998
+++ linux/fs/proc/proc_devtree.c	Tue Apr 14 17:34:00 1998
@@ -41,8 +41,8 @@
  * and "@10" to it.
  */
 
-static int devtree_readlink(struct inode *, char *, int);
-static struct dentry *devtree_follow_link(struct inode *, struct dentry *);
+static int devtree_readlink(struct dentry *, char *, int);
+static struct dentry *devtree_follow_link(struct dentry *, struct dentry *);
 
 struct inode_operations devtree_symlink_inode_operations = {
 	NULL,			/* no file-operations */
@@ -68,21 +68,23 @@
 static struct dentry *devtree_follow_link(struct dentry *dentry,
 					  struct dentry *base)
 {
+	struct inode *inode = dentry->d_inode;
 	struct proc_dir_entry * de;
 	char *link;
 
-	de = (struct proc_dir_entry *) dentry->inode->u.generic_ip;
+	de = (struct proc_dir_entry *) inode->u.generic_ip;
 	link = (char *) de->data;
 	return lookup_dentry(link, base, 1);
 }
 
 static int devtree_readlink(struct dentry *dentry, char *buffer, int buflen)
 {
+	struct inode *inode = dentry->d_inode;
 	struct proc_dir_entry * de;
 	char *link;
 	int linklen;
 
-	de = (struct proc_dir_entry *) dentry->inode->u.generic_ip;
+	de = (struct proc_dir_entry *) inode->u.generic_ip;
 	link = (char *) de->data;
 	linklen = strlen(link);
 	if (linklen > buflen)
@@ -206,7 +208,8 @@
 void proc_device_tree_init(void)
 {
 	struct device_node *root;
-
+	if ( !have_of )
+		return;
 	proc_device_tree = create_proc_entry("device-tree", S_IFDIR, 0);
 	if (proc_device_tree == 0)
 		return;
diff -u --recursive --new-file v2.1.96/linux/fs/proc/root.c linux/fs/proc/root.c
--- v2.1.96/linux/fs/proc/root.c	Fri Apr 10 13:03:49 1998
+++ linux/fs/proc/root.c	Tue Apr 14 17:44:23 1998
@@ -177,14 +177,14 @@
 
 #if defined(CONFIG_SUN_OPENPROMFS) || defined(CONFIG_SUN_OPENPROMFS_MODULE)
 
-static int (*proc_openprom_defreaddir_ptr)(struct inode *, struct file *, void *, filldir_t);
+static int (*proc_openprom_defreaddir_ptr)(struct file *, void *, filldir_t);
 static int (*proc_openprom_deflookup_ptr)(struct inode *, struct dentry *);
 void (*proc_openprom_use)(struct inode *, int) = 0;
 static struct openpromfs_dev *proc_openprom_devices = NULL;
 static ino_t proc_openpromdev_ino = PROC_OPENPROMD_FIRST;
 
 struct inode_operations *
-proc_openprom_register(int (*readdir)(struct inode *, struct file *, void *, filldir_t),
+proc_openprom_register(int (*readdir)(struct file *, void *, filldir_t),
 		       int (*lookup)(struct inode *, struct dentry *),
 		       void (*use)(struct inode *, int),
 		       struct openpromfs_dev ***devices)
@@ -236,14 +236,13 @@
 
 #if defined(CONFIG_SUN_OPENPROMFS_MODULE) && defined(CONFIG_KMOD)
 static int 
-proc_openprom_defreaddir(struct inode * inode, struct file * filp,
-			 void * dirent, filldir_t filldir)
+proc_openprom_defreaddir(struct file * filp, void * dirent, filldir_t filldir)
 {
 	request_module("openpromfs");
 	if ((proc_openprom_inode_operations.default_file_ops)->readdir !=
 	    proc_openprom_defreaddir)
 		return (proc_openprom_inode_operations.default_file_ops)->readdir 
-				(inode, filp, dirent, filldir);
+				(filp, dirent, filldir);
 	return -EINVAL;
 }
 #define OPENPROM_DEFREADDIR proc_openprom_defreaddir
diff -u --recursive --new-file v2.1.96/linux/include/asm-alpha/irq.h linux/include/asm-alpha/irq.h
--- v2.1.96/linux/include/asm-alpha/irq.h	Wed Apr  1 20:11:53 1998
+++ linux/include/asm-alpha/irq.h	Fri Apr 17 22:04:44 1998
@@ -52,6 +52,14 @@
 
 #endif
 
+static __inline__ int irq_cannonicalize(int irq)
+{
+	/*
+	 * XXX is this true for all Alpha's?  The old serial driver
+	 * did it this way for years without any complaints, so....
+	 */
+	return ((irq == 2) ? 9 : irq);
+}
 
 extern void disable_irq(unsigned int);
 extern void enable_irq(unsigned int);
diff -u --recursive --new-file v2.1.96/linux/include/asm-alpha/serial.h linux/include/asm-alpha/serial.h
--- v2.1.96/linux/include/asm-alpha/serial.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-alpha/serial.h	Fri Apr 17 22:04:44 1998
@@ -0,0 +1,73 @@
+/*
+ * include/asm-alpha/serial.h
+ */
+
+/*
+ * This assumes you have a 1.8432 MHz clock for your UART.
+ *
+ * It'd be nice if someone built a serial card with a 24.576 MHz
+ * clock, since the 16550A is capable of handling a top speed of 1.5
+ * megabits/second; but this requires the faster clock.
+ */
+#define BASE_BAUD ( 1843200 / 16 )
+
+/* Standard COM flags (except for COM4, because of the 8514 problem) */
+#ifdef CONFIG_SERIAL_DETECT_IRQ
+#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ)
+#define STD_COM4_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_AUTO_IRQ)
+#else
+#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST)
+#define STD_COM4_FLAGS ASYNC_BOOT_AUTOCONF
+#endif
+
+#ifdef CONFIG_SERIAL_MANY_PORTS
+#define FOURPORT_FLAGS ASYNC_FOURPORT
+#define ACCENT_FLAGS 0
+#define BOCA_FLAGS 0
+#endif
+	
+#define STD_SERIAL_PORT_DEFNS			\
+	/* UART CLK   PORT IRQ     FLAGS        */			\
+	{ 0, BASE_BAUD, 0x3F8, 4, STD_COM_FLAGS },	/* ttyS0 */	\
+	{ 0, BASE_BAUD, 0x2F8, 3, STD_COM_FLAGS },	/* ttyS1 */	\
+	{ 0, BASE_BAUD, 0x3E8, 4, STD_COM_FLAGS },	/* ttyS2 */	\
+	{ 0, BASE_BAUD, 0x2E8, 3, STD_COM4_FLAGS },	/* ttyS3 */
+
+
+#ifdef CONFIG_SERIAL_MANY_PORTS
+#define EXTRA_SERIAL_PORT_DEFNS			\	
+	{ 0, BASE_BAUD, 0x1A0, 9, FOURPORT_FLAGS }, 	/* ttyS4 */	\
+	{ 0, BASE_BAUD, 0x1A8, 9, FOURPORT_FLAGS },	/* ttyS5 */	\
+	{ 0, BASE_BAUD, 0x1B0, 9, FOURPORT_FLAGS },	/* ttyS6 */	\
+	{ 0, BASE_BAUD, 0x1B8, 9, FOURPORT_FLAGS },	/* ttyS7 */	\
+	{ 0, BASE_BAUD, 0x2A0, 5, FOURPORT_FLAGS },	/* ttyS8 */	\
+	{ 0, BASE_BAUD, 0x2A8, 5, FOURPORT_FLAGS },	/* ttyS9 */	\
+	{ 0, BASE_BAUD, 0x2B0, 5, FOURPORT_FLAGS },	/* ttyS10 */	\
+	{ 0, BASE_BAUD, 0x2B8, 5, FOURPORT_FLAGS },	/* ttyS11 */	\
+	{ 0, BASE_BAUD, 0x330, 4, ACCENT_FLAGS },	/* ttyS12 */	\
+	{ 0, BASE_BAUD, 0x338, 4, ACCENT_FLAGS },	/* ttyS13 */	\
+	{ 0, BASE_BAUD, 0x000, 0, 0 },	/* ttyS14 (spare) */		\
+	{ 0, BASE_BAUD, 0x000, 0, 0 },	/* ttyS15 (spare) */		\
+	{ 0, BASE_BAUD, 0x100, 12, BOCA_FLAGS },	/* ttyS16 */	\
+	{ 0, BASE_BAUD, 0x108, 12, BOCA_FLAGS },	/* ttyS17 */	\
+	{ 0, BASE_BAUD, 0x110, 12, BOCA_FLAGS },	/* ttyS18 */	\
+	{ 0, BASE_BAUD, 0x118, 12, BOCA_FLAGS },	/* ttyS19 */	\
+	{ 0, BASE_BAUD, 0x120, 12, BOCA_FLAGS },	/* ttyS20 */	\
+	{ 0, BASE_BAUD, 0x128, 12, BOCA_FLAGS },	/* ttyS21 */	\
+	{ 0, BASE_BAUD, 0x130, 12, BOCA_FLAGS },	/* ttyS22 */	\
+	{ 0, BASE_BAUD, 0x138, 12, BOCA_FLAGS },	/* ttyS23 */	\
+	{ 0, BASE_BAUD, 0x140, 12, BOCA_FLAGS },	/* ttyS24 */	\
+	{ 0, BASE_BAUD, 0x148, 12, BOCA_FLAGS },	/* ttyS25 */	\
+	{ 0, BASE_BAUD, 0x150, 12, BOCA_FLAGS },	/* ttyS26 */	\
+	{ 0, BASE_BAUD, 0x158, 12, BOCA_FLAGS },	/* ttyS27 */	\
+	{ 0, BASE_BAUD, 0x160, 12, BOCA_FLAGS },	/* ttyS28 */	\
+	{ 0, BASE_BAUD, 0x168, 12, BOCA_FLAGS },	/* ttyS29 */	\
+	{ 0, BASE_BAUD, 0x170, 12, BOCA_FLAGS },	/* ttyS30 */	\
+	{ 0, BASE_BAUD, 0x178, 12, BOCA_FLAGS },	/* ttyS31 */
+#else
+#define EXTRA_SERIAL_PORT_DEFNS
+#endif
+
+#define SERIAL_PORT_DFNS		\
+	STD_SERIAL_PORT_DEFNS		\
+	EXTRA_SERIAL_PORT_DEFNS
diff -u --recursive --new-file v2.1.96/linux/include/asm-i386/irq.h linux/include/asm-i386/irq.h
--- v2.1.96/linux/include/asm-i386/irq.h	Tue Jan 20 12:56:17 1998
+++ linux/include/asm-i386/irq.h	Fri Apr 17 22:04:44 1998
@@ -18,6 +18,11 @@
 
 #define TIMER_IRQ 0
 
+static __inline__ int irq_cannonicalize(int irq)
+{
+	return ((irq == 2) ? 9 : irq);
+}
+
 extern void disable_irq(unsigned int);
 extern void enable_irq(unsigned int);
 
diff -u --recursive --new-file v2.1.96/linux/include/asm-i386/serial.h linux/include/asm-i386/serial.h
--- v2.1.96/linux/include/asm-i386/serial.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-i386/serial.h	Fri Apr 17 22:04:44 1998
@@ -0,0 +1,126 @@
+/*
+ * include/asm-i386/serial.h
+ */
+
+/*
+ * This assumes you have a 1.8432 MHz clock for your UART.
+ *
+ * It'd be nice if someone built a serial card with a 24.576 MHz
+ * clock, since the 16550A is capable of handling a top speed of 1.5
+ * megabits/second; but this requires the faster clock.
+ */
+#define BASE_BAUD ( 1843200 / 16 )
+
+/* Standard COM flags (except for COM4, because of the 8514 problem) */
+#ifdef CONFIG_SERIAL_DETECT_IRQ
+#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ)
+#define STD_COM4_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_AUTO_IRQ)
+#else
+#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST)
+#define STD_COM4_FLAGS ASYNC_BOOT_AUTOCONF
+#endif
+
+#ifdef CONFIG_SERIAL_MANY_PORTS
+#define FOURPORT_FLAGS ASYNC_FOURPORT
+#define ACCENT_FLAGS 0
+#define BOCA_FLAGS 0
+#define HUB6_FLAGS 0
+#endif
+	
+/*
+ * The following define the access methods for the HUB6 card. All
+ * access is through two ports for all 24 possible chips. The card is
+ * selected through the high 2 bits, the port on that card with the
+ * "middle" 3 bits, and the register on that port with the bottom
+ * 3 bits.
+ *
+ * While the access port and interrupt is configurable, the default
+ * port locations are 0x302 for the port control register, and 0x303
+ * for the data read/write register. Normally, the interrupt is at irq3
+ * but can be anything from 3 to 7 inclusive. Note that using 3 will
+ * require disabling com2.
+ */
+
+#define C_P(card,port) (((card)<<6|(port)<<3) + 1)
+
+#define STD_SERIAL_PORT_DEFNS			\
+	/* UART CLK   PORT IRQ     FLAGS        */			\
+	{ 0, BASE_BAUD, 0x3F8, 4, STD_COM_FLAGS },	/* ttyS0 */	\
+	{ 0, BASE_BAUD, 0x2F8, 3, STD_COM_FLAGS },	/* ttyS1 */	\
+	{ 0, BASE_BAUD, 0x3E8, 4, STD_COM_FLAGS },	/* ttyS2 */	\
+	{ 0, BASE_BAUD, 0x2E8, 3, STD_COM4_FLAGS },	/* ttyS3 */
+
+
+#ifdef CONFIG_SERIAL_MANY_PORTS
+#define EXTRA_SERIAL_PORT_DEFNS			\
+	{ 0, BASE_BAUD, 0x1A0, 9, FOURPORT_FLAGS }, 	/* ttyS4 */	\
+	{ 0, BASE_BAUD, 0x1A8, 9, FOURPORT_FLAGS },	/* ttyS5 */	\
+	{ 0, BASE_BAUD, 0x1B0, 9, FOURPORT_FLAGS },	/* ttyS6 */	\
+	{ 0, BASE_BAUD, 0x1B8, 9, FOURPORT_FLAGS },	/* ttyS7 */	\
+	{ 0, BASE_BAUD, 0x2A0, 5, FOURPORT_FLAGS },	/* ttyS8 */	\
+	{ 0, BASE_BAUD, 0x2A8, 5, FOURPORT_FLAGS },	/* ttyS9 */	\
+	{ 0, BASE_BAUD, 0x2B0, 5, FOURPORT_FLAGS },	/* ttyS10 */	\
+	{ 0, BASE_BAUD, 0x2B8, 5, FOURPORT_FLAGS },	/* ttyS11 */	\
+	{ 0, BASE_BAUD, 0x330, 4, ACCENT_FLAGS },	/* ttyS12 */	\
+	{ 0, BASE_BAUD, 0x338, 4, ACCENT_FLAGS },	/* ttyS13 */	\
+	{ 0, BASE_BAUD, 0x000, 0, 0 },	/* ttyS14 (spare) */		\
+	{ 0, BASE_BAUD, 0x000, 0, 0 },	/* ttyS15 (spare) */		\
+	{ 0, BASE_BAUD, 0x100, 12, BOCA_FLAGS },	/* ttyS16 */	\
+	{ 0, BASE_BAUD, 0x108, 12, BOCA_FLAGS },	/* ttyS17 */	\
+	{ 0, BASE_BAUD, 0x110, 12, BOCA_FLAGS },	/* ttyS18 */	\
+	{ 0, BASE_BAUD, 0x118, 12, BOCA_FLAGS },	/* ttyS19 */	\
+	{ 0, BASE_BAUD, 0x120, 12, BOCA_FLAGS },	/* ttyS20 */	\
+	{ 0, BASE_BAUD, 0x128, 12, BOCA_FLAGS },	/* ttyS21 */	\
+	{ 0, BASE_BAUD, 0x130, 12, BOCA_FLAGS },	/* ttyS22 */	\
+	{ 0, BASE_BAUD, 0x138, 12, BOCA_FLAGS },	/* ttyS23 */	\
+	{ 0, BASE_BAUD, 0x140, 12, BOCA_FLAGS },	/* ttyS24 */	\
+	{ 0, BASE_BAUD, 0x148, 12, BOCA_FLAGS },	/* ttyS25 */	\
+	{ 0, BASE_BAUD, 0x150, 12, BOCA_FLAGS },	/* ttyS26 */	\
+	{ 0, BASE_BAUD, 0x158, 12, BOCA_FLAGS },	/* ttyS27 */	\
+	{ 0, BASE_BAUD, 0x160, 12, BOCA_FLAGS },	/* ttyS28 */	\
+	{ 0, BASE_BAUD, 0x168, 12, BOCA_FLAGS },	/* ttyS29 */	\
+	{ 0, BASE_BAUD, 0x170, 12, BOCA_FLAGS },	/* ttyS30 */	\
+	{ 0, BASE_BAUD, 0x178, 12, BOCA_FLAGS },	/* ttyS31 */
+#else
+#define EXTRA_SERIAL_PORT_DEFNS
+#endif
+
+/* You can have up to four HUB6's in the system, but I've only
+ * included two cards here for a total of twelve ports.
+ */
+#if (defined(CONFIG_HUB6) && defined(CONFIG_SERIAL_MANY_PORTS))
+#define HUB6_SERIAL_PORT_DFNS		\
+	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,0) },  /* ttyS32 */	\
+	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,1) },  /* ttyS33 */	\
+	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,2) },  /* ttyS34 */	\
+	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,3) },  /* ttyS35 */	\
+	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,4) },  /* ttyS36 */	\
+	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,5) },  /* ttyS37 */	\
+	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,0) },  /* ttyS38 */	\
+	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,1) },  /* ttyS39 */	\
+	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,2) },  /* ttyS40 */	\
+	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,3) },  /* ttyS41 */	\
+	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,4) },  /* ttyS42 */	\
+	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,5) },  /* ttyS43 */
+#else
+#define HUB6_SERIAL_PORT_DFNS
+#endif
+
+#ifdef CONFIG_MCA
+#define MCA_SERIAL_PORT_DFNS			\
+	{ 0, BASE_BAUD, 0x3220, 3, STD_COM_FLAGS },	\
+	{ 0, BASE_BAUD, 0x3228, 3, STD_COM_FLAGS },	\
+	{ 0, BASE_BAUD, 0x4220, 3, STD_COM_FLAGS },	\
+	{ 0, BASE_BAUD, 0x4228, 3, STD_COM_FLAGS },	\
+	{ 0, BASE_BAUD, 0x5220, 3, STD_COM_FLAGS },	\
+	{ 0, BASE_BAUD, 0x5228, 3, STD_COM_FLAGS },
+#else
+#define MCA_SERIAL_PORT_DFNS
+#endif
+
+#define SERIAL_PORT_DFNS		\
+	STD_SERIAL_PORT_DEFNS		\
+	EXTRA_SERIAL_PORT_DEFNS		\
+	HUB6_SERIAL_PORT_DFNS		\
+	MCA_SERIAL_PORT_DFNS
+
diff -u --recursive --new-file v2.1.96/linux/include/asm-i386/system.h linux/include/asm-i386/system.h
--- v2.1.96/linux/include/asm-i386/system.h	Tue Apr 14 14:29:25 1998
+++ linux/include/asm-i386/system.h	Wed Apr 15 12:33:44 1998
@@ -293,7 +293,7 @@
 	"movb $0,6(%2)\n\t" \
 	"movb %%ah,7(%2)\n\t" \
 	"rorl $16,%%eax" \
-	: "=m"(*(n)) : "a" (addr), "r"(n), "i"(limit), "i"(type))
+	: "=m"(*(n)) : "a" (addr), "r"(n), "g"(limit), "i"(type))
 
 #define set_tss_desc(n,addr) \
 	_set_tssldt_desc(((char *) (n)),((int)(addr)),235,0x89)
diff -u --recursive --new-file v2.1.96/linux/include/asm-m68k/irq.h linux/include/asm-m68k/irq.h
--- v2.1.96/linux/include/asm-m68k/irq.h	Wed Apr 23 19:01:27 1997
+++ linux/include/asm-m68k/irq.h	Fri Apr 17 22:04:44 1998
@@ -45,6 +45,11 @@
 
 #define IRQ_SCHED_TIMER	(8)    /* interrupt source for scheduling timer */
 
+static __inline__ int irq_cannonicalize(int irq)
+{
+	return irq;
+}
+
 /*
  * Machine specific interrupt sources.
  *
diff -u --recursive --new-file v2.1.96/linux/include/asm-ppc/8xx_immap.h linux/include/asm-ppc/8xx_immap.h
--- v2.1.96/linux/include/asm-ppc/8xx_immap.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-ppc/8xx_immap.h	Tue Apr 14 17:34:00 1998
@@ -0,0 +1,414 @@
+
+/*
+ * MPC8xx Internal Memory Map
+ * Copyright (c) 1997 Dan Malek (dmalek@jlc.net)
+ *
+ * The I/O on the MPC860 is comprised of blocks of special registers
+ * and the dual port ram for the Communication Processor Module.
+ * Within this space are functional units such as the SIU, memory
+ * controller, system timers, and other control functions.  It is
+ * a combination that I found difficult to separate into logical
+ * functional files.....but anyone else is welcome to try.  -- Dan
+ */
+#ifndef __IMMAP_8XX__
+#define __IMMAP_8XX__
+
+/* System configuration registers.
+*/
+typedef	struct sys_conf {
+	uint	sc_siumcr;
+	uint	sc_sypcr;
+	uint	sc_swt;
+	char	res1[2];
+	ushort	sc_swsr;
+	uint	sc_sipend;
+	uint	sc_simask;
+	uint	sc_siel;
+	uint	sc_sivec;
+	uint	sc_tesr;
+	char	res2[0xc];
+	uint	sc_sdcr;
+	char	res3[0x4c];
+} sysconf8xx_t;
+
+/* PCMCIA configuration registers.
+*/
+typedef struct pcmcia_conf {
+	uint	pcmc_pbr0;
+	uint	pcmc_por0;
+	uint	pcmc_pbr1;
+	uint	pcmc_por1;
+	uint	pcmc_pbr2;
+	uint	pcmc_por2;
+	uint	pcmc_pbr3;
+	uint	pcmc_por3;
+	uint	pcmc_pbr4;
+	uint	pcmc_por4;
+	uint	pcmc_pbr5;
+	uint	pcmc_por5;
+	uint	pcmc_pbr6;
+	uint	pcmc_por6;
+	uint	pcmc_pbr7;
+	uint	pcmc_por7;
+	char	res1[0x20];
+	uint	pcmc_pgcra;
+	uint	pcmc_pgcrb;
+	uint	pcmc_pscr;
+	char	res2[4];
+	uint	pcmc_pipr;
+	char	res3[4];
+	uint	pcmc_per;
+	char	res4[4];
+} pcmconf8xx_t;
+
+/* Memory controller registers.
+*/
+typedef struct	mem_ctlr {
+	uint	memc_br0;
+	uint	memc_or0;
+	uint	memc_br1;
+	uint	memc_or1;
+	uint	memc_br2;
+	uint	memc_or2;
+	uint	memc_br3;
+	uint	memc_or3;
+	uint	memc_br4;
+	uint	memc_or4;
+	uint	memc_br5;
+	uint	memc_or5;
+	uint	memc_br6;
+	uint	memc_or6;
+	uint	memc_br7;
+	uint	memc_or7;
+	char	res1[0x24];
+	uint	memc_mar;
+	uint	memc_mcr;
+	char	res2[4];
+	uint	memc_mamr;
+	uint	memc_mbmr;
+	ushort	memc_mstat;
+	ushort	memc_mptpr;
+	uint	memc_mdr;
+	char	res3[0x80];
+} memctl8xx_t;
+
+/* System Integration Timers.
+*/
+typedef struct	sys_int_timers {
+	ushort	sit_tbscr;
+	uint	sit_tbreff0;
+	uint	sit_tbreff1;
+	char	res1[0x14];
+	ushort	sit_rtcsc;
+	uint	sit_rtc;
+	uint	sit_rtsec;
+	uint	sit_rtcal;
+	char	res2[0x10];
+	ushort	sit_piscr;
+	char	res3[2];
+	uint	sit_pitc;
+	uint	sit_pitr;
+	char	res4[0x34];
+} sit8xx_t;
+
+#define TBSCR_TBIRQ_MASK	((ushort)0xff00)
+#define TBSCR_REFA		((ushort)0x0080)
+#define TBSCR_REFB		((ushort)0x0040)
+#define TBSCR_REFAE		((ushort)0x0008)
+#define TBSCR_REFBE		((ushort)0x0004)
+#define TBSCR_TBF		((ushort)0x0002)
+#define TBSCR_TBE		((ushort)0x0001)
+
+#define RTCSC_RTCIRQ_MASK	((ushort)0xff00)
+#define RTCSC_SEC		((ushort)0x0080)
+#define RTCSC_ALR		((ushort)0x0040)
+#define RTCSC_38K		((ushort)0x0010)
+#define RTCSC_SIE		((ushort)0x0008)
+#define RTCSC_ALE		((ushort)0x0004)
+#define RTCSC_RTF		((ushort)0x0002)
+#define RTCSC_RTE		((ushort)0x0001)
+
+#define PISCR_PIRQ_MASK		((ushort)0xff00)
+#define PISCR_PS		((ushort)0x0080)
+#define PISCR_PIE		((ushort)0x0004)
+#define PISCR_PTF		((ushort)0x0002)
+#define PISCR_PTE		((ushort)0x0001)
+
+/* Clocks and Reset.
+*/
+typedef struct clk_and_reset {
+	uint	car_sccr;
+	uint	car_plprcr;
+	uint	car_rsr;
+	char	res[0x74];        /* Reserved area                  */
+} car8xx_t;
+
+/* System Integration Timers keys.
+*/
+typedef struct sitk {
+	uint	sitk_tbscrk;
+	uint	sitk_tbreff0k;
+	uint	sitk_tbreff1k;
+	uint	sitk_tbk;
+	char	res1[0x10];
+	uint	sitk_rtcsck;
+	uint	sitk_rtck;
+	uint	sitk_rtseck;
+	uint	sitk_rtcalk;
+	char	res2[0x10];
+	uint	sitk_piscrk;
+	uint	sitk_pitck;
+	char	res3[0x38];
+} sitk8xx_t;
+
+/* Clocks and reset keys.
+*/
+typedef struct cark {
+	uint	cark_sccrk;
+	uint	cark_plprcrk;
+	uint	cark_rsrk;
+	char	res[0x474];
+} cark8xx_t;
+
+/* The key to unlock registers maintained by keep-alive power.
+*/
+#define KAPWR_KEY	((unsigned int)0x55ccaa33)
+
+/* LCD interface.  MPC821 Only.
+*/
+typedef struct lcd {
+	ushort	lcd_lcolr[16];
+	char	res[0x20];
+	uint	lcd_lccr;
+	uint	lcd_lchcr;
+	uint	lcd_lcvcr;
+	char	res2[4];
+	uint	lcd_lcfaa;
+	uint	lcd_lcfba;
+	char	lcd_lcsr;
+	char	res3[0x7];
+} lcd8xx_t;
+
+/* I2C
+*/
+typedef struct i2c {
+	u_char	i2c_i2mod;
+	char	res1[3];
+	u_char	i2c_i2add;
+	char	res2[3];
+	u_char	i2c_i2brg;
+	char	res3[3];
+	u_char	i2c_i2com;
+	char	res4[3];
+	u_char	i2c_i2cer;
+	char	res5[3];
+	u_char	i2c_i2cmr;
+	char	res6[0x8b];
+} i2c8xx_t;
+
+/* DMA control/status registers.
+*/
+typedef struct sdma_csr {
+	char	res1[4];
+	uint	sdma_sdar;
+	u_char	sdma_sdsr;
+	char	res3[3];
+	u_char	sdma_sdmr;
+	char	res4[3];
+	u_char	sdma_idsr1;
+	char	res5[3];
+	u_char	sdma_idmr1;
+	char	res6[3];
+	u_char	sdma_idsr2;
+	char	res7[3];
+	u_char	sdma_idmr2;
+	char	res8[0x13];
+} sdma8xx_t;
+
+/* Communication Processor Module Interrupt Controller.
+*/
+typedef struct cpm_ic {
+	ushort	cpic_civr;
+	char	res[0xe];
+	uint	cpic_cicr;
+	uint	cpic_cipr;
+	uint	cpic_cimr;
+	uint	cpic_cisr;
+} cpic8xx_t;
+
+/* Input/Output Port control/status registers.
+*/
+typedef struct io_port {
+	ushort	iop_padir;
+	ushort	iop_papar;
+	ushort	iop_paodr;
+	ushort	iop_padat;
+	char	res1[8];
+	ushort	iop_pcdir;
+	ushort	iop_pcpar;
+	ushort	iop_pcso;
+	ushort	iop_pcdat;
+	ushort	iop_pcint;
+	char	res2[6];
+	ushort	iop_pddir;
+	ushort	iop_pdpar;
+	char	res3[2];
+	ushort	iop_pddat;
+	char	res4[8];
+} iop8xx_t;
+
+/* Communication Processor Module Timers
+*/
+typedef struct cpm_timers {
+	ushort	cpmt_tgcr;
+	char	res1[0xe];
+	ushort	cpmt_tmr1;
+	ushort	cpmt_tmr2;
+	ushort	cpmt_trr1;
+	ushort	cpmt_trr2;
+	ushort	cpmt_tcr1;
+	ushort	cpmt_tcr2;
+	ushort	cpmt_tcn1;
+	ushort	cpmt_tcn2;
+	ushort	cpmt_tmr3;
+	ushort	cpmt_tmr4;
+	ushort	cpmt_trr3;
+	ushort	cpmt_trr4;
+	ushort	cpmt_tcr3;
+	ushort	cpmt_tcr4;
+	ushort	cpmt_tcn3;
+	ushort	cpmt_tcn4;
+	ushort	cpmt_ter1;
+	ushort	cpmt_ter2;
+	ushort	cpmt_ter3;
+	ushort	cpmt_ter4;
+	char	res2[8];
+} cpmtimer8xx_t;
+
+/* Finally, the Communication Processor stuff.....
+*/
+typedef struct scc {		/* Serial communication channels */
+	uint	scc_gsmrl;
+	uint	scc_gsmrh;
+	ushort	scc_pmsr;
+	char	res1[2];
+	ushort	scc_todr;
+	ushort	scc_dsr;
+	ushort	scc_scce;
+	char	res2[2];
+	ushort	scc_sccm;
+	char	res3;
+	u_char	scc_sccs;
+	char	res4[8];
+} scc_t;
+
+typedef struct smc {		/* Serial management channels */
+	char	res1[2];
+	ushort	smc_smcmr;
+	char	res2[2];
+	u_char	smc_smce;
+	char	res3[3];
+	u_char	smc_smcm;
+	char	res4[5];
+} smc_t;
+
+typedef struct comm_proc {
+	/* General control and status registers.
+	*/
+	ushort	cp_cpcr;
+	char	res1[2];
+	ushort	cp_rccr;
+	char	res2[6];
+	ushort	cp_cpmcr1;
+	ushort	cp_cpmcr2;
+	ushort	cp_cpmcr3;
+	ushort	cp_cpmcr4;
+	char	res3[2];
+	ushort	cp_rter;
+	char	res4[2];
+	ushort	cp_rtmr;
+	char	res5[0x14];
+
+	/* Baud rate generators.
+	*/
+	uint	cp_brgc1;
+	uint	cp_brgc2;
+	uint	cp_brgc3;
+	uint	cp_brgc4;
+
+	/* Serial Communication Channels.
+	*/
+	scc_t	cp_scc[4];
+
+	/* Serial Management Channels.
+	*/
+	smc_t	cp_smc[2];
+
+	/* Serial Peripheral Interface.
+	*/
+	ushort	cp_spmode;
+	char	res6[4];
+	u_char	cp_spie;
+	char	res7[3];
+	u_char	cp_spim;
+	char	res8[2];
+	u_char	cp_spcom;
+	char	res9[2];
+
+	/* Parallel Interface Port.
+	*/
+	char	res10[2];
+	ushort	cp_pipc;
+	char	res11[2];
+	ushort	cp_ptpr;
+	uint	cp_pbdir;
+	uint	cp_pbpar;
+	char	res12[2];
+	ushort	cp_pbodr;
+	uint	cp_pbdat;
+	char	res13[0x18];
+
+	/* Serial Interface and Time Slot Assignment.
+	*/
+	uint	cp_simode;
+	u_char	cp_sigmr;
+	char	res14;
+	u_char	cp_sistr;
+	u_char	cp_sicmr;
+	char	res15[4];
+	uint	cp_sicr;
+	uint	cp_sirp;
+	char	res16[0x10c];
+	u_char	cp_siram[0x200];
+	char	res17[0x200];
+	char	res18[0x1000];
+
+	/* Dual Ported RAM follows.
+	 * There are many different formats for this memory area
+	 * depending upon the devices used and options chosen.
+	 */
+	u_char	cp_dpmem[0x1000];	/* BD / Data / ucode */
+	u_char	res19[0xc00];
+	u_char	cp_dparam[0x400];	/* Parameter RAM */
+} cpm8xx_t;
+
+/* Internal memory map.
+*/
+typedef struct immap {
+	sysconf8xx_t	im_siu_conf;	/* SIU Configuration */
+	pcmconf8xx_t	im_pcmcia;	/* PCMCIA Configuration */
+	memctl8xx_t	im_memctl;	/* Memory Controller */
+	sit8xx_t	im_sit;		/* System integration timers */
+	car8xx_t	im_clkrst;	/* Clocks and reset */
+	sitk8xx_t	im_sitk;	/* Sys int timer keys */
+	cark8xx_t	im_clkrstk;	/* Clocks and reset keys */
+	lcd8xx_t	im_lcd;		/* LCD (821 only) */
+	i2c8xx_t	im_i2c;		/* I2C control/status */
+	sdma8xx_t	im_sdma;	/* SDMA control/status */
+	cpic8xx_t	im_cpic;	/* CPM Interrupt Controller */
+	iop8xx_t	im_ioport;	/* IO Port control/status */
+	cpmtimer8xx_t	im_cpmtimer;	/* CPM timers */
+	cpm8xx_t	im_cpm;		/* Communication processor */
+} immap_t;
+
+#endif /* __IMMAP_8XX__ */
diff -u --recursive --new-file v2.1.96/linux/include/asm-ppc/adb.h linux/include/asm-ppc/adb.h
--- v2.1.96/linux/include/asm-ppc/adb.h	Mon Jan 12 15:18:13 1998
+++ linux/include/asm-ppc/adb.h	Tue Apr 14 17:34:00 1998
@@ -18,13 +18,15 @@
 #define ADB_MODEM	5
 #define ADB_MISC	7	/* maybe a monitor */
 
+#define ADB_RET_OK	0
+#define ADB_RET_TIMEOUT	3
 
 #ifdef __KERNEL__
 
 struct adb_request {
-    unsigned char data[16];
+    unsigned char data[32];
     int nbytes;
-    unsigned char reply[16];
+    unsigned char reply[32];
     int reply_len;
     unsigned char reply_expected;
     unsigned char sent;
@@ -34,6 +36,11 @@
     struct adb_request *next;
 };
 
+struct adb_ids {
+    int nids;
+    unsigned char id[16];
+};
+
 extern enum adb_hw {
 	ADB_NONE, ADB_VIACUDA, ADB_VIAPMU, ADB_MACIO
 } adb_hardware;
@@ -48,7 +55,7 @@
 void adb_init(void);
 int adb_request(struct adb_request *req, void (*done)(struct adb_request *),
 		int flags, int nbytes, ...);
-int adb_register(int default_id,
+int adb_register(int default_id,int handler_id,struct adb_ids *ids,
 		 void (*handler)(unsigned char *, int, struct pt_regs *, int));
 void adb_input(unsigned char *, int, struct pt_regs *, int);
 
diff -u --recursive --new-file v2.1.96/linux/include/asm-ppc/cache.h linux/include/asm-ppc/cache.h
--- v2.1.96/linux/include/asm-ppc/cache.h	Mon Jan 12 15:18:13 1998
+++ linux/include/asm-ppc/cache.h	Tue Apr 14 17:34:00 1998
@@ -4,9 +4,84 @@
 #ifndef __ARCH_PPC_CACHE_H
 #define __ARCH_PPC_CACHE_H
 
+#include <linux/config.h>
+#include <asm/processor.h>
+/*#include <asm/system.h>*/
+
 /* bytes per L1 cache line */
 #define        L1_CACHE_BYTES  32      
 #define        L1_CACHE_ALIGN(x)       (((x)+(L1_CACHE_BYTES-1))&~(L1_CACHE_BYTES-1))
 #define L1_CACHE_PAGES		8
+
+#ifndef __ASSEMBLY__
+static inline unsigned long unlock_dcache(void)
+{
+#ifndef CONFIG_8xx	
+	ulong hid0 = 0;
+	/* 601 doesn't do this */
+	if ( (ulong) _get_PVR() == 1 )
+		return 0;
+	asm("mfspr %0,1008 \n\t" : "=r" (hid0) );
+	if ( !(hid0 & HID0_DLOCK) )
+		return 0;
+	asm("mtspr 1008,%0 \n\t" :: "r" (hid0 & ~(HID0_DLOCK)));
+	return (hid0 & HID0_DLOCK) ? 1 : 0;
+#else /* ndef CONFIG_8xx */
+	return 0;
+#endif
+}
+
+static inline void lock_dcache(unsigned long lockit)
+{
+#ifndef CONFIG_8xx
+	/* 601 doesn't do this */
+	if ( !lockit || ((ulong) _get_PVR() == 1) )
+		return;
+	asm("mfspr	%0,1008 \n\t"
+	    "ori	%0,%0,%2 \n\t"
+	    "mtspr	1008,%0 \n\t"
+	    "sync \n\t isync \n\t"
+	    : "=r" (lockit) : "0" (lockit), "i" (HID0_DLOCK));
+#endif /* ndef CONFIG_8xx */
+}
+
+#endif /* __ASSEMBLY__ */
+	  
+#ifdef CONFIG_8xx
+/* Cache control on the MPC8xx is provided through some additional
+ * special purpose registers.
+ */
+#define IC_CST		560	/* Instruction cache control/status */
+#define IC_ADR		561	/* Address needed for some commands */
+#define IC_DAT		562	/* Read-only data register */
+#define DC_CST		568	/* Data cache control/status */
+#define DC_ADR		569	/* Address needed for some commands */
+#define DC_DAT		570	/* Read-only data register */
+
+/* Commands.  Only the first few are available to the instruction cache.
+*/
+#define	IDC_ENABLE	0x02000000	/* Cache enable */
+#define IDC_DISABLE	0x04000000	/* Cache disable */
+#define IDC_LDLCK	0x06000000	/* Load and lock */
+#define IDC_UNLINE	0x08000000	/* Unlock line */
+#define IDC_UNALL	0x0a000000	/* Unlock all */
+#define IDC_INVALL	0x0c000000	/* Invalidate all */
+
+#define DC_FLINE	0x0e000000	/* Flush data cache line */
+#define DC_SFWT		0x01000000	/* Set forced writethrough mode */
+#define DC_CFWT		0x03000000	/* Clear forced writethrough mode */
+#define DC_SLES		0x05000000	/* Set little endian swap mode */
+#define DC_CLES		0x07000000	/* Clear little endian swap mode */
+
+/* Status.
+*/
+#define IDC_ENABLED	0x80000000	/* Cache is enabled */
+#define IDC_CERR1	0x00200000	/* Cache error 1 */
+#define IDC_CERR2	0x00100000	/* Cache error 2 */
+#define IDC_CERR3	0x00080000	/* Cache error 3 */
+
+#define DC_DFWT		0x40000000	/* Data cache is forced write through */
+#define DC_LES		0x20000000	/* Caches are little endian mode */
+#endif /* CONFIG_8xx */
 
 #endif
diff -u --recursive --new-file v2.1.96/linux/include/asm-ppc/cuda.h linux/include/asm-ppc/cuda.h
--- v2.1.96/linux/include/asm-ppc/cuda.h	Mon Jan 12 15:18:13 1998
+++ linux/include/asm-ppc/cuda.h	Tue Apr 14 17:34:00 1998
@@ -12,6 +12,7 @@
 #define TIMER_PACKET	3
 #define POWER_PACKET	4
 #define MACIIC_PACKET	5
+#define PMU_PACKET	6
 
 /* CUDA commands (2nd byte) */
 #define CUDA_WARM_START		0
diff -u --recursive --new-file v2.1.96/linux/include/asm-ppc/dma.h linux/include/asm-ppc/dma.h
--- v2.1.96/linux/include/asm-ppc/dma.h	Tue Feb 17 13:12:49 1998
+++ linux/include/asm-ppc/dma.h	Tue Apr 14 17:34:00 1998
@@ -53,6 +53,11 @@
 #define ISA_DMA_THRESHOLD ~0L
 #endif /* CONFIG_PMAC */
 
+#ifdef CONFIG_APUS
+/* This is bogus and should go away. */
+#define ISA_DMA_THRESHOLD (0x00ffffff)
+#endif
+
 #else
 /* in arch/ppc/kernel/setup.c -- Cort */
 extern unsigned long DMA_MODE_WRITE, DMA_MODE_READ;
diff -u --recursive --new-file v2.1.96/linux/include/asm-ppc/elf.h linux/include/asm-ppc/elf.h
--- v2.1.96/linux/include/asm-ppc/elf.h	Thu Feb 12 20:56:12 1998
+++ linux/include/asm-ppc/elf.h	Tue Apr 14 17:34:00 1998
@@ -59,22 +59,6 @@
 #define ELF_PLATFORM	(NULL)
 
 #ifdef __KERNEL__
-#define SET_PERSONALITY(ibcs2) current->personality = (ibcs2 ? PER_SVR4 : PER_LINUX)
-#endif
-
-
-/* This yields a mask that user programs can use to figure out what
-   instruction set this cpu supports.  */
-
-#define ELF_HWCAP	(0)
-
-/* This yields a string that ld.so will use to load implementation
-   specific libraries for optimization.  This is more specific in
-   intent than poking at uname or /proc/cpuinfo.  */
-
-#define ELF_PLATFORM	(NULL)
-
-#ifdef __KERNEL__
 #define SET_PERSONALITY(ex, ibcs2) \
 	current->personality = (ibcs2 ? PER_SVR4 : PER_LINUX)
 #endif
diff -u --recursive --new-file v2.1.96/linux/include/asm-ppc/ide.h linux/include/asm-ppc/ide.h
--- v2.1.96/linux/include/asm-ppc/ide.h	Mon Jan 12 15:18:13 1998
+++ linux/include/asm-ppc/ide.h	Tue Apr 14 17:34:00 1998
@@ -28,17 +28,35 @@
 
 #define ide_sti()	sti()
 
-typedef unsigned long ide_ioreg_t;
+typedef unsigned int ide_ioreg_t;
 void ide_init_hwif_ports(ide_ioreg_t *p, ide_ioreg_t base, int *irq);
 void prep_ide_init_hwif_ports(ide_ioreg_t *p, ide_ioreg_t base, int *irq);
+void mbx_ide_init_hwif_ports(ide_ioreg_t *p, ide_ioreg_t base, int *irq);
 void pmac_ide_init_hwif_ports(ide_ioreg_t *p, ide_ioreg_t base, int *irq);
+void chrp_ide_init_hwif_ports(ide_ioreg_t *p, ide_ioreg_t base, int *irq);
 void ide_insw(ide_ioreg_t port, void *buf, int ns);
 void ide_outsw(ide_ioreg_t port, void *buf, int ns);
 
+extern int pmac_ide_ports_known;
+extern ide_ioreg_t pmac_ide_regbase[MAX_HWIFS];
+extern int pmac_ide_irq[MAX_HWIFS];
+extern void pmac_ide_probe(void);
+
+extern int chrp_ide_ports_known;
+extern ide_ioreg_t chrp_ide_regbase[MAX_HWIFS];
+extern ide_ioreg_t chrp_idedma_regbase; /* one for both channels */
+extern unsigned int chrp_ide_irq;
+extern void chrp_ide_probe(void);
+
 static __inline__ int ide_default_irq(ide_ioreg_t base)
 {
-	if ( _machine == _MACH_Pmac )
+	if ( (_machine == _MACH_Pmac) || (_machine == _MACH_mbx) )
 		return 0;
+        else if ( _machine == _MACH_chrp) {
+                if (chrp_ide_ports_known == 0) 
+			chrp_ide_probe();
+                return chrp_ide_irq;
+        }
 	switch (base) {
 		case 0x1f0: return 13;
 		case 0x170: return 13;
@@ -51,8 +69,17 @@
 
 static __inline__ ide_ioreg_t ide_default_io_base(int index)
 {
-	if ( _machine == _MACH_Pmac )
-		return index;
+        if (_machine == _MACH_Pmac) {
+		if (!pmac_ide_ports_known)
+			pmac_ide_probe();
+		return pmac_ide_regbase[index];
+	}
+	if (_machine == _MACH_mbx) return index;
+        if ( _machine == _MACH_chrp ) {
+                if (chrp_ide_ports_known == 0)
+                        chrp_ide_probe();
+                return chrp_ide_regbase[index];
+        }
 	switch (index) {
 		case 0:	return 0x1f0;
 		case 1:	return 0x170;
@@ -65,21 +92,21 @@
 
 static __inline__ int ide_check_region (ide_ioreg_t from, unsigned int extent)
 {
-	if ( _machine == _MACH_Pmac )
+	if ( (_machine == _MACH_Pmac) || (_machine == _MACH_mbx))
 		return 0;
 	return check_region(from, extent);
 }
 
 static __inline__ void ide_request_region (ide_ioreg_t from, unsigned int extent, const char *name)
 {
-	if ( _machine == _MACH_Pmac )
+	if ( (_machine == _MACH_Pmac) || (_machine == _MACH_mbx) )
 		return;
 	request_region(from, extent, name);
 }
 
 static __inline__ void ide_release_region (ide_ioreg_t from, unsigned int extent)
 {
-	if ( _machine == _MACH_Pmac )
+	if ( (_machine == _MACH_Pmac) || (_machine == _MACH_mbx) )
 		return;
 	release_region(from, extent);
 }
@@ -87,7 +114,7 @@
 #define ide_fix_driveid(id)	do {			\
 	int nh;						\
 	unsigned short *p = (unsigned short *) id;	\
-	if ( _machine == _MACH_Pmac )			\
+	if (( _machine == _MACH_Pmac ) || (_machine == _MACH_chrp)|| (_machine == _MACH_mbx) )	\
 		for (nh = SECTOR_WORDS * 2; nh != 0; --nh, ++p)	\
 			*p = (*p << 8) + (*p >> 8);	\
 } while (0)
@@ -95,20 +122,39 @@
 
 #undef insw
 #define insw(port, buf, ns) 	do {			\
-	if ( _machine != _MACH_Pmac )			\
+	if ( _machine == _MACH_chrp)  {\
+		 ide_insw((port)+_IO_BASE, (buf), (ns));  \
+	}\
+	else if ( (_machine == _MACH_Pmac) || (_machine == _MACH_mbx) )			\
+		ide_insw((port), (buf), (ns));		\
+	else						\
 		/* this must be the same as insw in io.h!! */	\
 		_insw((unsigned short *)((port)+_IO_BASE), (buf), (ns)); \
-	else						\
-		ide_insw((port), (buf), (ns));		\
 } while (0)
 #undef outsw
+/*	printk("port: %x buf: %p ns: %d\n",port,buf,ns); \ */
 #define outsw(port, buf, ns) 	do {			\
-	if ( _machine != _MACH_Pmac )			\
+	if ( _machine == _MACH_chrp) {\
+		ide_outsw((port)+_IO_BASE, (buf), (ns)); \
+	}\
+	else if ( (_machine == _MACH_Pmac) || (_machine == _MACH_mbx) )			\
+		ide_outsw((port), (buf), (ns));		\
+	else						\
 		/* this must be the same as outsw in io.h!! */	\
 		_outsw((unsigned short *)((port)+_IO_BASE), (buf), (ns)); \
-	else						\
-		ide_outsw((port), (buf), (ns));		\
 } while (0)
+
+#undef inb
+#define inb(port)	\
+	in_8((unsigned char *)((port) + ((_machine==_MACH_Pmac)? 0: _IO_BASE)))
+#undef inb_p
+#define inb_p(port)	inb(port)
+
+#undef outb
+#define outb(val, port)	\
+	out_8((unsigned char *)((port) + ((_machine==_MACH_Pmac)? 0: _IO_BASE)), (val))
+#undef outb_p
+#define outb_p(val, port)	outb(val, port)
 
 typedef union {
 	unsigned all			: 8;	/* all of the bits together */
diff -u --recursive --new-file v2.1.96/linux/include/asm-ppc/io.h linux/include/asm-ppc/io.h
--- v2.1.96/linux/include/asm-ppc/io.h	Mon Jan 12 15:18:13 1998
+++ linux/include/asm-ppc/io.h	Tue Apr 14 17:34:00 1998
@@ -5,6 +5,8 @@
 #include <asm/page.h>
 #include <asm/byteorder.h>
 
+#define KERNELBASE	0xc0000000
+
 /* from the Carolina Technical Spec -- Cort */
 #define IBM_ACORN 0x82A
 #define SIO_CONFIG_RA	0x398
@@ -18,7 +20,6 @@
 
 #define SLOW_DOWN_IO
 
-#define PMAC_ISA_IO_BASE 	0
 #define PMAC_ISA_MEM_BASE 	0
 #define PMAC_PCI_DRAM_OFFSET 	0
 #define CHRP_ISA_IO_BASE 	0xf8000000
@@ -43,11 +44,18 @@
 #endif /* CONFIG_CHRP */
 
 #ifdef CONFIG_PMAC
-#define _IO_BASE	PMAC_ISA_IO_BASE
+extern unsigned long isa_io_base;
+#define _IO_BASE 	isa_io_base	/* well, PCI i/o base really */
 #define _ISA_MEM_BASE	PMAC_ISA_MEM_BASE
 #define PCI_DRAM_OFFSET PMAC_PCI_DRAM_OFFSET
 #endif /* CONFIG_PMAC */
 
+#ifdef CONFIG_MBX
+#define _IO_BASE        0
+#define _ISA_MEM_BASE   0
+#define PCI_DRAM_OFFSET 0x80000000
+#endif /* CONFIG_MBX8xx */
+
 #else /* CONFIG_MACH_SPECIFIC */
 extern unsigned long isa_io_base;
 #define _IO_BASE isa_io_base
@@ -122,8 +130,11 @@
  * Map in an area of physical address space, for accessing
  * I/O devices etc.
  */
+extern void *__ioremap(unsigned long address, unsigned long size,
+		       unsigned long flags);
 extern void *ioremap(unsigned long address, unsigned long size);
-extern void iounmap(unsigned long *addr);
+extern void iounmap(void *addr);
+extern unsigned long iopa(unsigned long addr);
 
 /*
  * Change virtual addresses to physical addresses and vv, for
diff -u --recursive --new-file v2.1.96/linux/include/asm-ppc/ioctl.h linux/include/asm-ppc/ioctl.h
--- v2.1.96/linux/include/asm-ppc/ioctl.h	Sat Nov 25 09:49:06 1995
+++ linux/include/asm-ppc/ioctl.h	Tue Apr 14 17:34:00 1998
@@ -49,4 +49,11 @@
 #define _IOC_NR(nr)		(((nr) >> _IOC_NRSHIFT) & _IOC_NRMASK)
 #define _IOC_SIZE(nr)		(((nr) >> _IOC_SIZESHIFT) & _IOC_SIZEMASK)
 
+/* various drivers, such as the pcmcia stuff, need these... */
+#define IOC_IN		(_IOC_WRITE << _IOC_DIRSHIFT)
+#define IOC_OUT		(_IOC_READ << _IOC_DIRSHIFT)
+#define IOC_INOUT	((_IOC_WRITE|_IOC_READ) << _IOC_DIRSHIFT)
+#define IOCSIZE_MASK	(_IOC_SIZEMASK << _IOC_SIZESHIFT)
+#define IOCSIZE_SHIFT	(_IOC_SIZESHIFT)
+
 #endif
diff -u --recursive --new-file v2.1.96/linux/include/asm-ppc/irq.h linux/include/asm-ppc/irq.h
--- v2.1.96/linux/include/asm-ppc/irq.h	Mon Jan 12 15:18:13 1998
+++ linux/include/asm-ppc/irq.h	Fri Apr 17 22:04:44 1998
@@ -1,6 +1,7 @@
 #ifndef _ASM_IRQ_H
 #define _ASM_IRQ_H
 
+#ifndef CONFIG_8xx
 /*
  * this is the # irq's for all ppc arch's (pmac/chrp/prep)
  * so it is the max of them all - which happens to be chrp
@@ -15,7 +16,68 @@
 #define irq_to_openpic(n)	((n)-NUM_8259_INTERRUPTS)
 #define IRQ_8259_CASCADE	NUM_8259_INTERRUPTS
 
+static __inline__ int irq_cannonicalize(int irq)
+{
+	return irq;
+}
+
 extern void disable_irq(unsigned int);
 extern void enable_irq(unsigned int);
+
+#else /* CONFIG_8xx */
+
+/* The MPC8xx cores have 16 possible interrupts.  There are eight
+ * possible level sensitive interrupts assigned and generated internally
+ * from such devices as CPM, PCMCIA, RTC, PIT, TimeBase and Decrementer.
+ * There are eight external interrupts (IRQs) that can be configured
+ * as either level or edge sensitive. 
+ * On the MBX implementation, there is also the possibility of an 8259
+ * through the PCI and PCI-ISA bridges.  All 8259 interrupts appear
+ * on the 8xx as IRQ3, but I may eventually add some of the 8259 code
+ * back into this port to handle that controller.
+ */
+#define NR_IRQS	16
+
+#define	SIU_IRQ0	0	/* Highest priority */
+#define	SIU_LEVEL0	1
+#define	SIU_IRQ1	2
+#define	SIU_LEVEL1	3
+#define	SIU_IRQ2	4
+#define	SIU_LEVEL2	5
+#define	SIU_IRQ3	6
+#define	SIU_LEVEL3	7
+#define	SIU_IRQ4	8
+#define	SIU_LEVEL4	9
+#define	SIU_IRQ5	10
+#define	SIU_LEVEL5	11
+#define	SIU_IRQ6	12
+#define	SIU_LEVEL6	13
+#define	SIU_IRQ7	14
+#define	SIU_LEVEL7	15
+
+/* The internal interrupts we can configure as we see fit.
+ * My personal preference is CPM at level 2, which puts it above the
+ * MBX PCI/ISA/IDE interrupts.
+ */
+#define PIT_INTERRUPT	SIU_LEVEL0
+#define CPM_INTERRUPT	SIU_LEVEL2
+#define DEC_INTERRUPT	SIU_LEVEL7
+
+/* Some internal interrupt registers use an 8-bit mask for the interrupt
+ * level instead of a number.
+ */
+#define	mk_int_int_mask(IL) (1 << (7 - (IL/2)))
+
+#ifdef CONFIG_MBX
+/* These are defined (and fixed) by the MBX hardware implementation.*/
+#define POWER_FAIL_INT	SIU_IRQ0	/* Power fail */
+#define TEMP_HILO_INT	SIU_IRQ1	/* Temperature sensor */
+#define QSPAN_INT	SIU_IRQ2	/* PCI Bridge (DMA CTLR?) */
+#define ISA_BRIDGE_INT	SIU_IRQ3	/* All those PC things */
+#define COMM_L_INT	SIU_IRQ6	/* MBX Comm expansion connector pin */
+#define STOP_ABRT_INT	SIU_IRQ7	/* Stop/Abort header pin */
+#endif /* CONFIG_MBX */
+
+#endif /* CONFIG_8xx */
 
 #endif
diff -u --recursive --new-file v2.1.96/linux/include/asm-ppc/keyboard.h linux/include/asm-ppc/keyboard.h
--- v2.1.96/linux/include/asm-ppc/keyboard.h	Thu Jul 31 13:09:18 1997
+++ linux/include/asm-ppc/keyboard.h	Tue Apr 14 17:34:00 1998
@@ -16,8 +16,11 @@
 #ifdef __KERNEL__
 
 #include <linux/config.h>
+#include <asm/adb.h>
 
-#ifdef CONFIG_MAC_KEYBOARD
+#define KEYBOARD_IRQ			1
+#define DISABLE_KBD_DURING_INTERRUPTS	0
+#define INIT_KBD
 
 extern int mackbd_setkeycode(unsigned int scancode, unsigned int keycode);
 extern int mackbd_getkeycode(unsigned int scancode);
@@ -28,19 +31,6 @@
 extern void mackbd_leds(unsigned char leds);
 extern void mackbd_init_hw(void);
 
-#define kbd_setkeycode		mackbd_setkeycode
-#define kbd_getkeycode		mackbd_getkeycode
-#define kbd_pretranslate	mackbd_pretranslate
-#define kbd_translate		mackbd_translate
-#define kbd_unexpected_up	mackbd_unexpected_up
-#define kbd_leds		mackbd_leds
-#define kbd_init_hw		mackbd_init_hw
-
-#else /* CONFIG_MAC_KEYBOARD */
-
-#define KEYBOARD_IRQ			1
-#define DISABLE_KBD_DURING_INTERRUPTS	0
-
 extern int pckbd_setkeycode(unsigned int scancode, unsigned int keycode);
 extern int pckbd_getkeycode(unsigned int scancode);
 extern int pckbd_pretranslate(unsigned char scancode, char raw_mode);
@@ -50,16 +40,133 @@
 extern void pckbd_leds(unsigned char leds);
 extern void pckbd_init_hw(void);
 
-#define kbd_setkeycode		pckbd_setkeycode
-#define kbd_getkeycode		pckbd_getkeycode
-#define kbd_pretranslate	pckbd_pretranslate
-#define kbd_translate		pckbd_translate
-#define kbd_unexpected_up	pckbd_unexpected_up
-#define kbd_leds		pckbd_leds
-#define kbd_init_hw		pckbd_init_hw
-
-#define INIT_KBD
-#endif /* CONFIG_MAC_KEYBOARD */
+static inline int kbd_setkeycode(unsigned int scancode, unsigned int keycode)
+{
+	if ( is_prep )
+		return pckbd_setkeycode(scancode,keycode);
+	else if ( is_chrp )
+#ifndef CONFIG_MAC_KEYBOARD
+		return pckbd_setkeycode(scancode,keycode);
+#else
+		/* I'm not actually sure if it's legal to have a CHRP machine
+		 * without an ADB controller. In any case, this should really
+		 * be changed to be a test to see if an ADB _keyboard_ exists
+		 * (not just a controller), but that's another story for
+		 * another night.
+		 */
+		if ( adb_hardware == ADB_NONE )
+			return pckbd_setkeycode(scancode,keycode);
+		else
+			return mackbd_setkeycode(scancode,keycode);
+#endif
+	else
+		return mackbd_setkeycode(scancode,keycode);
+}
+
+static inline int kbd_getkeycode(unsigned int x)
+{
+	if ( is_prep )
+		return pckbd_getkeycode(x);
+	else if ( is_chrp )
+#ifndef CONFIG_MAC_KEYBOARD
+		return pckbd_getkeycode(x);
+#else
+		if ( adb_hardware == ADB_NONE )
+			return pckbd_getkeycode(x);
+		else
+			return mackbd_getkeycode(x);
+#endif
+	else
+		return mackbd_getkeycode(x);
+}
+
+static inline int kbd_pretranslate(unsigned char x,char y)
+{
+	if ( is_prep )
+		return pckbd_pretranslate(x,y);
+	else if ( is_chrp )
+#ifndef CONFIG_MAC_KEYBOARD
+		return pckbd_pretranslate(x,y);
+#else
+		if ( adb_hardware == ADB_NONE )
+			return pckbd_pretranslate(x,y);
+		else
+			return mackbd_pretranslate(x,y);
+#endif
+	else
+		return mackbd_pretranslate(x,y);
+}
+
+static inline int kbd_translate(unsigned char keycode, unsigned char *keycodep,
+		     char raw_mode)
+{
+	if ( is_prep )
+		return pckbd_translate(keycode,keycodep,raw_mode);
+	else if ( is_chrp )
+#ifndef CONFIG_MAC_KEYBOARD
+		return pckbd_translate(keycode,keycodep,raw_mode);
+#else
+		if ( adb_hardware == ADB_NONE )
+			return pckbd_translate(keycode,keycodep,raw_mode);
+		else
+			return mackbd_translate(keycode,keycodep,raw_mode);
+#endif
+	else
+		return mackbd_translate(keycode,keycodep,raw_mode);
+	
+}
+
+static inline int kbd_unexpected_up(unsigned char keycode)
+{
+	if ( is_prep )
+		return pckbd_unexpected_up(keycode);
+	else if ( is_chrp )
+#ifndef CONFIG_MAC_KEYBOARD
+		return pckbd_unexpected_up(keycode);
+#else
+		if ( adb_hardware == ADB_NONE )
+			return pckbd_unexpected_up(keycode);
+		else
+			return mackbd_unexpected_up(keycode);
+#endif
+	else
+		return mackbd_unexpected_up(keycode);
+	
+}
+
+static inline void kbd_leds(unsigned char leds)
+{
+	if ( is_prep )
+		pckbd_leds(leds);
+	else if ( is_chrp )
+#ifndef CONFIG_MAC_KEYBOARD
+		pckbd_leds(leds);
+#else
+		if ( adb_hardware == ADB_NONE )
+			pckbd_leds(leds);
+		else
+			mackbd_leds(leds);
+#endif
+	else
+		mackbd_leds(leds);
+}
+
+static inline void kbd_init_hw(void)
+{
+	if ( is_prep )
+		pckbd_init_hw();
+	else if ( is_chrp )
+#ifndef CONFIG_MAC_KEYBOARD
+		pckbd_init_hw();
+#else
+		if ( adb_hardware == ADB_NONE )
+			pckbd_init_hw();
+		else
+			mackbd_init_hw();
+#endif
+	else
+		mackbd_init_hw();
+}
 
 #endif /* __KERNEL__ */
 
diff -u --recursive --new-file v2.1.96/linux/include/asm-ppc/kgdb.h linux/include/asm-ppc/kgdb.h
--- v2.1.96/linux/include/asm-ppc/kgdb.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-ppc/kgdb.h	Tue Apr 14 17:34:00 1998
@@ -0,0 +1,48 @@
+/* $Id: kgdb.h,v 1.2 1998/04/11 17:29:07 geert Exp $
+ * kgdb.h: Defines and declarations for serial line source level
+ *         remote debugging of the Linux kernel using gdb.
+ *
+ * PPC Mods (C) 1998 Michael Tesch (tesch@cs.wisc.edu)
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+#ifndef _PPC_KGDB_H
+#define _PPC_KGDB_H
+
+#ifndef __ASSEMBLY__
+/* To initialize the serial, first thing called */
+extern void zs_kgdb_hook(int tty_num);
+/* To init the kgdb engine. (called by serial hook)*/
+extern void set_debug_traps(void);
+
+/* To enter the debugger explicitly. */
+extern void breakpoint(void);
+
+/* For taking exceptions
+ * these are defined in traps.c
+ */
+extern void (*debugger)(struct pt_regs *regs);
+extern int (*debugger_bpt)(struct pt_regs *regs);
+extern int (*debugger_sstep)(struct pt_regs *regs);
+extern int (*debugger_iabr_match)(struct pt_regs *regs);
+extern int (*debugger_dabr_match)(struct pt_regs *regs);
+extern void (*debugger_fault_handler)(struct pt_regs *regs);
+
+/* What we bring to the party */
+int kgdb_bpt(struct pt_regs *regs);
+int kgdb_sstep(struct pt_regs *regs);
+void kgdb(struct pt_regs *regs);
+int kgdb_iabr_match(struct pt_regs *regs);
+int kgdb_dabr_match(struct pt_regs *regs);
+static void kgdb_fault_handler(struct pt_regs *regs);
+static void handle_exception (struct pt_regs *regs);
+
+/*
+ * external low-level support routines (ie macserial.c)
+ */
+extern void kgdb_interruptible(int); /* control interrupts from serial */
+extern void putDebugChar(char);   /* write a single character      */
+extern char getDebugChar(void);   /* read and return a single char */
+
+#endif /* !(__ASSEMBLY__) */
+#endif /* !(_PPC_KGDB_H) */
diff -u --recursive --new-file v2.1.96/linux/include/asm-ppc/mbx.h linux/include/asm-ppc/mbx.h
--- v2.1.96/linux/include/asm-ppc/mbx.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-ppc/mbx.h	Tue Apr 14 17:34:00 1998
@@ -0,0 +1,62 @@
+
+/*
+ * A collection of structures, addresses, and values associated with
+ * the Motorola MBX boards.  This was originally created for the
+ * MBX860, and probably needs revisions for other boards (like the 821).
+ * When this file gets out of control, we can split it up into more
+ * meaningful pieces.
+ *
+ * Copyright (c) 1997 Dan Malek (dmalek@jlc.net)
+ */
+#ifndef __MACH_MBX_DEFS
+#define __MACH_MBX_DEFS
+
+/* A Board Information structure that is given to a program when
+ * EPPC-Bug starts it up.
+ */
+typedef struct bd_info {
+	unsigned int	bi_tag;		/* Should be 0x42444944 "BDID" */
+	unsigned int	bi_size;	/* Size of this structure */
+	unsigned int	bi_revision;	/* revision of this structure */
+	unsigned int	bi_bdate;	/* EPPCbug date, i.e. 0x11061997 */
+	unsigned int	bi_memstart;	/* Memory start address */
+	unsigned int	bi_memsize;	/* Memory (end) size in bytes */
+	unsigned int	bi_intfreq;	/* Internal Freq, in Hz */
+	unsigned int	bi_busfreq;	/* Bus Freq, in Hz */
+	unsigned int	bi_clun;	/* Boot device controller */
+	unsigned int	bi_dlun;	/* Boot device logical dev */
+} bd_t;
+
+/* Memory map for the MBX as configured by EPPC-Bug.  We could reprogram
+ * The SIU and PCI bridge, and try to use larger MMU pages, but the
+ * performance gain is not measureable and it certainly complicates the
+ * generic MMU model.
+ *
+ * In a effort to minimize memory usage for embedded applications, any
+ * PCI driver or ISA driver must request or map the region required by
+ * the device.  For convenience (and since we can map up to 4 Mbytes with
+ * a single page table page), the MMU initialization will map the
+ * NVRAM, Status/Control registers, CPM Dual Port RAM, and the PCI
+ * Bridge CSRs 1:1 into the kernel address space.
+ */
+#define PCI_ISA_IO_ADDR		((uint)0x80000000)
+#define PCI_ISA_IO_SIZE		((uint)(512 * 1024 * 1024))
+#define PCI_ISA_MEM_ADDR	((uint)0xc0000000)
+#define PCI_ISA_MEM_SIZE	((uint)(512 * 1024 * 1024))
+#define PCMCIA_MEM_ADDR		((uint)0xe0000000)
+#define PCMCIA_MEM_SIZE		((uint)(64 * 1024 * 1024))
+#define PCMCIA_DMA_ADDR		((uint)0xe4000000)
+#define PCMCIA_DMA_SIZE		((uint)(64 * 1024 * 1024))
+#define PCMCIA_ATTRB_ADDR	((uint)0xe8000000)
+#define PCMCIA_ATTRB_SIZE	((uint)(64 * 1024 * 1024))
+#define PCMCIA_IO_ADDR		((uint)0xec000000)
+#define PCMCIA_IO_SIZE		((uint)(64 * 1024 * 1024))
+#define NVRAM_ADDR		((uint)0xfa000000)
+#define NVRAM_SIZE		((uint)(1 * 1024 * 1024))
+#define MBX_CSR_ADDR		((uint)0xfa100000)
+#define MBX_CSR_SIZE		((uint)(1 * 1024 * 1024))
+#define MBX_IMAP_ADDR		((uint)0xfa200000)
+#define MBX_IMAP_SIZE		((uint)(64 * 1024))
+#define PCI_CSR_ADDR		((uint)0xfa210000)
+#define PCI_CSR_SIZE		((uint)(64 * 1024))
+#endif
diff -u --recursive --new-file v2.1.96/linux/include/asm-ppc/mediabay.h linux/include/asm-ppc/mediabay.h
--- v2.1.96/linux/include/asm-ppc/mediabay.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-ppc/mediabay.h	Tue Apr 14 17:34:00 1998
@@ -0,0 +1,33 @@
+/*
+ * mediabay.h: definitions for using the media bay
+ * on PowerBook 3400 and similar computers.
+ *
+ * Copyright (C) 1997 Paul Mackerras.
+ */
+#ifndef _PPC_MEDIABAY_H
+#define _PPC_MEDIABAY_H
+
+#define MB_FD	0		/* media bay contains floppy drive */
+#define MB_CD	3		/* media bay contains ATA drive such as CD */
+#define MB_NO	7		/* media bay contains nothing */
+
+#ifdef __KERNEL__
+
+void media_bay_init(void);
+int check_media_bay(int what);
+int media_bay_task(void *);
+
+extern int media_bay_present;	/* 1 if this machine has a media bay */
+
+/*
+ * The following give information about the IDE interface
+ * of the media bay: the base virtual address and IRQ number,
+ * and the index that the IDE driver has assigned to it
+ * (or -1 if it is not currently registered with the driver).
+ */
+extern unsigned long mb_cd_base;
+extern int mb_cd_irq;
+extern int mb_cd_index;
+
+#endif /* __KERNEL__ */
+#endif /* _PPC_MEDIABAY_H */
diff -u --recursive --new-file v2.1.96/linux/include/asm-ppc/mmu.h linux/include/asm-ppc/mmu.h
--- v2.1.96/linux/include/asm-ppc/mmu.h	Mon Jan 12 15:18:13 1998
+++ linux/include/asm-ppc/mmu.h	Tue Apr 14 17:34:00 1998
@@ -5,6 +5,7 @@
 #ifndef _PPC_MMU_H_
 #define _PPC_MMU_H_
 
+#ifndef __ASSEMBLY__
 /* Hardware Page Table Entry */
 typedef struct _PTE {
 	unsigned long v:1;	/* Entry is valid */
@@ -157,4 +158,136 @@
 }
 
 extern void _tlbia(void);		/* invalidate all TLB entries */
-#endif
+#endif /* __ASSEMBLY__ */
+
+/* Control/status registers for the MPC8xx.
+ * A write operation to these registers causes serialized access.
+ * During software tablewalk, the registers used perform mask/shift-add
+ * operations when written/read.  A TLB entry is created when the Mx_RPN
+ * is written, and the contents of several registers are used to
+ * create the entry.
+ */
+#define MI_CTR		784	/* Instruction TLB control register */
+#define MI_GPM		0x80000000	/* Set domain manager mode */
+#define MI_PPM		0x40000000	/* Set subpage protection */
+#define MI_CIDEF	0x20000000	/* Set cache inhibit when MMU dis */
+#define MI_RSV4I	0x08000000	/* Reserve 4 TLB entries */
+#define MI_PPCS		0x02000000	/* Use MI_RPN prob/priv state */
+#define MI_IDXMASK	0x00001f00	/* TLB index to be loaded */
+#define MI_RESETVAL	0x00000000	/* Value of register at reset */
+
+/* These are the Ks and Kp from the PowerPC books.  For proper operation,
+ * Ks = 0, Kp = 1.
+ */
+#define MI_AP		786
+#define MI_Ks		0x80000000	/* Should not be set */
+#define MI_Kp		0x40000000	/* Should always be set */
+
+/* The effective page number register.  When read, contains the information
+ * about the last instruction TLB miss.  When MI_RPN is written, bits in
+ * this register are used to create the TLB entry.
+ */
+#define MI_EPN		787
+#define MI_EPNMASK	0xfffff000	/* Effective page number for entry */
+#define MI_EVALID	0x00000200	/* Entry is valid */
+#define MI_ASIDMASK	0x0000000f	/* ASID match value */
+					/* Reset value is undefined */
+
+/* A "level 1" or "segment" or whatever you want to call it register.
+ * For the instruction TLB, it contains bits that get loaded into the
+ * TLB entry when the MI_RPN is written.
+ */
+#define MI_TWC		789
+#define MI_APG		0x000001e0	/* Access protection group (0) */
+#define MI_GUARDED	0x00000010	/* Guarded storage */
+#define MI_PSMASK	0x0000000c	/* Mask of page size bits */
+#define MI_PS8MEG	0x0000000c	/* 8M page size */
+#define MI_PS512K	0x00000004	/* 512K page size */
+#define MI_PS4K_16K	0x00000000	/* 4K or 16K page size */
+#define MI_SVALID	0x00000001	/* Segment entry is valid */
+					/* Reset value is undefined */
+
+/* Real page number.  Defined by the pte.  Writing this register
+ * causes a TLB entry to be created for the instruction TLB, using
+ * additional information from the MI_EPN, and MI_TWC registers.
+ */
+#define MI_RPN		790
+
+/* Define an RPN value for mapping kernel memory to large virtual
+ * pages for boot initialization.  This has real page number of 0,
+ * large page size, shared page, cache enabled, and valid.
+ * Also mark all subpages valid and write access.
+ */
+#define MI_BOOTINIT	0x000001fd
+
+#define MD_CTR		792	/* Data TLB control register */
+#define MD_GPM		0x80000000	/* Set domain manager mode */
+#define MD_PPM		0x40000000	/* Set subpage protection */
+#define MD_CIDEF	0x20000000	/* Set cache inhibit when MMU dis */
+#define MD_WTDEF	0x10000000	/* Set writethrough when MMU dis */
+#define MD_RSV4I	0x08000000	/* Reserve 4 TLB entries */
+#define MD_TWAM		0x04000000	/* Use 4K page hardware assist */
+#define MD_PPCS		0x02000000	/* Use MI_RPN prob/priv state */
+#define MD_IDXMASK	0x00001f00	/* TLB index to be loaded */
+#define MD_RESETVAL	0x04000000	/* Value of register at reset */
+
+#define M_CASID		793	/* Address space ID (context) to match */
+#define MC_ASIDMASK	0x0000000f	/* Bits used for ASID value */
+
+
+/* These are the Ks and Kp from the PowerPC books.  For proper operation,
+ * Ks = 0, Kp = 1.
+ */
+#define MD_AP		794
+#define MD_Ks		0x80000000	/* Should not be set */
+#define MD_Kp		0x40000000	/* Should always be set */
+
+/* The effective page number register.  When read, contains the information
+ * about the last instruction TLB miss.  When MD_RPN is written, bits in
+ * this register are used to create the TLB entry.
+ */
+#define MD_EPN		795
+#define MD_EPNMASK	0xfffff000	/* Effective page number for entry */
+#define MD_EVALID	0x00000200	/* Entry is valid */
+#define MD_ASIDMASK	0x0000000f	/* ASID match value */
+					/* Reset value is undefined */
+
+/* The pointer to the base address of the first level page table.
+ * During a software tablewalk, reading this register provides the address
+ * of the entry associated with MD_EPN.
+ */
+#define M_TWB		796
+#define	M_L1TB		0xfffff000	/* Level 1 table base address */
+#define M_L1INDX	0x00000ffc	/* Level 1 index, when read */
+					/* Reset value is undefined */
+
+/* A "level 1" or "segment" or whatever you want to call it register.
+ * For the data TLB, it contains bits that get loaded into the TLB entry
+ * when the MD_RPN is written.  It is also provides the hardware assist
+ * for finding the PTE address during software tablewalk.
+ */
+#define MD_TWC		797
+#define MD_L2TB		0xfffff000	/* Level 2 table base address */
+#define MD_L2INDX	0xfffffe00	/* Level 2 index (*pte), when read */
+#define MD_APG		0x000001e0	/* Access protection group (0) */
+#define MD_GUARDED	0x00000010	/* Guarded storage */
+#define MD_PSMASK	0x0000000c	/* Mask of page size bits */
+#define MD_PS8MEG	0x0000000c	/* 8M page size */
+#define MD_PS512K	0x00000004	/* 512K page size */
+#define MD_PS4K_16K	0x00000000	/* 4K or 16K page size */
+#define MD_WT		0x00000002	/* Use writethrough page attribute */
+#define MD_SVALID	0x00000001	/* Segment entry is valid */
+					/* Reset value is undefined */
+
+
+/* Real page number.  Defined by the pte.  Writing this register
+ * causes a TLB entry to be created for the data TLB, using
+ * additional information from the MD_EPN, and MD_TWC registers.
+ */
+#define MD_RPN		798
+
+/* This is a temporary storage register that could be used to save
+ * a processor working register during a tablewalk.
+ */
+#define M_TW		799
+#endif /* _PPC_MMU_H_ */
diff -u --recursive --new-file v2.1.96/linux/include/asm-ppc/mmu_context.h linux/include/asm-ppc/mmu_context.h
--- v2.1.96/linux/include/asm-ppc/mmu_context.h	Sat Aug 16 09:51:09 1997
+++ linux/include/asm-ppc/mmu_context.h	Tue Apr 14 17:34:00 1998
@@ -6,21 +6,44 @@
    perhaps I can defer flushing the tlb by keeping a list of
    zombie vsid/context's and handling that through destroy_context
    later -- Cort
+
+   The MPC8xx has only 16 contexts.  We rotate through them on each
+   task switch.  A better way would be to keep track of tasks that
+   own contexts, and implement an LRU usage.  That way very active
+   tasks don't always have to pay the TLB reload overhead.  The
+   kernel pages are mapped shared, so the kernel can run on behalf
+   of any task that makes a kernel entry.  Shared does not mean they
+   are not protected, just that the ASID comparison is not performed.
+        -- Dan
  */
 
-#define NO_CONTEXT	0
-#define LAST_CONTEXT	0xfffff
+#ifdef CONFIG_8xx
+#define NO_CONTEXT      16
+#define LAST_CONTEXT    15
+#else
+#define NO_CONTEXT      0
+#define LAST_CONTEXT    0xfffff
+#endif
 
 extern int next_mmu_context;
 extern void mmu_context_overflow(void);
+
+#ifndef CONFIG_8xx
 extern void set_context(int context);
+#else
+#define set_context(context)    do { } while (0)
+#endif
 
+#ifndef CONFIG_8xx
 /*
  * Allocating context numbers this way tends to spread out
  * the entries in the hash table better than a simple linear
  * allocation.
  */
-#define MUNGE_CONTEXT(n)	(((n) * 897) & LAST_CONTEXT)
+#define MUNGE_CONTEXT(n)        (((n) * 897) & LAST_CONTEXT)
+#else
+#define MUNGE_CONTEXT(n)        (n)
+#endif
 
 /*
  * Get a new mmu context for task tsk if necessary.
@@ -45,7 +68,11 @@
 /*
  * We're finished using the context for an address space.
  */
-#define destroy_context(mm)	do { } while (0)
+#ifdef CONFIG_8xx
+#define destroy_context(mm)     ((mm)->context = NO_CONTEXT)
+#else
+#define destroy_context(mm)     do { } while (0)
+#endif
 
 /*
  * compute the vsid from the context and segment
diff -u --recursive --new-file v2.1.96/linux/include/asm-ppc/ohare.h linux/include/asm-ppc/ohare.h
--- v2.1.96/linux/include/asm-ppc/ohare.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-ppc/ohare.h	Tue Apr 14 17:34:00 1998
@@ -0,0 +1,41 @@
+/*
+ * ohare.h: definitions for using the "O'Hare" I/O controller chip.
+ *
+ * Copyright (C) 1997 Paul Mackerras.
+ */
+
+/* offset from ohare base for feature control register */
+#define OHARE_FEATURE_REG	0x38
+
+/*
+ * Bits in feature control register.
+ * These were mostly derived by experiment on a powerbook 3400
+ * and may differ for other machines.
+ */
+#define OH_SCC_RESET		1
+#define OH_BAY_RESET		2	/* a guess */
+#define OH_BAY_PCI_ENABLE	4	/* a guess */
+#define OH_BAY_IDE_ENABLE	8
+#define OH_BAY_FLOPPY_ENABLE	0x10
+#define OH_IDE_ENABLE		0x20
+#define OH_BAY_ENABLE		0x80
+#define OH_SCC_ENABLE		0x200
+#define OH_MESH_ENABLE		0x400
+#define OH_FLOPPY_ENABLE	0x800
+#define OH_SCCA_IO		0x2000
+#define OH_SCCB_IO		0x4000
+#define OH_VIA_ENABLE		0x10000
+#define OH_IDECD_POWER		0x800000
+
+/*
+ * Bits to set in the feature control register on PowerBooks.
+ */
+#define PBOOK_FEATURES		(OH_IDE_ENABLE | OH_SCC_ENABLE | \
+				 OH_MESH_ENABLE | OH_SCCA_IO | OH_SCCB_IO)
+
+/*
+ * A magic value to put into the feature control register of the
+ * "ohare" I/O controller on Starmaxes to enable the IDE CD interface.
+ * Contributed by Harry Eaton.
+ */
+#define STARMAX_FEATURES	0xbeff7a
diff -u --recursive --new-file v2.1.96/linux/include/asm-ppc/page.h linux/include/asm-ppc/page.h
--- v2.1.96/linux/include/asm-ppc/page.h	Mon Jan 12 15:18:13 1998
+++ linux/include/asm-ppc/page.h	Tue Apr 14 17:34:00 1998
@@ -6,8 +6,7 @@
 #define PAGE_SIZE	(1UL << PAGE_SHIFT)
 #define PAGE_MASK	(~(PAGE_SIZE-1))
 
-/* KERNELBASE comes from arch/ppc/Makefile */
-#define PAGE_OFFSET	KERNELBASE
+#define PAGE_OFFSET	0xc0000000
 
 
 #ifndef __ASSEMBLY__
@@ -66,8 +65,15 @@
 #define clear_page(page)        memset((void *)(page), 0, PAGE_SIZE)
 #define copy_page(to,from)	memcpy((void *)(to), (void *)(from), PAGE_SIZE)
 /* map phys->virtual and virtual->phys for RAM pages */
+#ifdef CONFIG_APUS
+#include <asm/amigappc.h>
+/* Word at CYBERBASEp has the value (-KERNELBASE+CYBERBASE). */
+#define __pa(x)			((unsigned long)(x)+(*(unsigned long*)CYBERBASEp))
+#define __va(x)			((void *)((unsigned long)(x)-(*(unsigned long*)CYBERBASEp)))
+#else
 #define __pa(x)			((unsigned long)(x)-PAGE_OFFSET)
 #define __va(x)			((void *)((unsigned long)(x)+PAGE_OFFSET))
+#endif
 
 #define MAP_NR(addr)		(((unsigned long)addr-PAGE_OFFSET) >> PAGE_SHIFT)
 #define MAP_PAGE_RESERVED	(1<<15)
diff -u --recursive --new-file v2.1.96/linux/include/asm-ppc/pci-bridge.h linux/include/asm-ppc/pci-bridge.h
--- v2.1.96/linux/include/asm-ppc/pci-bridge.h	Sat Aug 16 09:51:09 1997
+++ linux/include/asm-ppc/pci-bridge.h	Tue Apr 14 17:34:00 1998
@@ -17,4 +17,14 @@
 int pci_device_loc(struct device_node *dev, unsigned char *bus_ptr,
 		   unsigned char *devfn_ptr);
 
+struct bridge_data {
+	volatile unsigned int *cfg_addr;
+	volatile unsigned char *cfg_data;
+	void *io_base;
+	int bus_number;
+	int max_bus;
+	struct bridge_data *next;
+	struct device_node *node;
+};
+
 #endif
diff -u --recursive --new-file v2.1.96/linux/include/asm-ppc/pgtable.h linux/include/asm-ppc/pgtable.h
--- v2.1.96/linux/include/asm-ppc/pgtable.h	Mon Jan 12 15:18:13 1998
+++ linux/include/asm-ppc/pgtable.h	Tue Apr 14 17:34:00 1998
@@ -1,7 +1,10 @@
 #ifndef _PPC_PGTABLE_H
 #define _PPC_PGTABLE_H
 
+#ifndef __ASSEMBLY__
 #include <linux/mm.h>
+#include <asm/processor.h>		/* For TASK_SIZE */
+#include <asm/mmu.h>
 
 extern void local_flush_tlb_all(void);
 extern void local_flush_tlb_mm(struct mm_struct *mm);
@@ -36,6 +39,9 @@
 extern void flush_icache_range(unsigned long, unsigned long);
 extern void flush_page_to_ram(unsigned long);
 
+extern unsigned long va_to_phys(unsigned long address);
+extern pte_t *va_to_pte(struct task_struct *tsk, unsigned long address);
+#endif /* __ASSEMBLY__ */
 /*
  * The PowerPC MMU uses a hash table containing PTEs, together with
  * a set of 16 segment registers (on 32-bit implementations), to define
@@ -48,6 +54,23 @@
  * for extracting ptes from the tree and putting them into the hash table
  * when necessary, and updating the accessed and modified bits in the
  * page table tree.
+ *
+ * The PowerPC MPC8xx uses a TLB with hardware assisted, software tablewalk.
+ * We also use the two level tables, but we can put the real bits in them
+ * needed for the TLB and tablewalk.  These definitions require Mx_CTR.PPM = 0,
+ * Mx_CTR.PPCS = 0, and MD_CTR.TWAM = 1.  The level 2 descriptor has
+ * additional page protection (when Mx_CTR.PPCS = 1) that allows TLB hit
+ * based upon user/super access.  The TLB does not have accessed nor write
+ * protect.  We assume that if the TLB get loaded with an entry it is
+ * accessed, and overload the the changed bit for write protect.  We use
+ * two bits in the software pte that are supposed to be set to zero in
+ * the TLB entry (24 and 25) for these indicators.  Although the level 1
+ * descriptor contains the guarded and writethrough/copyback bits, we can
+ * set these at the page level since they get copied from the Mx_TWC
+ * register when the TLB entry is loaded.  We will use bit 27 for guard, since
+ * that is where it exists in the MD_TWC, and bit 26 for writethrough.
+ * These will get masked from the level 2 descriptor at TLB load time, and
+ * copied to the MD_TWC before it gets loaded.
  */
 
 /* PMD_SHIFT determines the size of the area mapped by the second-level page tables */
@@ -67,6 +90,7 @@
 #define PTRS_PER_PTE	1024
 #define PTRS_PER_PMD	1
 #define PTRS_PER_PGD	1024
+#define USER_PTRS_PER_PGD	(TASK_SIZE / PGDIR_SIZE)
 
 /* Just any arbitrary offset to the start of the vmalloc VM area: the
  * current 64MB value just means that there will be a 64MB "hole" after the
@@ -89,6 +113,7 @@
  * Bits in a linux-style PTE.  These match the bits in the
  * (hardware-defined) PowerPC PTE as closely as possible.
  */
+#ifndef CONFIG_8xx
 #define _PAGE_PRESENT	0x001	/* software: pte contains a translation */
 #define _PAGE_USER	0x002	/* matches one of the PP bits */
 #define _PAGE_RW	0x004	/* software: user write access allowed */
@@ -100,9 +125,34 @@
 #define _PAGE_ACCESSED	0x100	/* R: page referenced */
 #define _PAGE_HWWRITE	0x200	/* software: _PAGE_RW & _PAGE_DIRTY */
 
+#else
+#define _PAGE_PRESENT	0x0001	/* Page is valid */
+#define _PAGE_NO_CACHE	0x0002	/* I: cache inhibit */
+#define _PAGE_SHARED	0x0004	/* No ASID (context) compare */
+
+/* These four software bits must be masked out when the entry is loaded
+ * into the TLB.
+ */
+#define _PAGE_GUARDED	0x0010	/* software: guarded access */
+#define _PAGE_WRITETHRU 0x0020	/* software: use writethrough cache */
+#define _PAGE_RW	0x0040	/* software: user write access allowed */
+#define _PAGE_ACCESSED	0x0080	/* software: page referenced */
+
+#define _PAGE_DIRTY	0x0100	/* C: page changed (write protect) */
+#define _PAGE_USER	0x0800	/* One of the PP bits, the other must be 0 */
+
+/* This is used to enable or disable the actual hardware write
+ * protection.
+ */
+#define _PAGE_HWWRITE	_PAGE_DIRTY
+
+#endif /* CONFIG_8xx */
+
 #define _PAGE_CHG_MASK	(PAGE_MASK | _PAGE_ACCESSED | _PAGE_DIRTY)
 
 #define PAGE_NONE	__pgprot(_PAGE_PRESENT | _PAGE_ACCESSED)
+
+#ifndef CONFIG_8xx
 #define PAGE_SHARED	__pgprot(_PAGE_PRESENT | _PAGE_RW | _PAGE_USER | \
 				 _PAGE_ACCESSED)
 #define PAGE_COPY	__pgprot(_PAGE_PRESENT | _PAGE_USER | _PAGE_ACCESSED)
@@ -111,7 +161,17 @@
 				 _PAGE_HWWRITE | _PAGE_ACCESSED)
 #define PAGE_KERNEL_CI	__pgprot(_PAGE_PRESENT | _PAGE_NO_CACHE | _PAGE_RW | \
 				 _PAGE_HWWRITE | _PAGE_DIRTY | _PAGE_ACCESSED)
-
+#else
+#define PAGE_SHARED	__pgprot(_PAGE_PRESENT | _PAGE_RW | _PAGE_USER | \
+				 _PAGE_ACCESSED | _PAGE_SHARED)
+#define PAGE_COPY	__pgprot(_PAGE_PRESENT | _PAGE_USER | _PAGE_ACCESSED)
+#define PAGE_READONLY	__pgprot(_PAGE_PRESENT | _PAGE_USER | _PAGE_ACCESSED)
+#define PAGE_KERNEL	__pgprot(_PAGE_PRESENT | _PAGE_RW | _PAGE_DIRTY | \
+				 _PAGE_SHARED | _PAGE_ACCESSED)
+#define PAGE_KERNEL_CI	__pgprot(_PAGE_PRESENT | _PAGE_NO_CACHE | _PAGE_RW | \
+				 _PAGE_SHARED | _PAGE_DIRTY | _PAGE_ACCESSED)
+#endif /* CONFIG_8xx */
+     
 /*
  * The PowerPC can only do execute protection on a segment (256MB) basis,
  * not on a page basis.  So we consider execute permission the same as read.
@@ -143,11 +203,12 @@
  * ZERO_PAGE is a global shared page that is always zero: used
  * for zero-mapped memory areas etc..
  */
+#ifndef __ASSEMBLY__
 extern pte_t __bad_page(void);
 extern pte_t * __bad_pagetable(void);
 
 extern unsigned long empty_zero_page[1024];
-
+#endif __ASSEMBLY__
 #define BAD_PAGETABLE	__bad_pagetable()
 #define BAD_PAGE	__bad_page()
 #define ZERO_PAGE	((unsigned long) empty_zero_page)
@@ -164,9 +225,19 @@
 
 /* to set the page-dir */
 /* tsk is a task_struct and pgdir is a pte_t */
+#ifndef CONFIG_8xx
 #define SET_PAGE_DIR(tsk,pgdir)  \
 	((tsk)->tss.pg_tables = (unsigned long *)(pgdir))
-
+#else /* CONFIG_8xx */     
+#define SET_PAGE_DIR(tsk,pgdir)  \
+ do { \
+	unsigned long __pgdir = (unsigned long)pgdir; \
+	((tsk)->tss.pg_tables = (unsigned long *)(__pgdir)); \
+	asm("mtspr %0,%1 \n\t" :: "i"(M_TWB), "r"(__pa(__pgdir))); \
+ } while (0)
+#endif /* CONFIG_8xx */
+     
+#ifndef __ASSEMBLY__
 extern inline int pte_none(pte_t pte)		{ return !pte_val(pte); }
 extern inline int pte_present(pte_t pte)	{ return pte_val(pte) & _PAGE_PRESENT; }
 extern inline void pte_clear(pte_t *ptep)	{ pte_val(*ptep) = 0; }
@@ -197,8 +268,8 @@
 extern inline int pte_dirty(pte_t pte)		{ return pte_val(pte) & _PAGE_DIRTY; }
 extern inline int pte_young(pte_t pte)		{ return pte_val(pte) & _PAGE_ACCESSED; }
 
-extern inline int pte_uncache(pte_t pte)        { return pte_val(pte) |= _PAGE_NO_CACHE; }
-extern inline int pte_cache(pte_t pte)          { return pte_val(pte) &= ~_PAGE_NO_CACHE; }
+extern inline void pte_uncache(pte_t pte)       { pte_val(pte) |= _PAGE_NO_CACHE; }
+extern inline void pte_cache(pte_t pte)         { pte_val(pte) &= ~_PAGE_NO_CACHE; }
 
 extern inline pte_t pte_rdprotect(pte_t pte) {
 	pte_val(pte) &= ~_PAGE_USER; return pte; }
@@ -301,70 +372,123 @@
  * used to allocate a kernel page table, but are actually identical
  * to the xxx() versions.
  */
-extern inline void pte_free_kernel(pte_t * pte)
+#ifdef __SMP__
+/* Sliiiicck */
+#define pgd_quicklist           (cpu_data[smp_processor_id()].pgd_quick)
+#define pmd_quicklist           ((unsigned long *)0)
+#define pte_quicklist           (cpu_data[smp_processor_id()].pte_quick)
+#define pgtable_cache_size      (cpu_data[smp_processor_id()].pgtable_cache_sz)
+#else
+extern struct pgtable_cache_struct {
+	unsigned long *pgd_cache;
+	unsigned long *pte_cache;
+	unsigned long pgtable_cache_sz;
+} quicklists;
+#define pgd_quicklist (quicklists.pgd_cache)
+#define pmd_quicklist ((unsigned long *)0)
+#define pte_quicklist (quicklists.pte_cache)
+#define pgtable_cache_size (quicklists.pgtable_cache_sz)
+#endif
+
+
+extern __inline__ pgd_t *get_pgd_slow(void)
 {
-	free_page((unsigned long) pte);
+	pgd_t *ret = (pgd_t *)__get_free_page(GFP_KERNEL), *init;
+
+	if (ret) {
+		init = pgd_offset(&init_mm, 0);
+		memset (ret, 0, USER_PTRS_PER_PGD * sizeof(pgd_t));
+		memcpy (ret + USER_PTRS_PER_PGD, init + USER_PTRS_PER_PGD,
+			(PTRS_PER_PGD - USER_PTRS_PER_PGD) * sizeof(pgd_t));
+	}
+	return ret;
 }
 
-extern inline pte_t * pte_alloc_kernel(pmd_t * pmd, unsigned long address)
+extern __inline__ pgd_t *get_pgd_fast(void)
 {
-	address = (address >> PAGE_SHIFT) & (PTRS_PER_PTE - 1);
-	if (pmd_none(*pmd)) {
-		pte_t * page = (pte_t *) get_free_page(GFP_KERNEL);
-		if (pmd_none(*pmd)) {
-			if (page) {
-				pmd_val(*pmd) = (unsigned long) page;
-				return page + address;
-			}
-			pmd_val(*pmd) = (unsigned long) BAD_PAGETABLE;
-			return NULL;
-		}
-		free_page((unsigned long) page);
-	}
-	if (pmd_bad(*pmd)) {
-		printk("Bad pmd in pte_alloc: %08lx\n", pmd_val(*pmd));
-		pmd_val(*pmd) = (unsigned long) BAD_PAGETABLE;
-		return NULL;
-	}
-	return (pte_t *) pmd_page(*pmd) + address;
+        unsigned long *ret;
+
+        if((ret = pgd_quicklist) != NULL) {
+                pgd_quicklist = (unsigned long *)(*ret);
+                ret[0] = ret[1];
+                pgtable_cache_size--;
+        } else
+                ret = (unsigned long *)get_pgd_slow();
+        return (pgd_t *)ret;
 }
 
-/*
- * allocating and freeing a pmd is trivial: the 1-entry pmd is
- * inside the pgd, so has no extra memory associated with it.
- */
-extern inline void pmd_free_kernel(pmd_t * pmd)
+extern __inline__ void free_pgd_fast(pgd_t *pgd)
 {
+        *(unsigned long *)pgd = (unsigned long) pgd_quicklist;
+        pgd_quicklist = (unsigned long *) pgd;
+        pgtable_cache_size++;
 }
 
-extern inline pmd_t * pmd_alloc_kernel(pgd_t * pgd, unsigned long address)
+extern __inline__ void free_pgd_slow(pgd_t *pgd)
 {
-	return (pmd_t *) pgd;
+	free_page((unsigned long)pgd);
+}
+
+extern pte_t *get_pte_slow(pmd_t *pmd, unsigned long address_preadjusted);
+
+extern __inline__ pte_t *get_pte_fast(void)
+{
+        unsigned long *ret;
+
+        if((ret = (unsigned long *)pte_quicklist) != NULL) {
+                pte_quicklist = (unsigned long *)(*ret);
+                ret[0] = ret[1];
+                pgtable_cache_size--;
+        }
+        return (pte_t *)ret;
 }
 
-extern inline void pte_free(pte_t * pte)
+extern __inline__ void free_pte_fast(pte_t *pte)
 {
-	free_page((unsigned long) pte);
+        *(unsigned long *)pte = (unsigned long) pte_quicklist;
+        pte_quicklist = (unsigned long *) pte;
+        pgtable_cache_size++;
 }
 
+extern __inline__ void free_pte_slow(pte_t *pte)
+{
+	free_page((unsigned long)pte);
+}
+
+/* We don't use pmd cache, so this is a dummy routine */
+extern __inline__ pmd_t *get_pmd_fast(void)
+{
+	return (pmd_t *)0;
+}
+
+extern __inline__ void free_pmd_fast(pmd_t *pmd)
+{
+}
+
+extern __inline__ void free_pmd_slow(pmd_t *pmd)
+{
+}
+
+extern void __bad_pte(pmd_t *pmd);
+
+#define pte_free_kernel(pte)    free_pte_fast(pte)
+#define pte_free(pte)           free_pte_fast(pte)
+#define pgd_free(pgd)           free_pgd_fast(pgd)
+#define pgd_alloc()             get_pgd_fast()
+
 extern inline pte_t * pte_alloc(pmd_t * pmd, unsigned long address)
 {
 	address = (address >> PAGE_SHIFT) & (PTRS_PER_PTE - 1);
 	if (pmd_none(*pmd)) {
-		pte_t * page = (pte_t *) get_free_page(GFP_KERNEL);
-		if (pmd_none(*pmd)) {
-			if (page) {
-				pmd_val(*pmd) = (unsigned long) page;
-				return page + address;
-			}
-			pmd_val(*pmd) = (unsigned long) BAD_PAGETABLE;
-			return NULL;
-		}
-		free_page((unsigned long) page);
+		pte_t * page = (pte_t *) get_pte_fast();
+		
+		if (!page)
+			return get_pte_slow(pmd, address);
+		pmd_val(*pmd) = (unsigned long) page;
+		return page + address;
 	}
 	if (pmd_bad(*pmd)) {
-		printk("Bad pmd in pte_alloc: %08lx\n", pmd_val(*pmd));
-		pmd_val(*pmd) = (unsigned long) BAD_PAGETABLE;
+		__bad_pte(pmd);
 		return NULL;
 	}
 	return (pte_t *) pmd_page(*pmd) + address;
@@ -383,18 +507,64 @@
 	return (pmd_t *) pgd;
 }
 
-extern inline void pgd_free(pgd_t * pgd)
-{
-	free_page((unsigned long) pgd);
+#define pmd_free_kernel		pmd_free
+#define pmd_alloc_kernel	pmd_alloc
+#define pte_alloc_kernel	pte_alloc
+
+extern inline void set_pgdir(unsigned long address, pgd_t entry)
+{
+	struct task_struct * p;
+	pgd_t *pgd;
+#ifdef __SMP__
+	int i;
+#endif	
+        
+	read_lock(&tasklist_lock);
+	for_each_task(p) {
+		if (!p->mm)
+			continue;
+		*pgd_offset(p->mm,address) = entry;
+	}
+	read_unlock(&tasklist_lock);
+#ifndef __SMP__
+	for (pgd = (pgd_t *)pgd_quicklist; pgd; pgd = (pgd_t *)*(unsigned long *)pgd)
+		pgd[address >> PGDIR_SHIFT] = entry;
+#else
+	/* To pgd_alloc/pgd_free, one holds master kernel lock and so does our callee, so we can
+	   modify pgd caches of other CPUs as well. -jj */
+	for (i = 0; i < NR_CPUS; i++)
+		for (pgd = (pgd_t *)cpu_data[i].pgd_quick; pgd; pgd = (pgd_t *)*(unsigned long *)pgd)
+			pgd[address >> PGDIR_SHIFT] = entry;
+#endif
 }
 
-extern inline pgd_t * pgd_alloc(void)
+extern pgd_t swapper_pg_dir[1024];
+
+extern __inline__ pte_t *find_pte(struct mm_struct *mm,unsigned long va)
 {
-	return (pgd_t *) get_free_page(GFP_KERNEL);
+	pgd_t *dir;
+	pmd_t *pmd;
+	pte_t *pte;
+
+	va &= PAGE_MASK;
+	
+	dir = pgd_offset( mm, va );
+	if (dir)
+	{
+		pmd = pmd_offset(dir, va & PAGE_MASK);
+		if (pmd && pmd_present(*pmd))
+		{
+			pte = pte_offset(pmd, va);
+			if (pte && pte_present(*pte))
+			{			
+				pte_uncache(*pte);
+				flush_tlb_page(find_vma(mm,va),va);
+			}
+		}
+	}
+	return pte;
 }
 
-extern pgd_t swapper_pg_dir[1024];
-
 /*
  * Page tables may have changed.  We don't need to do anything here
  * as entries are faulted into the hash table by the low-level
@@ -416,5 +586,9 @@
 
 #define module_map      vmalloc
 #define module_unmap    vfree
+#define module_shrink	vshrink
 
+/* Needs to be defined here and not in linux/mm.h, as it is arch dependent */
+#define PageSkip(page)		(0)
+#endif __ASSEMBLY__
 #endif /* _PPC_PGTABLE_H */
diff -u --recursive --new-file v2.1.96/linux/include/asm-ppc/pmu.h linux/include/asm-ppc/pmu.h
--- v2.1.96/linux/include/asm-ppc/pmu.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-ppc/pmu.h	Tue Apr 14 17:34:00 1998
@@ -0,0 +1,42 @@
+/*
+ * Definitions for talking to the PMU.  The PMU is a microcontroller
+ * which controls battery charging and system power on PowerBook 3400
+ * and 2400 models as well as the RTC and various other things.
+ *
+ * Copyright (C) 1998 Paul Mackerras.
+ */
+
+/*
+ * PMU commands
+ */
+#define PMU_BACKLIGHT_CTRL	0x11	/* control backlight */
+#define PMU_ADB_CMD		0x20	/* send ADB packet */
+#define PMU_ADB_POLL_OFF	0x21	/* disable ADB auto-poll */
+#define PMU_WRITE_NVRAM		0x33	/* write non-volatile RAM */
+#define PMU_READ_NVRAM		0x3b	/* read non-volatile RAM */
+#define PMU_SET_RTC		0x30	/* set real-time clock */
+#define PMU_READ_RTC		0x38	/* read real-time clock */
+#define PMU_BACKLIGHT_BRIGHT	0x41	/* set backlight brightness */
+#define PMU_SET_INTR_MASK	0x70	/* set PMU interrupt mask */
+#define PMU_INT_ACK		0x78	/* read interrupt bits */
+#define PMU_SHUTDOWN		0x7e	/* turn power off */
+#define PMU_SLEEP		0x7f	/* put CPU to sleep */
+#define PMU_RESET		0xd0	/* reset CPU */
+
+/* Bits in PMU interrupt and interrupt mask bytes */
+#define PMU_INT_ADB_AUTO	0x04	/* ADB autopoll, when PMU_INT_ADB */
+#define PMU_INT_PCEJECT		0x04	/* PC-card eject buttons */
+#define PMU_INT_SNDBRT		0x08	/* sound/brightness up/down buttons */
+#define PMU_INT_ADB		0x10	/* ADB autopoll or reply data */
+#define PMU_INT_TICK		0x80	/* 1-second tick interrupt */
+
+#ifdef __KERNEL__
+void via_pmu_init(void);
+int pmu_request(struct adb_request *req,
+		void (*done)(struct adb_request *), int nbytes, ...);
+int pmu_send_request(struct adb_request *req);
+void pmu_poll(void);
+
+void pmu_enable_backlight(int on);
+
+#endif	/* __KERNEL */
diff -u --recursive --new-file v2.1.96/linux/include/asm-ppc/processor.h linux/include/asm-ppc/processor.h
--- v2.1.96/linux/include/asm-ppc/processor.h	Fri Jan 16 20:36:39 1998
+++ linux/include/asm-ppc/processor.h	Tue Apr 14 17:34:00 1998
@@ -24,7 +24,11 @@
 #define MSR_RI		(1<<1)		/* Recoverable Exception */
 #define MSR_LE		(1<<0)		/* Little-Endian enable */
 
+#ifdef CONFIG_APUS
+#define MSR_		MSR_ME|MSR_FE0|MSR_FE1|MSR_IP|MSR_RI
+#else
 #define MSR_		MSR_ME|MSR_FE0|MSR_FE1|MSR_RI
+#endif
 #define MSR_KERNEL      MSR_|MSR_IR|MSR_DR
 #define MSR_USER	MSR_KERNEL|MSR_PR|MSR_EE
 
@@ -58,6 +62,8 @@
 #define _MACH_prep     1
 #define _MACH_Pmac     2 /* pmac or pmac clone (non-chrp) */
 #define _MACH_chrp     4 /* chrp machine */
+#define _MACH_mbx      8 /* Motorola MBX board */
+#define _MACH_apus    16 /* amiga with phase5 powerup */
 
 /* see residual.h for these */
 #define _PREP_Motorola 0x01  /* motorola prep */
@@ -117,6 +123,18 @@
 #define IABR	1010	/* Instruction Address Breakpoint */
 #define DEC	22	/* Decrementer */
 #define EAR	282	/* External Address Register */
+#define L2CR	1017    /* PPC 750 L2 control register */
+
+#define THRM1	1020
+#define THRM2	1021
+#define THRM3	1022
+#define THRM1_TIN 0x1
+#define THRM1_TIV 0x2
+#define THRM1_THRES (0x7f<<2)
+#define THRM1_TID (1<<29)
+#define THRM1_TIE (1<<30)
+#define THRM1_V   (1<<31)
+#define THRM3_E   (1<<31)
 
 /* Segment Registers */
 #define SR0	0
@@ -146,18 +164,36 @@
 #ifdef CONFIG_PREP
 #define _machine (_MACH_prep)
 #define is_prep (1)
+#define is_chrp (0)
+#define have_of (0)
 #endif /* CONFIG_PREP */
 
 #ifdef CONFIG_CHRP
 #define _machine (_MACH_chrp)
 #define is_prep (0)
+#define is_chrp (1)
+#define have_of (1)
 #endif /* CONFIG_CHRP */
 
 #ifdef CONFIG_PMAC
 #define _machine (_MACH_Pmac)
 #define is_prep (0)
+#define is_chrp (0)
+#define have_of (1)
 #endif /* CONFIG_PMAC */
 
+#ifdef CONFIG_MBX
+#define _machine (_MACH_mbx)
+#define is_prep (0)
+#define is_chrp (0)
+#define have_of (0)
+#endif /* CONFIG_MBX */
+
+#ifdef CONFIG_APUS
+#define _machine (_MACH_apus)
+#define is_prep (0)
+#endif /* CONFIG_APUS */
+
 #else /* CONFIG_MACH_SPECIFIC */
 
 extern int _machine;
@@ -165,10 +201,13 @@
 /* if we're a prep machine */
 #define is_prep (_machine == _MACH_prep)
 
-#endif /* CONFIG_MACH_SPECIFIC */
+/* if we're a chrp machine */
+#define is_chrp (_machine == _MACH_chrp)
 
 /* if we have openfirmware */
 extern unsigned long have_of;
+#endif /* CONFIG_MACH_SPECIFIC */
+
 /* what kind of prep workstation we are */
 extern int _prep_type;
 
@@ -216,6 +255,7 @@
 	double		fpr[32];	/* Complete floating point set */
 	unsigned long	fpscr_pad;	/* fpr ... fpscr must be contiguous */
 	unsigned long	fpscr;		/* Floating point status */
+	unsigned long	smp_fork_ret;
 };
 
 #define INIT_SP		(sizeof(init_stack) + (unsigned long) &init_stack)
@@ -227,10 +267,14 @@
 	(struct pt_regs *)INIT_SP - 1, /* regs */ \
 	KERNEL_DS, /*fs*/ \
 	0, /* last_syscall */ \
-	{0}, 0, 0 \
+	{0}, 0, 0, 0 \
 }
 
-#define INIT_MMAP { &init_mm, KERNELBASE/*0*/, 0xffffffff/*0x40000000*/, \
+/*
+ * Note: the vm_start and vm_end fields here should *not*
+ * be in kernel space.  (Could vm_end == vm_start perhaps?)
+ */
+#define INIT_MMAP { &init_mm, 0, 0x1000, \
 		      PAGE_SHARED, VM_READ | VM_WRITE | VM_EXEC }
 
 /*
diff -u --recursive --new-file v2.1.96/linux/include/asm-ppc/prom.h linux/include/asm-ppc/prom.h
--- v2.1.96/linux/include/asm-ppc/prom.h	Mon Feb 23 18:12:11 1998
+++ linux/include/asm-ppc/prom.h	Tue Apr 14 17:34:00 1998
@@ -20,6 +20,11 @@
 	unsigned int size;
 };
 
+struct interrupt_info {
+	int	line;
+	int	sense;		/* +ve/-ve logic, edge or level, etc. */
+};
+
 struct reg_property {
 	unsigned int address;
 	unsigned int size;
@@ -46,7 +51,7 @@
 	int	n_addrs;
 	struct	address_range *addrs;
 	int	n_intrs;
-	int	*intrs;
+	struct	interrupt_info *intrs;
 	char	*full_name;
 	struct	property *properties;
 	struct	device_node *parent;
diff -u --recursive --new-file v2.1.96/linux/include/asm-ppc/ptrace.h linux/include/asm-ppc/ptrace.h
--- v2.1.96/linux/include/asm-ppc/ptrace.h	Sat Aug 16 09:51:09 1997
+++ linux/include/asm-ppc/ptrace.h	Tue Apr 14 17:34:00 1998
@@ -28,6 +28,7 @@
 	unsigned long xer;
 	unsigned long ccr;
 	unsigned long mq;	/* 601 only (not used at present) */
+				/* Used on APUS to hold IPL value. */
 	unsigned long trap;	/* Reason for being here */
 	unsigned long dar;	/* Fault registers */
 	unsigned long dsisr;
diff -u --recursive --new-file v2.1.96/linux/include/asm-ppc/smp.h linux/include/asm-ppc/smp.h
--- v2.1.96/linux/include/asm-ppc/smp.h	Mon Jan 12 15:18:13 1998
+++ linux/include/asm-ppc/smp.h	Tue Apr 14 17:34:00 1998
@@ -19,26 +19,34 @@
 struct cpuinfo_PPC {
 	unsigned long loops_per_sec;
 	unsigned long pvr;
+	unsigned long *pgd_quick;
+	unsigned long *pte_quick;
+	unsigned long pgtable_cache_sz;
 };
 
 extern struct cpuinfo_PPC cpu_data[NR_CPUS];
 
-struct klock_info {
-	unsigned char kernel_flag;
+struct klock_info_struct {
+	unsigned long kernel_flag;
 	unsigned char akp;
 };
 
-extern struct klock_info klock_info;
+extern struct klock_info_struct klock_info;
 
-#define KLOCK_HELD       0xff
-#define KLOCK_CLEAR      0x00
+#define KLOCK_HELD       0xffffffff
+#define KLOCK_CLEAR      0x0
 
-#define PROC_CHANGE_PENALTY     1000 /* don't change cpu's for now */
+#define PROC_CHANGE_PENALTY     20
 
 extern __volatile__ int cpu_number_map[NR_CPUS];
-extern __volatile__ int cpu_logical_map[NR_CPUS];
+extern __volatile__ int __cpu_logical_map[NR_CPUS];
 extern unsigned long smp_proc_in_lock[NR_CPUS];
 
+extern __inline__ int cpu_logical_map(int cpu)
+{
+	return __cpu_logical_map[cpu];
+}
+
 extern __inline__ int hard_smp_processor_id(void)
 {
 	int cpuid = 0;
@@ -48,8 +56,17 @@
 
 #define smp_processor_id() (current->processor)
 
+extern void smp_message_pass(int target, int msg, unsigned long data, int wait);
+
 #endif /* __ASSEMBLY__ */
 
+#else /* !(__SMP__) */
+#ifndef __ASSEMBLY__
+extern __inline__ int cpu_logical_map(int cpu)
+{
+	return cpu;
+}
+#endif
 #endif /* !(__SMP__) */
 
 #define NO_PROC_ID               0xFF            /* No processor magic marker */
diff -u --recursive --new-file v2.1.96/linux/include/asm-ppc/socket.h linux/include/asm-ppc/socket.h
--- v2.1.96/linux/include/asm-ppc/socket.h	Mon Jan 12 15:18:13 1998
+++ linux/include/asm-ppc/socket.h	Tue Apr 14 17:34:00 1998
@@ -41,4 +41,8 @@
 
 #define SO_BINDTODEVICE	25
 
+/* Socket filtering */
+#define SO_ATTACH_FILTER	26
+#define SO_DETACH_FILTER	27
+
 #endif /* _ASM_SOCKET_H */
diff -u --recursive --new-file v2.1.96/linux/include/asm-ppc/softirq.h linux/include/asm-ppc/softirq.h
--- v2.1.96/linux/include/asm-ppc/softirq.h	Mon Jan 12 15:18:13 1998
+++ linux/include/asm-ppc/softirq.h	Tue Apr 14 17:34:00 1998
@@ -69,8 +69,8 @@
 }
 
 /* These are for the irq's testing the lock */
-#define softirq_trylock()	(__ppc_bh_counter? 0: ((__ppc_bh_counter=1),1))
-#define softirq_endlock()	(__ppc_bh_counter = 0)
+#define softirq_trylock(cpu)	(__ppc_bh_counter? 0: ((__ppc_bh_counter=1),1))
+#define softirq_endlock(cpu)	(__ppc_bh_counter = 0)
 
 #else /* __SMP__ */
 
@@ -129,7 +129,7 @@
 	spin_unlock_irqrestore(&global_bh_lock, flags);	\
 } while(0)
 
-#define softirq_trylock()					\
+#define softirq_trylock(cpu)					\
 ({								\
 	int ret = 1;						\
 	if(atomic_add_return(1, &__ppc_bh_counter) != 1) {	\
@@ -138,7 +138,7 @@
 	}							\
 	ret;							\
 })
-#define softirq_endlock()	atomic_dec(&__ppc_bh_counter)
+#define softirq_endlock(cpu)	atomic_dec(&__ppc_bh_counter)
 #define clear_active_bhs(mask)				\
 do {	unsigned long flags;				\
 	spin_lock_irqsave(&global_bh_lock, flags);	\
diff -u --recursive --new-file v2.1.96/linux/include/asm-ppc/spinlock.h linux/include/asm-ppc/spinlock.h
--- v2.1.96/linux/include/asm-ppc/spinlock.h	Mon Jan 12 15:18:13 1998
+++ linux/include/asm-ppc/spinlock.h	Tue Apr 14 17:34:00 1998
@@ -57,26 +57,23 @@
  * We make no fairness assumptions. They have a cost.
  */
 
-struct _spinlock_debug {
+typedef struct {
 	volatile unsigned long lock;
 	volatile unsigned long owner_pc;
-};
+	volatile unsigned long owner_cpu;
+} spinlock_t;
 
-typedef struct _spinlock_debug spinlock_t;
-
-#define SPIN_LOCK_UNLOCKED { 0, 0 }
-
-#define SPIN_LOCK_UNLOCKED	{ 0, 0 }
-#define spin_lock_init(lp)	do { (lp)->owner_pc = 0; (lp)->lock = 0; } while(0)
+#define SPIN_LOCK_UNLOCKED	{ 0, 0, 0 }
+#define spin_lock_init(lp) \
+do { spinlock_t *p = (lp); p->owner_pc = p->owner_cpu = p->lock = 0; } while(0)
 #define spin_unlock_wait(lp)	do { barrier(); } while((lp)->lock)
 
 extern void _spin_lock(spinlock_t *lock);
 extern void _spin_unlock(spinlock_t *lock);
+extern int spin_trylock(spinlock_t *lock);
 
 #define spin_lock(lp)			_spin_lock(lp)
 #define spin_unlock(lp)			_spin_unlock(lp)
-
-#define spin_trylock(l) (!test_and_set_bit(0, &((l)->lock) ))
 
 #define spin_lock_irq(lock) \
 	do { __cli(); spin_lock(lock); } while (0)
diff -u --recursive --new-file v2.1.96/linux/include/asm-ppc/stat.h linux/include/asm-ppc/stat.h
--- v2.1.96/linux/include/asm-ppc/stat.h	Wed Dec 18 00:54:10 1996
+++ linux/include/asm-ppc/stat.h	Tue Apr 14 17:34:00 1998
@@ -36,4 +36,41 @@
 	unsigned long  	__unused4;
 	unsigned long  	__unused5;
 };
+
+typedef struct {
+	unsigned int	major;
+	unsigned int	minor;
+} __new_dev_t;
+
+struct stat64 {
+	__new_dev_t	st_dev;
+	__u64		st_ino;
+	unsigned int	st_mode;
+	unsigned int	st_nlink;
+	unsigned int	st_uid;
+	unsigned int	st_gid;
+	__new_dev_t	st_rdev;
+	__s64		st_size;
+	__u64		st_blocks;
+	unsigned long	st_atime;
+	unsigned long	__unused1;
+	unsigned long	st_mtime;
+	unsigned long	__unused2;
+	unsigned long	st_ctime;
+	unsigned long	__unused3;
+	unsigned long	st_blksize;
+	unsigned long	__unused4;
+};
+
+#define __XSTAT_VER_1		1
+#define __XSTAT_VER_2		2
+#define __XSTAT_VER_MASK	0xff
+
+#define __XSTAT_VER_XSTAT	0x000
+#define __XSTAT_VER_LXSTAT	0x100
+#define __XSTAT_VER_FXSTAT	0x200
+#define __XSTAT_VER_TYPEMASK	0xff00
+
+#define __XMKNOD_VER_1		1
+
 #endif
diff -u --recursive --new-file v2.1.96/linux/include/asm-ppc/system.h linux/include/asm-ppc/system.h
--- v2.1.96/linux/include/asm-ppc/system.h	Mon Jan 12 15:18:13 1998
+++ linux/include/asm-ppc/system.h	Tue Apr 14 17:34:00 1998
@@ -72,14 +72,14 @@
 extern void flush_instruction_cache(void);
 extern void hard_reset_now(void);
 extern void poweroff_now(void);
-/*extern void note_bootable_part(kdev_t, int);*/
-extern int sd_find_target(void *, int);
 extern int _get_PVR(void);
+extern long _get_L2CR(void);
 extern void via_cuda_init(void);
 extern void pmac_nvram_init(void);
 extern void read_rtc_time(void);
 extern void pmac_find_display(void);
 extern void giveup_fpu(void);
+extern void smp_giveup_fpu(struct task_struct *);
 extern void cvt_fd(float *from, double *to);
 extern void cvt_df(double *from, float *to);
 
@@ -102,6 +102,7 @@
 #define sti()	__sti()
 #define save_flags(flags)	__save_flags(flags)
 #define restore_flags(flags)	__restore_flags(flags)
+#define save_and_cli(flags)	__save_and_cli(flags)
 
 #else /* __SMP__ */
 
diff -u --recursive --new-file v2.1.96/linux/include/asm-ppc/unistd.h linux/include/asm-ppc/unistd.h
--- v2.1.96/linux/include/asm-ppc/unistd.h	Mon Jan 12 15:18:13 1998
+++ linux/include/asm-ppc/unistd.h	Tue Apr 14 17:34:00 1998
@@ -177,6 +177,8 @@
 #define __NR_setresgid		169
 #define __NR_getresgid		170
 #define __NR_prctl		171
+#define __NR_xstat		172
+#define __NR_xmknod		173
 
 #define __NR(n)	#n
 #define __do_syscall(n) \
@@ -212,10 +214,11 @@
 #ifdef __KERNEL_SYSCALLS__
 
 /*
- * Forking from kernel space will result in NO COPY ON WRITE (!!!),
- * until an execve is executed. This is no problem, but for the stack.
- * This is handled by not letting main() use the stack at all after
- * fork().  On the PowerPC, this means we can only call leaf functions.
+ * Forking from kernel space will result in the child getting a new,
+ * empty kernel stack area.  Thus the child cannot access automatic
+ * variables set in the parent unless they are in registers, and the
+ * procedure where the fork was done cannot return to its caller in
+ * the child.
  */
 
 /*
@@ -241,6 +244,9 @@
 int open(const char *, int, int);
 int close(int);
 pid_t waitpid(pid_t, int *, int);
+pid_t fork(void);
+void _exit(int);
+int delete_module(const char *);
 
 static inline pid_t wait(int * wait_stat) 
 {
diff -u --recursive --new-file v2.1.96/linux/include/asm-ppc/xstat.h linux/include/asm-ppc/xstat.h
--- v2.1.96/linux/include/asm-ppc/xstat.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-ppc/xstat.h	Tue Apr 14 17:34:00 1998
@@ -0,0 +1,35 @@
+/* $Id: xstat.h,v 1.1 1998/02/06 12:51:55 jj Exp $
+ * xstat.h: sys_xstat/xmknod architecture dependent stuff.
+ *
+ * Copyright (C) 1998  Jakub Jelinek  (jj@sunsite.mff.cuni.cz)
+ */
+ 
+extern __inline__ int cp_xstat(struct inode *inode, struct stat64 *s, unsigned long blocks, int blksize)
+{
+	struct stat64 tmp;
+	
+	memset (&tmp, 0, sizeof(tmp));
+	tmp.st_dev.major = MAJOR(inode->i_dev);
+	tmp.st_dev.minor = MINOR(inode->i_dev);
+	tmp.st_ino = inode->i_ino;
+	tmp.st_mode = inode->i_mode;
+	tmp.st_nlink = inode->i_nlink;
+	tmp.st_uid = inode->i_uid;
+	tmp.st_gid = inode->i_gid;
+	tmp.st_rdev.major = MAJOR(inode->i_rdev);
+	tmp.st_rdev.minor = MINOR(inode->i_rdev);
+	tmp.st_size = inode->i_size;
+	tmp.st_blksize = blksize;
+	tmp.st_blocks = blocks;
+	tmp.st_atime = inode->i_atime;
+	tmp.st_mtime = inode->i_mtime;
+	tmp.st_ctime = inode->i_ctime;
+	return copy_to_user(s,&tmp,sizeof(tmp));
+}
+
+extern __inline__ int get_user_new_dev_t(kdev_t *kdev, __new_dev_t *udev) {
+	__new_dev_t ndev;
+	if (copy_from_user (&ndev, udev, sizeof(__new_dev_t))) return -EFAULT;
+	*kdev = MKDEV(ndev.major, ndev.minor);
+	return 0;
+}
diff -u --recursive --new-file v2.1.96/linux/include/asm-sparc/a.out.h linux/include/asm-sparc/a.out.h
--- v2.1.96/linux/include/asm-sparc/a.out.h	Fri Dec 13 01:37:39 1996
+++ linux/include/asm-sparc/a.out.h	Tue Apr 14 17:44:23 1998
@@ -1,4 +1,4 @@
-/* $Id: a.out.h,v 1.11 1996/12/03 08:44:56 jj Exp $ */
+/* $Id: a.out.h,v 1.12 1998/02/05 14:20:00 jj Exp $ */
 #ifndef __SPARC_A_OUT_H__
 #define __SPARC_A_OUT_H__
 
@@ -89,10 +89,31 @@
 
 #ifdef __KERNEL__
 
+#include <linux/config.h>
+#include <asm/btfixup.h>
+
+#ifdef CONFIG_SUN4
+
+#define STACK_TOP	(0xefffe000UL)
+
+#else
+
 extern unsigned long	stack_top;
 
-#define STACK_TOP	(stack_top)
+#  ifndef MODULE
+
+    BTFIXUPDEF_SETHI_INIT(stack_top,0xeffff000)
+
+#    define STACK_TOP	((unsigned long)BTFIXUP_SETHI(stack_top))
+
+#  else /* MODULE */
+
+#    define STACK_TOP	(stack_top)
+
+#  endif /* MODULE */
+
+#endif /* !CONFIG_SUN4 */
 
-#endif
+#endif /* __KERNEL__ */
 
 #endif /* __SPARC_A_OUT_H__ */
diff -u --recursive --new-file v2.1.96/linux/include/asm-sparc/asi.h linux/include/asm-sparc/asi.h
--- v2.1.96/linux/include/asm-sparc/asi.h	Mon Jul  7 08:18:55 1997
+++ linux/include/asm-sparc/asi.h	Tue Apr 14 17:44:23 1998
@@ -1,4 +1,4 @@
-/* $Id: asi.h,v 1.17 1997/06/24 15:48:10 jj Exp $ */
+/* $Id: asi.h,v 1.18 1998/03/09 14:04:46 jj Exp $ */
 #ifndef _SPARC_ASI_H
 #define _SPARC_ASI_H
 
@@ -102,6 +102,9 @@
 #define ASI_M_DC_FLCLEAR   0x37
 
 #define ASI_M_DCDR         0x39   /* Data Cache Diagnostics Register rw, ss */
+
+#define ASI_M_VIKING_TMP1  0x40	  /* Emulation temporary 1 on Viking */
+#define ASI_M_VIKING_TMP2  0x41	  /* Emulation temporary 2 on Viking */
 
 #define ASI_M_ACTION       0x4c   /* Breakpoint Action Register (GNU/Viking) */
 
diff -u --recursive --new-file v2.1.96/linux/include/asm-sparc/asm_offsets.h linux/include/asm-sparc/asm_offsets.h
--- v2.1.96/linux/include/asm-sparc/asm_offsets.h	Mon Jan 12 15:15:45 1998
+++ linux/include/asm-sparc/asm_offsets.h	Tue Apr 14 17:44:23 1998
@@ -2,22 +2,291 @@
 #ifndef __ASM_OFFSETS_H__
 #define __ASM_OFFSETS_H__
 
+#ifndef __SMP__
+
 #define AOFF_task_state	0x00000000
 #define ASIZ_task_state	0x00000004
-#define AOFF_task_counter	0x00000004
+#define AOFF_task_flags	0x00000004
+#define ASIZ_task_flags	0x00000004
+#define AOFF_task_sigpending	0x00000008
+#define ASIZ_task_sigpending	0x00000004
+#define AOFF_task_addr_limit	0x0000000c
+#define ASIZ_task_addr_limit	0x00000004
+#define AOFF_task_exec_domain	0x00000010
+#define ASIZ_task_exec_domain	0x00000004
+#define AOFF_task_debugreg	0x00000014
+#define ASIZ_task_debugreg	0x00000020
+#define AOFF_task_counter	0x00000034
 #define ASIZ_task_counter	0x00000004
-#define AOFF_task_priority	0x00000008
+#define AOFF_task_priority	0x00000038
 #define ASIZ_task_priority	0x00000004
-#define AOFF_task_flags	0x0000000c
+#define AOFF_task_binfmt	0x0000003c
+#define ASIZ_task_binfmt	0x00000004
+#define AOFF_task_next_task	0x00000040
+#define ASIZ_task_next_task	0x00000004
+#define AOFF_task_prev_task	0x00000044
+#define ASIZ_task_prev_task	0x00000004
+#define AOFF_task_next_run	0x00000048
+#define ASIZ_task_next_run	0x00000004
+#define AOFF_task_prev_run	0x0000004c
+#define ASIZ_task_prev_run	0x00000004
+#define AOFF_task_exit_code	0x00000050
+#define ASIZ_task_exit_code	0x00000004
+#define AOFF_task_exit_signal	0x00000054
+#define ASIZ_task_exit_signal	0x00000004
+#define AOFF_task_pdeath_signal	0x00000058
+#define ASIZ_task_pdeath_signal	0x00000004
+#define AOFF_task_personality	0x0000005c
+#define ASIZ_task_personality	0x00000004
+#define AOFF_task_pid	0x00000064
+#define ASIZ_task_pid	0x00000004
+#define AOFF_task_pgrp	0x00000068
+#define ASIZ_task_pgrp	0x00000004
+#define AOFF_task_tty_old_pgrp	0x0000006c
+#define ASIZ_task_tty_old_pgrp	0x00000004
+#define AOFF_task_session	0x00000070
+#define ASIZ_task_session	0x00000004
+#define AOFF_task_leader	0x00000074
+#define ASIZ_task_leader	0x00000004
+#define AOFF_task_ngroups	0x00000078
+#define ASIZ_task_ngroups	0x00000004
+#define AOFF_task_groups	0x0000007c
+#define ASIZ_task_groups	0x00000040
+#define AOFF_task_p_opptr	0x000000bc
+#define ASIZ_task_p_opptr	0x00000004
+#define AOFF_task_p_pptr	0x000000c0
+#define ASIZ_task_p_pptr	0x00000004
+#define AOFF_task_p_cptr	0x000000c4
+#define ASIZ_task_p_cptr	0x00000004
+#define AOFF_task_p_ysptr	0x000000c8
+#define ASIZ_task_p_ysptr	0x00000004
+#define AOFF_task_p_osptr	0x000000cc
+#define ASIZ_task_p_osptr	0x00000004
+#define AOFF_task_pidhash_next	0x000000d0
+#define ASIZ_task_pidhash_next	0x00000004
+#define AOFF_task_pidhash_pprev	0x000000d4
+#define ASIZ_task_pidhash_pprev	0x00000004
+#define AOFF_task_tarray_ptr	0x000000d8
+#define ASIZ_task_tarray_ptr	0x00000004
+#define AOFF_task_wait_chldexit	0x000000dc
+#define ASIZ_task_wait_chldexit	0x00000004
+#define AOFF_task_uid	0x000000e0
+#define ASIZ_task_uid	0x00000002
+#define AOFF_task_euid	0x000000e2
+#define ASIZ_task_euid	0x00000002
+#define AOFF_task_suid	0x000000e4
+#define ASIZ_task_suid	0x00000002
+#define AOFF_task_fsuid	0x000000e6
+#define ASIZ_task_fsuid	0x00000002
+#define AOFF_task_gid	0x000000e8
+#define ASIZ_task_gid	0x00000002
+#define AOFF_task_egid	0x000000ea
+#define ASIZ_task_egid	0x00000002
+#define AOFF_task_sgid	0x000000ec
+#define ASIZ_task_sgid	0x00000002
+#define AOFF_task_fsgid	0x000000ee
+#define ASIZ_task_fsgid	0x00000002
+#define AOFF_task_timeout	0x000000f0
+#define ASIZ_task_timeout	0x00000004
+#define AOFF_task_policy	0x000000f4
+#define ASIZ_task_policy	0x00000004
+#define AOFF_task_rt_priority	0x000000f8
+#define ASIZ_task_rt_priority	0x00000004
+#define AOFF_task_it_real_value	0x000000fc
+#define ASIZ_task_it_real_value	0x00000004
+#define AOFF_task_it_prof_value	0x00000100
+#define ASIZ_task_it_prof_value	0x00000004
+#define AOFF_task_it_virt_value	0x00000104
+#define ASIZ_task_it_virt_value	0x00000004
+#define AOFF_task_it_real_incr	0x00000108
+#define ASIZ_task_it_real_incr	0x00000004
+#define AOFF_task_it_prof_incr	0x0000010c
+#define ASIZ_task_it_prof_incr	0x00000004
+#define AOFF_task_it_virt_incr	0x00000110
+#define ASIZ_task_it_virt_incr	0x00000004
+#define AOFF_task_real_timer	0x00000114
+#define ASIZ_task_real_timer	0x00000014
+#define AOFF_task_times	0x00000128
+#define ASIZ_task_times	0x00000010
+#define AOFF_task_start_time	0x00000138
+#define ASIZ_task_start_time	0x00000004
+#define AOFF_task_per_cpu_utime	0x0000013c
+#define ASIZ_task_per_cpu_utime	0x00000004
+#define AOFF_task_min_flt	0x00000144
+#define ASIZ_task_min_flt	0x00000004
+#define AOFF_task_maj_flt	0x00000148
+#define ASIZ_task_maj_flt	0x00000004
+#define AOFF_task_nswap	0x0000014c
+#define ASIZ_task_nswap	0x00000004
+#define AOFF_task_cmin_flt	0x00000150
+#define ASIZ_task_cmin_flt	0x00000004
+#define AOFF_task_cmaj_flt	0x00000154
+#define ASIZ_task_cmaj_flt	0x00000004
+#define AOFF_task_cnswap	0x00000158
+#define ASIZ_task_cnswap	0x00000004
+#define AOFF_task_swap_address	0x00000160
+#define ASIZ_task_swap_address	0x00000004
+#define AOFF_task_old_maj_flt	0x00000164
+#define ASIZ_task_old_maj_flt	0x00000004
+#define AOFF_task_dec_flt	0x00000168
+#define ASIZ_task_dec_flt	0x00000004
+#define AOFF_task_swap_cnt	0x0000016c
+#define ASIZ_task_swap_cnt	0x00000004
+#define AOFF_task_rlim	0x00000170
+#define ASIZ_task_rlim	0x00000050
+#define AOFF_task_used_math	0x000001c0
+#define ASIZ_task_used_math	0x00000002
+#define AOFF_task_io_usage	0x000001c4
+#define ASIZ_task_io_usage	0x00000004
+#define AOFF_task_comm	0x000001c8
+#define ASIZ_task_comm	0x00000010
+#define AOFF_task_link_count	0x000001d8
+#define ASIZ_task_link_count	0x00000004
+#define AOFF_task_tty	0x000001dc
+#define ASIZ_task_tty	0x00000004
+#define AOFF_task_semundo	0x000001e0
+#define ASIZ_task_semundo	0x00000004
+#define AOFF_task_semsleeping	0x000001e4
+#define ASIZ_task_semsleeping	0x00000004
+#define AOFF_task_ldt	0x000001e8
+#define ASIZ_task_ldt	0x00000004
+#define AOFF_task_tss	0x000001f0
+#define ASIZ_task_tss	0x00000390
+#define AOFF_task_fs	0x00000580
+#define ASIZ_task_fs	0x00000004
+#define AOFF_task_files	0x00000584
+#define ASIZ_task_files	0x00000004
+#define AOFF_task_mm	0x00000588
+#define ASIZ_task_mm	0x00000004
+#define AOFF_task_sig	0x0000058c
+#define ASIZ_task_sig	0x00000004
+#define AOFF_task_signal	0x00000590
+#define ASIZ_task_signal	0x00000008
+#define AOFF_task_blocked	0x00000598
+#define ASIZ_task_blocked	0x00000008
+#define AOFF_task_sigqueue	0x000005a0
+#define ASIZ_task_sigqueue	0x00000004
+#define AOFF_task_sigqueue_tail	0x000005a4
+#define ASIZ_task_sigqueue_tail	0x00000004
+#define AOFF_task_has_cpu	0x000005a8
+#define ASIZ_task_has_cpu	0x00000004
+#define AOFF_task_processor	0x000005ac
+#define ASIZ_task_processor	0x00000004
+#define AOFF_task_last_processor	0x000005b0
+#define ASIZ_task_last_processor	0x00000004
+#define AOFF_task_lock_depth	0x000005b4
+#define ASIZ_task_lock_depth	0x00000004
+#define AOFF_task_sigmask_lock	0x000005b8
+#define ASIZ_task_sigmask_lock	0x00000000
+#define AOFF_mm_mmap	0x00000000
+#define ASIZ_mm_mmap	0x00000004
+#define AOFF_mm_mmap_cache	0x00000004
+#define ASIZ_mm_mmap_cache	0x00000004
+#define AOFF_mm_pgd	0x00000008
+#define ASIZ_mm_pgd	0x00000004
+#define AOFF_mm_count	0x0000000c
+#define ASIZ_mm_count	0x00000004
+#define AOFF_mm_map_count	0x00000010
+#define ASIZ_mm_map_count	0x00000004
+#define AOFF_mm_mmap_sem	0x00000014
+#define ASIZ_mm_mmap_sem	0x0000000c
+#define AOFF_mm_context	0x00000020
+#define ASIZ_mm_context	0x00000004
+#define AOFF_mm_start_code	0x00000024
+#define ASIZ_mm_start_code	0x00000004
+#define AOFF_mm_end_code	0x00000028
+#define ASIZ_mm_end_code	0x00000004
+#define AOFF_mm_start_data	0x0000002c
+#define ASIZ_mm_start_data	0x00000004
+#define AOFF_mm_end_data	0x00000030
+#define ASIZ_mm_end_data	0x00000004
+#define AOFF_mm_start_brk	0x00000034
+#define ASIZ_mm_start_brk	0x00000004
+#define AOFF_mm_brk	0x00000038
+#define ASIZ_mm_brk	0x00000004
+#define AOFF_mm_start_stack	0x0000003c
+#define ASIZ_mm_start_stack	0x00000004
+#define AOFF_mm_arg_start	0x00000040
+#define ASIZ_mm_arg_start	0x00000004
+#define AOFF_mm_arg_end	0x00000044
+#define ASIZ_mm_arg_end	0x00000004
+#define AOFF_mm_env_start	0x00000048
+#define ASIZ_mm_env_start	0x00000004
+#define AOFF_mm_env_end	0x0000004c
+#define ASIZ_mm_env_end	0x00000004
+#define AOFF_mm_rss	0x00000050
+#define ASIZ_mm_rss	0x00000004
+#define AOFF_mm_total_vm	0x00000054
+#define ASIZ_mm_total_vm	0x00000004
+#define AOFF_mm_locked_vm	0x00000058
+#define ASIZ_mm_locked_vm	0x00000004
+#define AOFF_mm_def_flags	0x0000005c
+#define ASIZ_mm_def_flags	0x00000004
+#define AOFF_mm_cpu_vm_mask	0x00000060
+#define ASIZ_mm_cpu_vm_mask	0x00000004
+#define AOFF_thread_uwinmask	0x00000000
+#define ASIZ_thread_uwinmask	0x00000004
+#define AOFF_thread_kregs	0x00000004
+#define ASIZ_thread_kregs	0x00000004
+#define AOFF_thread_sig_address	0x00000008
+#define ASIZ_thread_sig_address	0x00000004
+#define AOFF_thread_sig_desc	0x0000000c
+#define ASIZ_thread_sig_desc	0x00000004
+#define AOFF_thread_ksp	0x00000010
+#define ASIZ_thread_ksp	0x00000004
+#define AOFF_thread_kpc	0x00000014
+#define ASIZ_thread_kpc	0x00000004
+#define AOFF_thread_kpsr	0x00000018
+#define ASIZ_thread_kpsr	0x00000004
+#define AOFF_thread_kwim	0x0000001c
+#define ASIZ_thread_kwim	0x00000004
+#define AOFF_thread_fork_kpsr	0x00000020
+#define ASIZ_thread_fork_kpsr	0x00000004
+#define AOFF_thread_fork_kwim	0x00000024
+#define ASIZ_thread_fork_kwim	0x00000004
+#define AOFF_thread_reg_window	0x00000028
+#define ASIZ_thread_reg_window	0x00000200
+#define AOFF_thread_rwbuf_stkptrs	0x00000228
+#define ASIZ_thread_rwbuf_stkptrs	0x00000020
+#define AOFF_thread_w_saved	0x00000248
+#define ASIZ_thread_w_saved	0x00000004
+#define AOFF_thread_float_regs	0x00000250
+#define ASIZ_thread_float_regs	0x00000080
+#define AOFF_thread_fsr	0x000002d0
+#define ASIZ_thread_fsr	0x00000004
+#define AOFF_thread_fpqdepth	0x000002d4
+#define ASIZ_thread_fpqdepth	0x00000004
+#define AOFF_thread_fpqueue	0x000002d8
+#define ASIZ_thread_fpqueue	0x00000080
+#define AOFF_thread_sstk_info	0x00000358
+#define ASIZ_thread_sstk_info	0x00000008
+#define AOFF_thread_flags	0x00000360
+#define ASIZ_thread_flags	0x00000004
+#define AOFF_thread_current_ds	0x00000364
+#define ASIZ_thread_current_ds	0x00000004
+#define AOFF_thread_core_exec	0x00000368
+#define ASIZ_thread_core_exec	0x00000020
+#define AOFF_thread_new_signal	0x00000388
+#define ASIZ_thread_new_signal	0x00000004
+
+#else /* __SMP__ */
+
+#define AOFF_task_state	0x00000000
+#define ASIZ_task_state	0x00000004
+#define AOFF_task_flags	0x00000004
 #define ASIZ_task_flags	0x00000004
-#define AOFF_task_addr_limit	0x00000010
-#define ASIZ_task_addr_limit	0x00000004
-#define AOFF_task_sigpending	0x00000014
+#define AOFF_task_sigpending	0x00000008
 #define ASIZ_task_sigpending	0x00000004
-#define AOFF_task_debugreg	0x00000018
-#define ASIZ_task_debugreg	0x00000020
-#define AOFF_task_exec_domain	0x00000038
+#define AOFF_task_addr_limit	0x0000000c
+#define ASIZ_task_addr_limit	0x00000004
+#define AOFF_task_exec_domain	0x00000010
 #define ASIZ_task_exec_domain	0x00000004
+#define AOFF_task_debugreg	0x00000014
+#define ASIZ_task_debugreg	0x00000020
+#define AOFF_task_counter	0x00000034
+#define ASIZ_task_counter	0x00000004
+#define AOFF_task_priority	0x00000038
+#define ASIZ_task_priority	0x00000004
 #define AOFF_task_binfmt	0x0000003c
 #define ASIZ_task_binfmt	0x00000004
 #define AOFF_task_next_task	0x00000040
@@ -108,72 +377,74 @@
 #define ASIZ_task_times	0x00000010
 #define AOFF_task_start_time	0x00000138
 #define ASIZ_task_start_time	0x00000004
-#define AOFF_task_min_flt	0x0000013c
+#define AOFF_task_per_cpu_utime	0x0000013c
+#define ASIZ_task_per_cpu_utime	0x00000080
+#define AOFF_task_min_flt	0x0000023c
 #define ASIZ_task_min_flt	0x00000004
-#define AOFF_task_maj_flt	0x00000140
+#define AOFF_task_maj_flt	0x00000240
 #define ASIZ_task_maj_flt	0x00000004
-#define AOFF_task_nswap	0x00000144
+#define AOFF_task_nswap	0x00000244
 #define ASIZ_task_nswap	0x00000004
-#define AOFF_task_cmin_flt	0x00000148
+#define AOFF_task_cmin_flt	0x00000248
 #define ASIZ_task_cmin_flt	0x00000004
-#define AOFF_task_cmaj_flt	0x0000014c
+#define AOFF_task_cmaj_flt	0x0000024c
 #define ASIZ_task_cmaj_flt	0x00000004
-#define AOFF_task_cnswap	0x00000150
+#define AOFF_task_cnswap	0x00000250
 #define ASIZ_task_cnswap	0x00000004
-#define AOFF_task_swap_address	0x00000158
+#define AOFF_task_swap_address	0x00000258
 #define ASIZ_task_swap_address	0x00000004
-#define AOFF_task_old_maj_flt	0x0000015c
+#define AOFF_task_old_maj_flt	0x0000025c
 #define ASIZ_task_old_maj_flt	0x00000004
-#define AOFF_task_dec_flt	0x00000160
+#define AOFF_task_dec_flt	0x00000260
 #define ASIZ_task_dec_flt	0x00000004
-#define AOFF_task_swap_cnt	0x00000164
+#define AOFF_task_swap_cnt	0x00000264
 #define ASIZ_task_swap_cnt	0x00000004
-#define AOFF_task_rlim	0x00000168
+#define AOFF_task_rlim	0x00000268
 #define ASIZ_task_rlim	0x00000050
-#define AOFF_task_used_math	0x000001b8
+#define AOFF_task_used_math	0x000002b8
 #define ASIZ_task_used_math	0x00000002
-#define AOFF_task_io_usage	0x000001bc
+#define AOFF_task_io_usage	0x000002bc
 #define ASIZ_task_io_usage	0x00000004
-#define AOFF_task_comm	0x000001c0
+#define AOFF_task_comm	0x000002c0
 #define ASIZ_task_comm	0x00000010
-#define AOFF_task_link_count	0x000001d0
+#define AOFF_task_link_count	0x000002d0
 #define ASIZ_task_link_count	0x00000004
-#define AOFF_task_tty	0x000001d4
+#define AOFF_task_tty	0x000002d4
 #define ASIZ_task_tty	0x00000004
-#define AOFF_task_semundo	0x000001d8
+#define AOFF_task_semundo	0x000002d8
 #define ASIZ_task_semundo	0x00000004
-#define AOFF_task_semsleeping	0x000001dc
+#define AOFF_task_semsleeping	0x000002dc
 #define ASIZ_task_semsleeping	0x00000004
-#define AOFF_task_ldt	0x000001e0
+#define AOFF_task_ldt	0x000002e0
 #define ASIZ_task_ldt	0x00000004
-#define AOFF_task_tss	0x000001e8
+#define AOFF_task_tss	0x000002e8
 #define ASIZ_task_tss	0x00000390
-#define AOFF_task_fs	0x00000578
+#define AOFF_task_fs	0x00000678
 #define ASIZ_task_fs	0x00000004
-#define AOFF_task_files	0x0000057c
+#define AOFF_task_files	0x0000067c
 #define ASIZ_task_files	0x00000004
-#define AOFF_task_mm	0x00000580
+#define AOFF_task_mm	0x00000680
 #define ASIZ_task_mm	0x00000004
-#define AOFF_task_sig	0x00000584
+#define AOFF_task_sig	0x00000684
 #define ASIZ_task_sig	0x00000004
-#define AOFF_task_signal	0x00000588
+#define AOFF_task_signal	0x00000688
 #define ASIZ_task_signal	0x00000008
-#define AOFF_task_blocked	0x00000590
+#define AOFF_task_blocked	0x00000690
 #define ASIZ_task_blocked	0x00000008
-#define AOFF_task_sigqueue	0x00000598
+#define AOFF_task_sigqueue	0x00000698
 #define ASIZ_task_sigqueue	0x00000004
-#define AOFF_task_sigqueue_tail	0x0000059c
+#define AOFF_task_sigqueue_tail	0x0000069c
 #define ASIZ_task_sigqueue_tail	0x00000004
-#define AOFF_task_has_cpu	0x000005a0
+#define AOFF_task_has_cpu	0x000006a0
 #define ASIZ_task_has_cpu	0x00000004
-#define AOFF_task_processor	0x000005a4
+#define AOFF_task_processor	0x000006a4
 #define ASIZ_task_processor	0x00000004
-#define AOFF_task_last_processor	0x000005a8
+#define AOFF_task_last_processor	0x000006a8
 #define ASIZ_task_last_processor	0x00000004
-#define AOFF_task_lock_depth	0x000005ac
+#define AOFF_task_lock_depth	0x000006ac
 #define ASIZ_task_lock_depth	0x00000004
-#define AOFF_task_sigmask_lock	0x000005b0
-#define ASIZ_task_sigmask_lock	0x00000000
+#define AOFF_task_sigmask_lock	0x000006b0
+#define ASIZ_task_sigmask_lock	0x00000001
 #define AOFF_mm_mmap	0x00000000
 #define ASIZ_mm_mmap	0x00000004
 #define AOFF_mm_mmap_cache	0x00000004
@@ -182,26 +453,26 @@
 #define ASIZ_mm_pgd	0x00000004
 #define AOFF_mm_count	0x0000000c
 #define ASIZ_mm_count	0x00000004
-#define AOFF_mm_mmap_sem	0x00000010
+#define AOFF_mm_map_count	0x00000010
+#define ASIZ_mm_map_count	0x00000004
+#define AOFF_mm_mmap_sem	0x00000014
 #define ASIZ_mm_mmap_sem	0x0000000c
-#define AOFF_mm_context	0x0000001c
+#define AOFF_mm_context	0x00000020
 #define ASIZ_mm_context	0x00000004
-#define AOFF_mm_start_code	0x00000020
+#define AOFF_mm_start_code	0x00000024
 #define ASIZ_mm_start_code	0x00000004
-#define AOFF_mm_end_code	0x00000024
+#define AOFF_mm_end_code	0x00000028
 #define ASIZ_mm_end_code	0x00000004
-#define AOFF_mm_start_data	0x00000028
+#define AOFF_mm_start_data	0x0000002c
 #define ASIZ_mm_start_data	0x00000004
-#define AOFF_mm_end_data	0x0000002c
+#define AOFF_mm_end_data	0x00000030
 #define ASIZ_mm_end_data	0x00000004
-#define AOFF_mm_start_brk	0x00000030
+#define AOFF_mm_start_brk	0x00000034
 #define ASIZ_mm_start_brk	0x00000004
-#define AOFF_mm_brk	0x00000034
+#define AOFF_mm_brk	0x00000038
 #define ASIZ_mm_brk	0x00000004
-#define AOFF_mm_start_stack	0x00000038
+#define AOFF_mm_start_stack	0x0000003c
 #define ASIZ_mm_start_stack	0x00000004
-#define AOFF_mm_start_mmap	0x0000003c
-#define ASIZ_mm_start_mmap	0x00000004
 #define AOFF_mm_arg_start	0x00000040
 #define ASIZ_mm_arg_start	0x00000004
 #define AOFF_mm_arg_end	0x00000044
@@ -264,5 +535,7 @@
 #define ASIZ_thread_core_exec	0x00000020
 #define AOFF_thread_new_signal	0x00000388
 #define ASIZ_thread_new_signal	0x00000004
+
+#endif /* __SMP__ */
 
 #endif /* __ASM_OFFSETS_H__ */
diff -u --recursive --new-file v2.1.96/linux/include/asm-sparc/asmmacro.h linux/include/asm-sparc/asmmacro.h
--- v2.1.96/linux/include/asm-sparc/asmmacro.h	Tue May 13 22:41:17 1997
+++ linux/include/asm-sparc/asmmacro.h	Tue Apr 14 17:44:23 1998
@@ -6,11 +6,24 @@
 #ifndef _SPARC_ASMMACRO_H
 #define _SPARC_ASMMACRO_H
 
-#define GET_PROCESSOR_ID(reg) \
+#include <linux/config.h>
+#include <asm/btfixup.h>
+#include <asm/asi.h>
+
+#define GET_PROCESSOR4M_ID(reg) \
 	rd	%tbr, %reg; \
 	srl	%reg, 12, %reg; \
 	and	%reg, 3, %reg;
 
+#define GET_PROCESSOR4D_ID(reg) \
+	lda	[%g0] ASI_M_VIKING_TMP1, %reg;
+
+/* Blackbox */
+#define GET_PROCESSOR_ID(reg) \
+	sethi	%hi(___b_smp_processor_id), %reg; \
+	sethi	%hi(boot_cpu_id), %reg; \
+	ldub	[%reg + %lo(boot_cpu_id)], %reg;
+
 #define GET_PROCESSOR_MID(reg, tmp) \
 	rd	%tbr, %reg; \
 	sethi	C_LABEL(mid_xlate), %tmp; \
@@ -19,15 +32,15 @@
 	and	%reg, 3, %reg; \
 	ldub	[%tmp + %reg], %reg;
 
-#define GET_PROCESSOR_OFFSET(reg) \
+#define GET_PROCESSOR_OFFSET(reg, tmp) \
 	GET_PROCESSOR_ID(reg) \
-	sll	%reg, 2, %reg;
-
-#define PROCESSOR_OFFSET_TO_ID(reg) \
-	srl	%reg, 2, %reg;
+	sethi	C_LABEL(cpu_offset), %tmp; \
+	sll	%reg, 2, %reg; \
+	or	%tmp, %lo(C_LABEL(cpu_offset)), %tmp; \
+	ld	[%tmp + %reg], %reg;
 
-#define PROCESSOR_ID_TO_OFFSET(reg) \
-	sll	%reg, 2, %reg;
+#define GET_PAGE_OFFSET(reg) \
+	sethi	BTFIXUP_SETHI_INIT(page_offset,0xf0000000), %reg;
 
 /* All trap entry points _must_ begin with this macro or else you
  * lose.  It makes sure the kernel has a proper window so that
@@ -42,5 +55,16 @@
 
 /* All traps low-level code here must end with this macro. */
 #define RESTORE_ALL b ret_trap_entry; clr %l6;
+
+/* sun4 probably wants half word accesses to ASI_SEGMAP, while sun4c+
+   likes byte accesses. These are to avoid ifdef mania. */
+
+#ifdef CONFIG_SUN4
+#define lduXa	lduha
+#define stXa	stha
+#else
+#define lduXa	lduba
+#define stXa	stba
+#endif
 
 #endif /* !(_SPARC_ASMMACRO_H) */
diff -u --recursive --new-file v2.1.96/linux/include/asm-sparc/bitops.h linux/include/asm-sparc/bitops.h
--- v2.1.96/linux/include/asm-sparc/bitops.h	Mon Jan 12 15:15:45 1998
+++ linux/include/asm-sparc/bitops.h	Tue Apr 14 17:44:23 1998
@@ -1,4 +1,4 @@
-/* $Id: bitops.h,v 1.48 1997/12/18 02:44:06 ecd Exp $
+/* $Id: bitops.h,v 1.49 1998/02/23 01:46:44 rth Exp $
  * bitops.h: Bit string operations on the Sparc.
  *
  * Copyright 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -60,7 +60,7 @@
 	return (unsigned long) ADDR;
 }
 
-extern __inline__ unsigned long change_bit(unsigned long nr, void *addr)
+extern __inline__ void change_bit(unsigned long nr, void *addr)
 {
 	int mask;
 	unsigned long *ADDR = (unsigned long *) addr;
@@ -76,8 +76,6 @@
 	: "=&r" (ADDR)
 	: "0" (ADDR), "r" (mask)
 	: "g2", "g3");
-
-	return (unsigned long) ADDR;
 }
 
 #else /* __KERNEL__ */
@@ -136,7 +134,7 @@
 	return mask;
 }
 
-extern __inline__ unsigned long clear_bit(unsigned long nr, __SMPVOL void *addr)
+extern __inline__ void clear_bit(unsigned long nr, __SMPVOL void *addr)
 {
 	(void) test_and_clear_bit(nr, addr);
 }
@@ -159,7 +157,7 @@
 	return mask;
 }
 
-extern __inline__ unsigned long change_bit(unsigned long nr, __SMPVOL void *addr)
+extern __inline__ void change_bit(unsigned long nr, __SMPVOL void *addr)
 {
 	(void) test_and_change_bit(nr, addr);
 }
diff -u --recursive --new-file v2.1.96/linux/include/asm-sparc/btfixup.h linux/include/asm-sparc/btfixup.h
--- v2.1.96/linux/include/asm-sparc/btfixup.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-sparc/btfixup.h	Tue Apr 14 17:44:23 1998
@@ -0,0 +1,219 @@
+/* $Id: btfixup.h,v 1.4 1998/03/09 14:04:43 jj Exp $
+ *  asm-sparc/btfixup.h:    Macros for boot time linking.
+ *
+ *  Copyright (C) 1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ */
+ 
+#ifndef _SPARC_BTFIXUP_H
+#define _SPARC_BTFIXUP_H
+
+#include <linux/init.h>
+
+#ifndef __ASSEMBLY__
+
+#ifdef MODULE
+extern unsigned int ___illegal_use_of_BTFIXUP_SIMM13_in_module(void);
+extern unsigned int ___illegal_use_of_BTFIXUP_SETHI_in_module(void);
+extern unsigned int ___illegal_use_of_BTFIXUP_HALF_in_module(void);
+extern unsigned int ___illegal_use_of_BTFIXUP_INT_in_module(void);
+#endif
+
+/* Fixup call xx */
+
+#define BTFIXUPDEF_CALL(__type, __name, __args...) 					\
+	extern __type ___f_##__name(__args);						\
+	extern unsigned ___fs_##__name[3];
+#define BTFIXUPDEF_CALL_CONST(__type, __name, __args...) 				\
+	extern __type ___f_##__name(__args) __attribute__((const));			\
+	extern unsigned ___fs_##__name[3];
+#define BTFIXUP_CALL(__name) ___f_##__name
+
+#define BTFIXUPDEF_BLACKBOX(__name)							\
+	extern unsigned ___bs_##__name[2];
+#ifdef MODULE
+#define BTFIXUP_BLACKBOX(__name) ___illegal_use_of_BTFIXUP_BLACKBOX_in_module
+#else
+/* This must be written in assembly and present in a sethi */
+#define BTFIXUP_BLACKBOX(__name) ___b_##__name
+#endif
+
+/* Put bottom 13bits into some register variable */
+
+#define BTFIXUPDEF_SIMM13(__name)							\
+	extern unsigned int ___sf_##__name(void) __attribute__((const));		\
+	extern unsigned ___ss_##__name[2];						\
+	extern __inline__ unsigned int ___sf_##__name(void) {				\
+		unsigned int ret;							\
+		__asm__ ("or %%g0, ___s_" #__name ", %0" : "=r"(ret));			\
+		return ret;								\
+	}
+#define BTFIXUPDEF_SIMM13_INIT(__name,__val)						\
+	extern unsigned int ___sf_##__name(void) __attribute__((const));		\
+	extern unsigned ___ss_##__name[2];						\
+	extern __inline__ unsigned int ___sf_##__name(void) {				\
+		unsigned int ret;							\
+		__asm__ ("or %%g0, ___s_" #__name "__btset_" #__val ", %0" : "=r"(ret));\
+		return ret;								\
+	}
+#ifdef MODULE
+#define BTFIXUP_SIMM13(__name) ___illegal_use_of_BTFIXUP_SIMM13_in_module()
+#else
+#define BTFIXUP_SIMM13(__name) ___sf_##__name()
+#endif
+
+/* Put either bottom 13 bits, or upper 22 bits into some register variable
+ * (depending on the value, this will lead into sethi FIX, reg; or
+ * mov FIX, reg; )
+ */
+
+#define BTFIXUPDEF_HALF(__name)								\
+	extern unsigned int ___af_##__name(void) __attribute__((const));		\
+	extern unsigned ___as_##__name[2];						\
+	extern __inline__ unsigned int ___af_##__name(void) {				\
+		unsigned int ret;							\
+		__asm__ ("or %%g0, ___a_" #__name ", %0" : "=r"(ret));			\
+		return ret;								\
+	}
+#define BTFIXUPDEF_HALF_INIT(__name,__val)						\
+	extern unsigned int ___af_##__name(void) __attribute__((const));		\
+	extern unsigned ___as_##__name[2];						\
+	extern __inline__ unsigned int ___af_##__name(void) {				\
+		unsigned int ret;							\
+		__asm__ ("or %%g0, ___a_" #__name "__btset_" #__val ", %0" : "=r"(ret));\
+		return ret;								\
+	}
+#ifdef MODULE
+#define BTFIXUP_HALF(__name) ___illegal_use_of_BTFIXUP_HALF_in_module()
+#else
+#define BTFIXUP_HALF(__name) ___af_##__name()
+#endif
+
+/* Put upper 22 bits into some register variable */
+
+#define BTFIXUPDEF_SETHI(__name)							\
+	extern unsigned int ___hf_##__name(void) __attribute__((const));		\
+	extern unsigned ___hs_##__name[2];						\
+	extern __inline__ unsigned int ___hf_##__name(void) {				\
+		unsigned int ret;							\
+		__asm__ ("sethi %%hi(___h_" #__name "), %0" : "=r"(ret));		\
+		return ret;								\
+	}
+#define BTFIXUPDEF_SETHI_INIT(__name,__val)						\
+	extern unsigned int ___hf_##__name(void) __attribute__((const));		\
+	extern unsigned ___hs_##__name[2];						\
+	extern __inline__ unsigned int ___hf_##__name(void) {				\
+		unsigned int ret;							\
+		__asm__ ("sethi %%hi(___h_" #__name "__btset_" #__val "), %0" : 	\
+			 "=r"(ret));							\
+		return ret;								\
+	}
+#ifdef MODULE
+#define BTFIXUP_SETHI(__name) ___illegal_use_of_BTFIXUP_SETHI_in_module()
+#else
+#define BTFIXUP_SETHI(__name) ___hf_##__name()
+#endif
+
+/* Put a full 32bit integer into some register variable */
+
+#define BTFIXUPDEF_INT(__name)								\
+	extern unsigned char ___i_##__name;						\
+	extern unsigned ___is_##__name[2];
+#ifdef MODULE
+#define BTFIXUP_INT(__name) ___illegal_use_of_BTFIXUP_INT_in_module()
+#else
+#define BTFIXUP_INT(__name) ((unsigned int)&___i_##__name)
+#endif
+
+#define BTFIXUPCALL_NORM	0x00000000			/* Always call */
+#define BTFIXUPCALL_NOP		0x01000000			/* Possibly optimize to nop */
+#define BTFIXUPCALL_RETINT(i)	(0x90102000|((i) & 0x1fff))	/* Possibly optimize to mov i, %o0 */
+#define BTFIXUPCALL_ORINT(i)	(0x90122000|((i) & 0x1fff))	/* Possibly optimize to or %o0, i, %o0 */
+#define BTFIXUPCALL_RETO0	0x01000000			/* Return first parameter, actually a nop */
+#define BTFIXUPCALL_ANDNINT(i)	(0x902a2000|((i) & 0x1fff))	/* Possibly optimize to andn %o0, i, %o0 */
+#define BTFIXUPCALL_SWAPO0O1	0xd27a0000			/* Possibly optimize to swap [%o0],%o1 */
+#define BTFIXUPCALL_SWAPO0G0	0xc07a0000			/* Possibly optimize to swap [%o0],%g0 */
+#define BTFIXUPCALL_SWAPG1G2	0xc4784000			/* Possibly optimize to swap [%g1],%g2 */
+#define BTFIXUPCALL_STG0O0	0xc0220000			/* Possibly optimize to st %g0,[%o0] */
+#define BTFIXUPCALL_STO1O0	0xd2220000			/* Possibly optimize to st %o1,[%o0] */
+
+#define BTFIXUPSET_CALL(__name, __addr, __insn)						\
+	do {										\
+		___fs_##__name[0] |= 1;							\
+		___fs_##__name[1] = (unsigned long)__addr;				\
+		___fs_##__name[2] = __insn;						\
+	} while (0)
+	
+#define BTFIXUPSET_BLACKBOX(__name, __func)						\
+	do {										\
+		___bs_##__name[0] |= 1;							\
+		___bs_##__name[1] = (unsigned long)__func;				\
+	} while (0)
+	
+#define BTFIXUPCOPY_CALL(__name, __from)						\
+	do {										\
+		___fs_##__name[0] |= 1;							\
+		___fs_##__name[1] = ___fs_##__from[1];					\
+		___fs_##__name[2] = ___fs_##__from[2];					\
+	} while (0)
+		
+#define BTFIXUPSET_SIMM13(__name, __val)						\
+	do {										\
+		___ss_##__name[0] |= 1;							\
+		___ss_##__name[1] = (unsigned)__val;					\
+	} while (0)
+	
+#define BTFIXUPCOPY_SIMM13(__name, __from)						\
+	do {										\
+		___ss_##__name[0] |= 1;							\
+		___ss_##__name[1] = ___ss_##__from[1];					\
+	} while (0)
+		
+#define BTFIXUPSET_HALF(__name, __val)							\
+	do {										\
+		___as_##__name[0] |= 1;							\
+		___as_##__name[1] = (unsigned)__val;					\
+	} while (0)
+	
+#define BTFIXUPCOPY_HALF(__name, __from)						\
+	do {										\
+		___as_##__name[0] |= 1;							\
+		___as_##__name[1] = ___as_##__from[1];					\
+	} while (0)
+		
+#define BTFIXUPSET_SETHI(__name, __val)							\
+	do {										\
+		___hs_##__name[0] |= 1;							\
+		___hs_##__name[1] = (unsigned)__val;					\
+	} while (0)
+	
+#define BTFIXUPCOPY_SETHI(__name, __from)						\
+	do {										\
+		___hs_##__name[0] |= 1;							\
+		___hs_##__name[1] = ___hs_##__from[1];					\
+	} while (0)
+		
+#define BTFIXUPSET_INT(__name, __val)							\
+	do {										\
+		___is_##__name[0] |= 1;							\
+		___is_##__name[1] = (unsigned)__val;					\
+	} while (0)
+	
+#define BTFIXUPCOPY_INT(__name, __from)							\
+	do {										\
+		___is_##__name[0] |= 1;							\
+		___is_##__name[1] = ___is_##__from[1];					\
+	} while (0)
+	
+#define BTFIXUPVAL_CALL(__name)								\
+	((unsigned long)___fs_##__name[1])
+	
+extern void btfixup(void);
+
+#else /* __ASSEMBLY__ */
+
+#define BTFIXUP_SETHI(__name)			%hi(___h_ ## __name)
+#define BTFIXUP_SETHI_INIT(__name,__val)	%hi(___h_ ## __name ## __btset_ ## __val)
+
+#endif /* __ASSEMBLY__ */
+	
+#endif /* !(_SPARC_BTFIXUP_H) */
diff -u --recursive --new-file v2.1.96/linux/include/asm-sparc/contregs.h linux/include/asm-sparc/contregs.h
--- v2.1.96/linux/include/asm-sparc/contregs.h	Sat Nov  9 00:29:14 1996
+++ linux/include/asm-sparc/contregs.h	Tue Apr 14 17:44:23 1998
@@ -1,4 +1,4 @@
-/* $Id: contregs.h,v 1.6 1995/11/25 02:31:27 davem Exp $ */
+/* $Id: contregs.h,v 1.7 1998/01/30 10:59:48 jj Exp $ */
 #ifndef _SPARC_CONTREGS_H
 #define _SPARC_CONTREGS_H
 
@@ -14,7 +14,7 @@
 #define AC_CONTEXT    0x30000000    /* 4c current mmu-context                */
 #define AC_SENABLE    0x40000000    /* 4c system dvma/cache/reset enable reg */
 #define AC_UDVMA_ENB  0x50000000    /* 4  Not used on Sun boards, byte       */
-#define AC_BUS_ERROR  0x60000000    /* 4  Cleared on read, byte.             */
+#define AC_BUS_ERROR  0x60000000    /* 4  Not cleared on read, byte.         */
 #define AC_SYNC_ERR   0x60000000    /*  c fault type                         */
 #define AC_SYNC_VA    0x60000004    /*  c fault virtual address              */
 #define AC_ASYNC_ERR  0x60000008    /*  c asynchronous fault type            */
diff -u --recursive --new-file v2.1.96/linux/include/asm-sparc/dma.h linux/include/asm-sparc/dma.h
--- v2.1.96/linux/include/asm-sparc/dma.h	Mon Apr 14 16:28:20 1997
+++ linux/include/asm-sparc/dma.h	Tue Apr 14 17:44:23 1998
@@ -1,4 +1,4 @@
-/* $Id: dma.h,v 1.24 1997/04/10 05:13:21 davem Exp $
+/* $Id: dma.h,v 1.25 1998/02/09 13:27:01 jj Exp $
  * include/asm-sparc/dma.h
  *
  * Copyright 1995 (C) David S. Miller (davem@caip.rutgers.edu)
@@ -7,6 +7,7 @@
 #ifndef _ASM_SPARC_DMA_H
 #define _ASM_SPARC_DMA_H
 
+#include <linux/config.h>
 #include <linux/kernel.h>
 #include <linux/types.h>
 
@@ -71,7 +72,13 @@
 extern struct Linux_SBus_DMA *dma_chain;
 
 /* Broken hardware... */
+#ifdef CONFIG_SUN4
+/* Have to sort this out. Does rev0 work fine on sun4[cmd] without isbroken?
+ * Or is rev0 present only on sun4 boxes? -jj */
+#define DMA_ISBROKEN(dma)    ((dma)->revision == dvmarev0 || (dma)->revision == dvmarev1)
+#else
 #define DMA_ISBROKEN(dma)    ((dma)->revision == dvmarev1)
+#endif
 #define DMA_ISESC1(dma)      ((dma)->revision == dvmaesc1)
 
 /* Main routines in dma.c */
diff -u --recursive --new-file v2.1.96/linux/include/asm-sparc/elf.h linux/include/asm-sparc/elf.h
--- v2.1.96/linux/include/asm-sparc/elf.h	Thu Feb 12 20:56:13 1998
+++ linux/include/asm-sparc/elf.h	Tue Apr 14 17:44:23 1998
@@ -1,4 +1,4 @@
-/* $Id: elf.h,v 1.11 1997/09/26 18:37:32 tdyas Exp $ */
+/* $Id: elf.h,v 1.15 1998/03/23 08:41:32 jj Exp $ */
 #ifndef __ASMSPARC_ELF_H
 #define __ASMSPARC_ELF_H
 
@@ -6,6 +6,7 @@
  * ELF register definitions..
  */
 
+#include <linux/config.h>
 #include <asm/ptrace.h>
 #include <asm/mbus.h>
 
@@ -29,7 +30,12 @@
 #define ELF_DATA	ELFDATA2MSB
 
 #define USE_ELF_CORE_DUMP
+#ifndef CONFIG_SUN4
 #define ELF_EXEC_PAGESIZE	4096
+#else
+#define ELF_EXEC_PAGESIZE	8192
+#endif
+
 
 /* This is the location that an ET_DYN program is loaded if exec'ed.  Typical
    use of this is to invoke "./ld.so someprog" to test out a new version of
@@ -45,7 +51,7 @@
 /* Sun4c has none of the capabilities, most sun4m's have them all.
  * XXX This is gross, set some global variable at boot time. -DaveM
  */
-#define ELF_HWCAP	((sparc_cpu_model == sun4c) ? 0 : \
+#define ELF_HWCAP	((ARCH_SUN4C_SUN4) ? 0 : \
 			 (HWCAP_SPARC_FLUSH | HWCAP_SPARC_STBAR | \
 			  HWCAP_SPARC_SWAP | \
 			  ((srmmu_modtype != Cypress && \
@@ -55,7 +61,7 @@
 
 /* This yields a string that ld.so will use to load implementation
    specific libraries for optimization.  This is more specific in
-   intent than poking at uname or /proc/cpuinfo.  */
+   intent than poking at uname or /proc/cpuinfo. */
 
 #define ELF_PLATFORM	(NULL)
 
diff -u --recursive --new-file v2.1.96/linux/include/asm-sparc/head.h linux/include/asm-sparc/head.h
--- v2.1.96/linux/include/asm-sparc/head.h	Mon Jan 12 15:15:54 1998
+++ linux/include/asm-sparc/head.h	Tue Apr 14 17:44:23 1998
@@ -1,4 +1,4 @@
-/* $Id: head.h,v 1.33 1997/10/04 08:54:22 ecd Exp $ */
+/* $Id: head.h,v 1.35 1998/03/18 09:15:40 jj Exp $ */
 #ifndef __SPARC_HEAD_H
 #define __SPARC_HEAD_H
 
@@ -10,8 +10,7 @@
 
 #define NCPUS            4              /* Architectural limit of sun4m. */
 
-#define SUN4_PROM_VECTOR 0xFFE81000     /* To safely die on a SUN4 */
-#define SUN4_PRINTF      0x84           /* Offset into SUN4_PROM_VECTOR */
+#define SUN4_PROM_VECTOR 0xFFE81000     /* SUN4 PROM needs to be hardwired */
 
 #define WRITE_PAUSE      nop; nop; nop; /* Have to do this after %wim/%psr chg */
 #define NOP_INSN         0x01000000     /* Used to patch sparc_save_state */
@@ -31,6 +30,10 @@
 /* This is for traps we should NEVER get. */
 #define BAD_TRAP(num) \
         rd %psr, %l0; mov num, %l7; b bad_trap_handler; rd %wim, %l3;
+
+/* This is for traps when we want just skip the instruction which caused it */
+#define SKIP_TRAP(type, name) \
+	jmpl %l2, %g0; rett %l2 + 4; nop; nop;
 
 /* Notice that for the system calls we pull a trick.  We load up a
  * different pointer to the system call vector table in %l7, but call
diff -u --recursive --new-file v2.1.96/linux/include/asm-sparc/io-unit.h linux/include/asm-sparc/io-unit.h
--- v2.1.96/linux/include/asm-sparc/io-unit.h	Mon Jan 12 15:15:54 1998
+++ linux/include/asm-sparc/io-unit.h	Tue Apr 14 17:44:23 1998
@@ -1,6 +1,6 @@
 /* io-unit.h: Definitions for the sun4d IO-UNIT.
  *
- * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
  */
 #ifndef _SPARC_IO_UNIT_H
 #define _SPARC_IO_UNIT_H
@@ -22,8 +22,8 @@
  
 #define IOUNIT_DMA_BASE	    0xfc000000 /* TOP - 64M */
 #define IOUNIT_DMA_SIZE	    0x04000000 /* 64M */
-/* We use last 4M for sparc_dvma_malloc */
-#define IOUNIT_DVMA_SIZE    0x00400000 /* 4M */
+/* We use last 1M for sparc_dvma_malloc */
+#define IOUNIT_DVMA_SIZE    0x00100000 /* 1M */
 
 /* The format of an iopte in the external page tables */
 #define IOUPTE_PAGE          0xffffff00 /* Physical page number (PA[35:12]) */
@@ -36,8 +36,18 @@
 #define IOUPTE_PARITY        0x00000001
 
 struct iounit_struct {
-	spinlock_t		iommu_lock;
+	unsigned int		bmap[(IOUNIT_DMA_SIZE >> (PAGE_SHIFT + 3)) / sizeof(unsigned int)];
+	spinlock_t		lock;
 	iopte_t			*page_table;
+	unsigned long		rotor[3];
+	unsigned long		limit[4];
 };
+
+#define IOUNIT_BMAP1_START	0x00000000
+#define IOUNIT_BMAP1_END	(IOUNIT_DMA_SIZE >> (PAGE_SHIFT + 1))
+#define IOUNIT_BMAP2_START	IOUNIT_BMAP1_END
+#define IOUNIT_BMAP2_END	IOUNIT_BMAP2_START + (IOUNIT_DMA_SIZE >> (PAGE_SHIFT + 2))
+#define IOUNIT_BMAPM_START	IOUNIT_BMAP2_END
+#define IOUNIT_BMAPM_END	((IOUNIT_DMA_SIZE - IOUNIT_DVMA_SIZE) >> PAGE_SHIFT)
 
 #endif /* !(_SPARC_IO_UNIT_H) */
diff -u --recursive --new-file v2.1.96/linux/include/asm-sparc/io.h linux/include/asm-sparc/io.h
--- v2.1.96/linux/include/asm-sparc/io.h	Mon Apr 14 16:28:20 1997
+++ linux/include/asm-sparc/io.h	Tue Apr 14 17:44:23 1998
@@ -1,4 +1,4 @@
-/* $Id: io.h,v 1.14 1997/04/10 05:13:22 davem Exp $ */
+/* $Id: io.h,v 1.15 1998/01/30 10:59:51 jj Exp $ */
 #ifndef __SPARC_IO_H
 #define __SPARC_IO_H
 
@@ -99,6 +99,7 @@
 {
 	switch(sparc_cpu_model) {
 	case sun4c:
+	case sun4:
 		sun4c_mapioaddr(physaddr, virt_addr, bus, rdonly);
 		break;
 	case sun4m:
@@ -122,6 +123,7 @@
 {
 	switch(sparc_cpu_model) {
 	case sun4c:
+	case sun4:
 		sun4c_unmapioaddr(virt_addr);
 		break;
 	case sun4m:
diff -u --recursive --new-file v2.1.96/linux/include/asm-sparc/irq.h linux/include/asm-sparc/irq.h
--- v2.1.96/linux/include/asm-sparc/irq.h	Mon Jan 12 15:15:54 1998
+++ linux/include/asm-sparc/irq.h	Fri Apr 17 22:04:44 1998
@@ -1,4 +1,4 @@
-/* $Id: irq.h,v 1.21 1997/11/19 15:12:20 jj Exp $
+/* $Id: irq.h,v 1.22 1998/02/05 14:20:05 jj Exp $
  * irq.h: IRQ registers on the Sparc.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -10,6 +10,7 @@
 #include <linux/linkage.h>
 
 #include <asm/system.h>     /* For NCPUS */
+#include <asm/btfixup.h>
 
 /* This is used for sun4d */
 struct devid_cookie {
@@ -66,27 +67,47 @@
 #define irq_exit(cpu, irq)		(local_irq_count[cpu]--)
 #endif
 
+static __inline__ int irq_cannonicalize(int irq)
+{
+	return irq;
+}
+
 /* Dave Redman (djhr@tadpole.co.uk)
  * changed these to function pointers.. it saves cycles and will allow
  * the irq dependencies to be split into different files at a later date
  * sun4c_irq.c, sun4m_irq.c etc so we could reduce the kernel size.
+ * Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Changed these to btfixup entities... It saves cycles :)
  */
-extern void (*disable_irq)(unsigned int);
-extern void (*enable_irq)(unsigned int);
-extern void (*disable_pil_irq)(unsigned int);
-extern void (*enable_pil_irq)(unsigned int);
-extern void (*clear_clock_irq)(void);
-extern void (*clear_profile_irq)(int);
-extern void (*load_profile_irq)(int cpu, unsigned int timeout);
+BTFIXUPDEF_CALL(void, disable_irq, unsigned int)
+BTFIXUPDEF_CALL(void, enable_irq, unsigned int)
+BTFIXUPDEF_CALL(void, disable_pil_irq, unsigned int)
+BTFIXUPDEF_CALL(void, enable_pil_irq, unsigned int)
+BTFIXUPDEF_CALL(void, clear_clock_irq, void)
+BTFIXUPDEF_CALL(void, clear_profile_irq, int)
+BTFIXUPDEF_CALL(void, load_profile_irq, int, unsigned int)
+
+#define disable_irq(irq) BTFIXUP_CALL(disable_irq)(irq)
+#define enable_irq(irq) BTFIXUP_CALL(enable_irq)(irq)
+#define disable_pil_irq(irq) BTFIXUP_CALL(disable_pil_irq)(irq)
+#define enable_pil_irq(irq) BTFIXUP_CALL(enable_pil_irq)(irq)
+#define clear_clock_irq() BTFIXUP_CALL(clear_clock_irq)()
+#define clear_profile_irq(cpu) BTFIXUP_CALL(clear_profile_irq)(cpu)
+#define load_profile_irq(cpu,limit) BTFIXUP_CALL(load_profile_irq)(cpu,limit)
+
 extern void (*init_timers)(void (*lvl10_irq)(int, void *, struct pt_regs *));
 extern void claim_ticker14(void (*irq_handler)(int, void *, struct pt_regs *),
 			   int irq,
 			   unsigned int timeout);
 
 #ifdef __SMP__
-extern void (*set_cpu_int)(int, int);
-extern void (*clear_cpu_int)(int, int);
-extern void (*set_irq_udt)(int);
+BTFIXUPDEF_CALL(void, set_cpu_int, int, int)
+BTFIXUPDEF_CALL(void, clear_cpu_int, int, int)
+BTFIXUPDEF_CALL(void, set_irq_udt, int)
+
+#define set_cpu_int(cpu,level) BTFIXUP_CALL(set_cpu_int)(cpu,level)
+#define clear_cpu_int(cpu,level) BTFIXUP_CALL(clear_cpu_int)(cpu,level)
+#define set_irq_udt(cpu) BTFIXUP_CALL(set_irq_udt)(cpu)
 #endif
 
 extern int request_fast_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *), unsigned long flags, __const__ char *devname);
diff -u --recursive --new-file v2.1.96/linux/include/asm-sparc/mmu_context.h linux/include/asm-sparc/mmu_context.h
--- v2.1.96/linux/include/asm-sparc/mmu_context.h	Mon Dec 30 01:59:59 1996
+++ linux/include/asm-sparc/mmu_context.h	Tue Apr 14 17:44:23 1998
@@ -1,6 +1,8 @@
 #ifndef __SPARC_MMU_CONTEXT_H
 #define __SPARC_MMU_CONTEXT_H
 
+#include <asm/btfixup.h>
+
 /* For now I still leave the context handling in the
  * switch_to() macro, I'll do it right soon enough.
  */
@@ -9,11 +11,15 @@
 /* Initialize the context related info for a new mm_struct
  * instance.
  */
-extern void (*init_new_context)(struct mm_struct *mm);
+BTFIXUPDEF_CALL(void, init_new_context, struct mm_struct *)
+
+#define init_new_context(mm) BTFIXUP_CALL(init_new_context)(mm)
 
 /* Destroy context related info for an mm_struct that is about
  * to be put to rest.
  */
-extern void (*destroy_context)(struct mm_struct *mm);
+BTFIXUPDEF_CALL(void, destroy_context, struct mm_struct *)
+
+#define destroy_context(mm) BTFIXUP_CALL(destroy_context)(mm)
 
 #endif /* !(__SPARC_MMU_CONTEXT_H) */
diff -u --recursive --new-file v2.1.96/linux/include/asm-sparc/namei.h linux/include/asm-sparc/namei.h
--- v2.1.96/linux/include/asm-sparc/namei.h	Mon Jan 12 15:15:54 1998
+++ linux/include/asm-sparc/namei.h	Tue Apr 14 17:44:23 1998
@@ -1,4 +1,4 @@
-/* $Id: namei.h,v 1.10 1997/09/24 16:20:36 jj Exp $
+/* $Id: namei.h,v 1.11 1998/01/15 12:58:24 jj Exp $
  * linux/include/asm-sparc/namei.h
  *
  * Routines to handle famous /usr/gnemul/s*.
@@ -18,8 +18,11 @@
 	char *emul;
 
 	switch (current->personality) {
+#if 0
+/* Until we solve, why SunOS apps sometime crash, disable gnemul support for SunOS */
 	case PER_BSD:
 		emul = SPARC_BSD_EMUL; break;
+#endif
 	case PER_SVR4:
 		emul = SPARC_SOL_EMUL; break;
 	default:
diff -u --recursive --new-file v2.1.96/linux/include/asm-sparc/obio.h linux/include/asm-sparc/obio.h
--- v2.1.96/linux/include/asm-sparc/obio.h	Mon Jan 12 15:15:54 1998
+++ linux/include/asm-sparc/obio.h	Tue Apr 14 17:44:23 1998
@@ -1,4 +1,4 @@
-/* $Id: obio.h,v 1.3 1997/12/18 14:21:41 jj Exp $
+/* $Id: obio.h,v 1.4 1998/03/09 14:04:55 jj Exp $
  * obio.h:  Some useful locations in 0xFXXXXXXXX PA obio space on sun4d.
  *
  * Copyright (C) 1997 Jakub Jelinek <jj@sunsite.mff.cuni.cz>
@@ -66,9 +66,20 @@
 /* Boot Bus */
 #define BB_LOCAL_BASE		0xf0000000
 
+#define BB_STAT1		0x00100000
+#define BB_STAT2		0x00120000
+#define BB_STAT3		0x00140000
 #define BB_LEDS			0x002e0000
 
+/* Bits in BB_STAT2 */
+#define BB_STAT2_AC_INTR	0x04	/* Aiee! 5ms and power is gone... */
+#define BB_STAT2_TMP_INTR	0x10	/* My Penguins are burning. Are you able to smell it? */
+#define BB_STAT2_FAN_INTR	0x20	/* My fan refuses to work */
+#define BB_STAT2_PWR_INTR	0x40	/* On SC2000, one of the two ACs died. Ok, we go on... */
+#define BB_STAT2_MASK		(BB_STAT2_AC_INTR|BB_STAT2_TMP_INTR|BB_STAT2_FAN_INTR|BB_STAT2_PWR_INTR)
+
 /* Cache Controller */
+#define CC_BASE		0x1F00000
 #define CC_DATSTREAM	0x1F00000  /* Data stream register */
 #define CC_DATSIZE	0x1F0003F  /* Size */
 #define CC_SRCSTREAM	0x1F00100  /* Source stream register */
@@ -144,10 +155,13 @@
 			      "i" (ASI_M_CTL));
 }
 
-extern __inline__ void show_leds(int cpuid, unsigned char mask)
+extern unsigned char cpu_leds[32];
+
+extern __inline__ void show_leds(int cpuid)
 {
+	cpuid &= 0x1e;
 	__asm__ __volatile__ ("stba %0, [%1] %2" : :
-			      "r" (mask),
+			      "r" ((cpu_leds[cpuid] << 4) | cpu_leds[cpuid+1]),
 			      "r" (ECSR_BASE(cpuid) | BB_LEDS),
 			      "i" (ASI_M_CTL));
 }
@@ -190,12 +204,44 @@
 			      "i" (ASI_M_MXCC));
 }
 
+extern __inline__ unsigned cc_get_imsk_other(int cpuid)
+{
+	unsigned mask;
+	
+	__asm__ __volatile__ ("lduha [%1] %2, %0" :
+			      "=r" (mask) :
+			      "r" (ECSR_BASE(cpuid) | CC_IMSK),
+			      "i" (ASI_M_CTL));
+	return mask;
+}
+
+extern __inline__ void cc_set_imsk_other(int cpuid, unsigned mask)
+{
+	__asm__ __volatile__ ("stha %0, [%1] %2" : :
+			      "r" (mask),
+			      "r" (ECSR_BASE(cpuid) | CC_IMSK),
+			      "i" (ASI_M_CTL));
+}
+
 extern __inline__ void cc_set_igen(unsigned gen)
 {
 	__asm__ __volatile__ ("sta %0, [%1] %2" : :
 			      "r" (gen),
 			      "r" (CC_IGEN),
 			      "i" (ASI_M_MXCC));
+}
+
+/* +-------+-------------+-----------+------------------------------------+
+ * | bcast |  devid      |   sid     |              levels mask           |
+ * +-------+-------------+-----------+------------------------------------+
+ *  31      30         23 22       15 14                                 0
+ */
+#define IGEN_MESSAGE(bcast, devid, sid, levels) \
+	(((bcast) << 31) | ((devid) << 23) | ((sid) << 15) | (levels))
+            
+extern __inline__ void sun4d_send_ipi(int cpu, int level)
+{
+	cc_set_igen(IGEN_MESSAGE(0, cpu << 3, 6 + ((level >> 1) & 7), 1 << (level - 1)));
 }
 
 #endif /* !__ASSEMBLY__ */
diff -u --recursive --new-file v2.1.96/linux/include/asm-sparc/oplib.h linux/include/asm-sparc/oplib.h
--- v2.1.96/linux/include/asm-sparc/oplib.h	Mon Jan 12 15:15:54 1998
+++ linux/include/asm-sparc/oplib.h	Tue Apr 14 17:44:23 1998
@@ -1,4 +1,4 @@
-/* $Id: oplib.h,v 1.18 1997/09/24 11:34:18 jj Exp $
+/* $Id: oplib.h,v 1.19 1998/01/30 10:59:53 jj Exp $
  * oplib.h:  Describes the interface and available routines in the
  *           Linux Prom library.
  *
@@ -20,6 +20,7 @@
 	PROM_V3,      /* sun4m and later, up to sun4d/sun4e machines V3 */
 	PROM_P1275,   /* IEEE compliant ISA based Sun PROM, only sun4u */
         PROM_AP1000,  /* actually no prom at all */
+	PROM_SUN4,    /* Old sun4 proms are totally different, but we'll shoehorn it to make it fit */
 };
 
 extern enum prom_major_version prom_vers;
diff -u --recursive --new-file v2.1.96/linux/include/asm-sparc/page.h linux/include/asm-sparc/page.h
--- v2.1.96/linux/include/asm-sparc/page.h	Mon Jan 12 15:15:54 1998
+++ linux/include/asm-sparc/page.h	Tue Apr 14 17:44:23 1998
@@ -1,4 +1,4 @@
-/* $Id: page.h,v 1.37 1997/11/28 15:59:21 jj Exp $
+/* $Id: page.h,v 1.40 1998/02/06 14:14:46 jj Exp $
  * page.h:  Various defines and such for MMU operations on the Sparc for
  *          the Linux kernel.
  *
@@ -8,7 +8,12 @@
 #ifndef _SPARC_PAGE_H
 #define _SPARC_PAGE_H
 
+#include <linux/config.h>
+#ifdef CONFIG_SUN4
+#define PAGE_SHIFT   13
+#else
 #define PAGE_SHIFT   12
+#endif
 #define PAGE_SIZE    (1 << PAGE_SHIFT)
 #define PAGE_MASK    (~(PAGE_SIZE-1))
 
@@ -16,6 +21,10 @@
 
 #include <linux/config.h>
 #include <asm/head.h>       /* for KERNBASE */
+#include <asm/btfixup.h>
+
+/* This is always 2048*sizeof(long), doesn't change with PAGE_SIZE */
+#define TASK_UNION_SIZE		8192
 
 #ifndef __ASSEMBLY__
 
@@ -24,11 +33,20 @@
 
 extern unsigned long page_offset;
 
-#define PAGE_OFFSET  (page_offset)
+BTFIXUPDEF_SETHI_INIT(page_offset,0xf0000000)
+
+#ifdef MODULE
+#define 	PAGE_OFFSET  (page_offset)
+#else
+#define		PAGE_OFFSET  BTFIXUP_SETHI(page_offset)
+#endif
 
 /* translate between physical and virtual addresses */
-extern unsigned long (*mmu_v2p)(unsigned long);
-extern unsigned long (*mmu_p2v)(unsigned long);
+BTFIXUPDEF_CALL_CONST(unsigned long, mmu_v2p, unsigned long)
+BTFIXUPDEF_CALL_CONST(unsigned long, mmu_p2v, unsigned long)
+
+#define mmu_v2p(vaddr) BTFIXUP_CALL(mmu_v2p)(vaddr)
+#define mmu_p2v(paddr) BTFIXUP_CALL(mmu_p2v)(paddr)
 
 #define __pa(x)    (mmu_v2p((unsigned long)(x)))
 #define __va(x)    ((void *)(mmu_p2v((unsigned long)(x))))
@@ -248,7 +266,9 @@
 
 extern unsigned long sparc_unmapped_base;
 
-#define TASK_UNMAPPED_BASE	(sparc_unmapped_base)
+BTFIXUPDEF_SETHI(sparc_unmapped_base)
+
+#define TASK_UNMAPPED_BASE	BTFIXUP_SETHI(sparc_unmapped_base)
 
 /* to align the pointer to the (next) page boundary */
 #define PAGE_ALIGN(addr)  (((addr)+PAGE_SIZE-1)&PAGE_MASK)
diff -u --recursive --new-file v2.1.96/linux/include/asm-sparc/pgtable.h linux/include/asm-sparc/pgtable.h
--- v2.1.96/linux/include/asm-sparc/pgtable.h	Mon Jan 12 15:15:54 1998
+++ linux/include/asm-sparc/pgtable.h	Tue Apr 14 17:44:23 1998
@@ -1,4 +1,4 @@
-/* $Id: pgtable.h,v 1.64 1997/11/07 15:01:44 jj Exp $ */
+/* $Id: pgtable.h,v 1.74 1998/03/11 04:08:37 tdyas Exp $ */
 #ifndef _SPARC_PGTABLE_H
 #define _SPARC_PGTABLE_H
 
@@ -6,33 +6,42 @@
  *                        with Sparc page tables.
  *
  *  Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ *  Copyright (C) 1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
  */
 
 #include <linux/mm.h>
+#include <linux/config.h>
 #include <asm/asi.h>
+#ifdef CONFIG_SUN4
+#include <asm/pgtsun4.h>
+#else
 #include <asm/pgtsun4c.h>
+#endif
 #include <asm/pgtsrmmu.h>
 #include <asm/vac-ops.h>
 #include <asm/oplib.h>
 #include <asm/sbus.h>
+#include <asm/btfixup.h>
+#include <asm/spinlock.h>
 
 extern void load_mmu(void);
 extern int io_remap_page_range(unsigned long from, unsigned long to,
 			       unsigned long size, pgprot_t prot, int space);
+			       
+BTFIXUPDEF_CALL(void, quick_kernel_fault, unsigned long)
 
-extern void (*quick_kernel_fault)(unsigned long);
+#define quick_kernel_fault(addr) BTFIXUP_CALL(quick_kernel_fault)(addr)
 
 /* Allocate a block of RAM which is aligned to its size.
    This procedure can be used until the call to mem_init(). */
 extern void *sparc_init_alloc(unsigned long *kbrk, unsigned long size);
 
-/* translate between physical and virtual addresses */
-extern unsigned long (*mmu_v2p)(unsigned long);
-extern unsigned long (*mmu_p2v)(unsigned long);
-
 /* Routines for data transfer buffers. */
-extern char *(*mmu_lockarea)(char *, unsigned long);
-extern void  (*mmu_unlockarea)(char *, unsigned long);
+BTFIXUPDEF_CALL(char *, mmu_lockarea, char *, unsigned long)
+BTFIXUPDEF_CALL(void,   mmu_unlockarea, char *, unsigned long)
+
+#define mmu_lockarea(vaddr,len) BTFIXUP_CALL(mmu_lockarea)(vaddr,len)
+#define mmu_unlockarea(vaddr,len) BTFIXUP_CALL(mmu_unlockarea)(vaddr,len)
 
 /* Routines for getting a dvma scsi buffer. */
 struct mmu_sglist {
@@ -41,58 +50,76 @@
 	unsigned int len;
 	__u32 dvma_addr;
 };
-extern __u32 (*mmu_get_scsi_one)(char *, unsigned long, struct linux_sbus *sbus);
-extern void  (*mmu_get_scsi_sgl)(struct mmu_sglist *, int, struct linux_sbus *sbus);
-extern void  (*mmu_release_scsi_one)(__u32, unsigned long, struct linux_sbus *sbus);
-extern void  (*mmu_release_scsi_sgl)(struct mmu_sglist *, int, struct linux_sbus *sbus);
-
-extern void  (*mmu_map_dma_area)(unsigned long addr, int len);
-
-extern unsigned int pmd_shift;
-extern unsigned int pmd_size;
-extern unsigned int pmd_mask;
-extern unsigned int (*pmd_align)(unsigned int);
-
-extern unsigned int pgdir_shift;
-extern unsigned int pgdir_size;
-extern unsigned int pgdir_mask;
-extern unsigned int (*pgdir_align)(unsigned int);
-
-extern unsigned int ptrs_per_pte;
-extern unsigned int ptrs_per_pmd;
-extern unsigned int ptrs_per_pgd;
+BTFIXUPDEF_CALL(__u32, mmu_get_scsi_one, char *, unsigned long, struct linux_sbus *sbus)
+BTFIXUPDEF_CALL(void,  mmu_get_scsi_sgl, struct mmu_sglist *, int, struct linux_sbus *sbus)
+BTFIXUPDEF_CALL(void,  mmu_release_scsi_one, __u32, unsigned long, struct linux_sbus *sbus)
+BTFIXUPDEF_CALL(void,  mmu_release_scsi_sgl, struct mmu_sglist *, int, struct linux_sbus *sbus)
+BTFIXUPDEF_CALL(void,  mmu_map_dma_area, unsigned long addr, int len)
+
+#define mmu_get_scsi_one(vaddr,len,sbus) BTFIXUP_CALL(mmu_get_scsi_one)(vaddr,len,sbus)
+#define mmu_get_scsi_sgl(sg,sz,sbus) BTFIXUP_CALL(mmu_get_scsi_sgl)(sg,sz,sbus)
+#define mmu_release_scsi_one(vaddr,len,sbus) BTFIXUP_CALL(mmu_release_scsi_one)(vaddr,len,sbus)
+#define mmu_release_scsi_sgl(sg,sz,sbus) BTFIXUP_CALL(mmu_release_scsi_sgl)(sg,sz,sbus)
+
+#define mmu_map_dma_area(addr,len) BTFIXUP_CALL(mmu_map_dma_area)(addr,len)
+
+BTFIXUPDEF_SIMM13(pmd_shift)
+BTFIXUPDEF_SETHI(pmd_size)
+BTFIXUPDEF_SETHI(pmd_mask)
 
-extern unsigned int ptrs_per_page;
+extern unsigned int pmd_align(unsigned int addr) __attribute__((const));
+extern __inline__ unsigned int pmd_align(unsigned int addr)
+{
+	return ((addr + ~BTFIXUP_SETHI(pmd_mask)) & BTFIXUP_SETHI(pmd_mask));
+}
+
+BTFIXUPDEF_SIMM13(pgdir_shift)
+BTFIXUPDEF_SETHI(pgdir_size)
+BTFIXUPDEF_SETHI(pgdir_mask)
 
-extern unsigned long (*(vmalloc_start))(void);
+extern unsigned int pgdir_align(unsigned int addr) __attribute__((const));
+extern __inline__ unsigned int pgdir_align(unsigned int addr)
+{
+	return ((addr + ~BTFIXUP_SETHI(pgdir_mask)) & BTFIXUP_SETHI(pgdir_mask));
+}
+
+BTFIXUPDEF_SIMM13(ptrs_per_pte)
+BTFIXUPDEF_SIMM13(ptrs_per_pmd)
+BTFIXUPDEF_SIMM13(ptrs_per_pgd)
+BTFIXUPDEF_SIMM13(user_ptrs_per_pgd)
 
 #define VMALLOC_VMADDR(x) ((unsigned long)(x))
-#define VMALLOC_START vmalloc_start()
+/* This is the same accross all platforms */
+#define VMALLOC_START (0xfe300000)
+
+BTFIXUPDEF_INT(page_none)
+BTFIXUPDEF_INT(page_shared)
+BTFIXUPDEF_INT(page_copy)
+BTFIXUPDEF_INT(page_readonly)
+BTFIXUPDEF_INT(page_kernel)
+
+#define PMD_SHIFT       	BTFIXUP_SIMM13(pmd_shift)
+#define PMD_SIZE        	BTFIXUP_SETHI(pmd_size)
+#define PMD_MASK        	BTFIXUP_SETHI(pmd_mask)
+#define PMD_ALIGN(addr) 	pmd_align(addr)
+#define PGDIR_SHIFT     	BTFIXUP_SIMM13(pgdir_shift)
+#define PGDIR_SIZE      	BTFIXUP_SETHI(pgdir_size)
+#define PGDIR_MASK      	BTFIXUP_SETHI(pgdir_mask)
+#define PGDIR_ALIGN     	pgdir_align(addr)
+#define PTRS_PER_PTE    	BTFIXUP_SIMM13(ptrs_per_pte)
+#define PTRS_PER_PMD    	BTFIXUP_SIMM13(ptrs_per_pmd)
+#define PTRS_PER_PGD    	BTFIXUP_SIMM13(ptrs_per_pgd)
+#define USER_PTRS_PER_PGD	BTFIXUP_SIMM13(user_ptrs_per_pgd)
+
+#define PAGE_NONE      __pgprot(BTFIXUP_INT(page_none))
+#define PAGE_SHARED    __pgprot(BTFIXUP_INT(page_shared))
+#define PAGE_COPY      __pgprot(BTFIXUP_INT(page_copy))
+#define PAGE_READONLY  __pgprot(BTFIXUP_INT(page_readonly))
+#define PAGE_KERNEL    __pgprot(BTFIXUP_INT(page_kernel))
 
-extern pgprot_t page_none;
-extern pgprot_t page_shared;
-extern pgprot_t page_copy;
-extern pgprot_t page_readonly;
-extern pgprot_t page_kernel;
-
-#define PMD_SHIFT      (pmd_shift)
-#define PMD_SIZE       (pmd_size)
-#define PMD_MASK       (pmd_mask)
-#define PMD_ALIGN      (pmd_align)
-#define PGDIR_SHIFT    (pgdir_shift)
-#define PGDIR_SIZE     (pgdir_size)
-#define PGDIR_MASK     (pgdir_mask)
-#define PGDIR_ALIGN    (pgdir_align)
-#define PTRS_PER_PTE   (ptrs_per_pte)
-#define PTRS_PER_PMD   (ptrs_per_pmd)
-#define PTRS_PER_PGD   (ptrs_per_pgd)
-
-#define PAGE_NONE      (page_none)
-#define PAGE_SHARED    (page_shared)
-#define PAGE_COPY      (page_copy)
-#define PAGE_READONLY  (page_readonly)
-#define PAGE_KERNEL    (page_kernel)
-#define PAGE_INVALID   (page_invalid)
+BTFIXUPDEF_CALL(void, set_pgdir, unsigned long, pgd_t)
+
+#define set_pgdir(address,entry) BTFIXUP_CALL(set_pgdir)(address,entry)
 
 /* Top-level page directory */
 extern pgd_t swapper_pg_dir[1024];
@@ -159,122 +186,250 @@
 
 #define SIZEOF_PTR_LOG2   2
 
-extern unsigned long (*pte_page)(pte_t);
-extern unsigned long (*pmd_page)(pmd_t);
-extern unsigned long (*pgd_page)(pgd_t);
+BTFIXUPDEF_CALL_CONST(unsigned long, pte_page, pte_t)
+BTFIXUPDEF_CALL_CONST(unsigned long, pmd_page, pmd_t)
+BTFIXUPDEF_CALL_CONST(unsigned long, pgd_page, pgd_t)
+
+#define pte_page(pte) BTFIXUP_CALL(pte_page)(pte)
+#define pmd_page(pmd) BTFIXUP_CALL(pmd_page)(pmd)
+#define pgd_page(pgd) BTFIXUP_CALL(pgd_page)(pgd)
 
-extern void (*sparc_update_rootmmu_dir)(struct task_struct *, pgd_t *pgdir);
+BTFIXUPDEF_CALL(void, sparc_update_rootmmu_dir, struct task_struct *, pgd_t *pgdir)
 
-#define SET_PAGE_DIR(tsk,pgdir) sparc_update_rootmmu_dir(tsk, pgdir)
+#define SET_PAGE_DIR(tsk,pgdir) BTFIXUP_CALL(sparc_update_rootmmu_dir)(tsk, pgdir)
        
 /* to find an entry in a page-table */
 #define PAGE_PTR(address) \
 ((unsigned long)(address)>>(PAGE_SHIFT-SIZEOF_PTR_LOG2)&PTR_MASK&~PAGE_MASK)
 
-extern int (*pte_none)(pte_t);
-extern int (*pte_present)(pte_t);
-extern void (*pte_clear)(pte_t *);
-
-extern int (*pmd_none)(pmd_t);
-extern int (*pmd_bad)(pmd_t);
-extern int (*pmd_present)(pmd_t);
-extern void (*pmd_clear)(pmd_t *);
-
-extern int (*pgd_none)(pgd_t);
-extern int (*pgd_bad)(pgd_t);
-extern int (*pgd_present)(pgd_t);
-extern void (*pgd_clear)(pgd_t *);
+BTFIXUPDEF_SETHI(none_mask)
+BTFIXUPDEF_CALL_CONST(int, pte_present, pte_t)
+BTFIXUPDEF_CALL(void, pte_clear, pte_t *)
+
+extern __inline__ int pte_none(pte_t pte)
+{
+	return !(pte_val(pte) & ~BTFIXUP_SETHI(none_mask));
+}
+
+#define pte_present(pte) BTFIXUP_CALL(pte_present)(pte)
+#define pte_clear(pte) BTFIXUP_CALL(pte_clear)(pte)
+
+BTFIXUPDEF_CALL_CONST(int, pmd_bad, pmd_t)
+BTFIXUPDEF_CALL_CONST(int, pmd_present, pmd_t)
+BTFIXUPDEF_CALL(void, pmd_clear, pmd_t *)
+
+extern __inline__ int pmd_none(pmd_t pmd)
+{
+	return !(pmd_val(pmd) & ~BTFIXUP_SETHI(none_mask));
+}
+
+#define pmd_bad(pmd) BTFIXUP_CALL(pmd_bad)(pmd)
+#define pmd_present(pmd) BTFIXUP_CALL(pmd_present)(pmd)
+#define pmd_clear(pmd) BTFIXUP_CALL(pmd_clear)(pmd)
+
+BTFIXUPDEF_CALL_CONST(int, pgd_none, pgd_t)
+BTFIXUPDEF_CALL_CONST(int, pgd_bad, pgd_t)
+BTFIXUPDEF_CALL_CONST(int, pgd_present, pgd_t)
+BTFIXUPDEF_CALL(void, pgd_clear, pgd_t *)
+
+#define pgd_none(pgd) BTFIXUP_CALL(pgd_none)(pgd)
+#define pgd_bad(pgd) BTFIXUP_CALL(pgd_bad)(pgd)
+#define pgd_present(pgd) BTFIXUP_CALL(pgd_present)(pgd)
+#define pgd_clear(pgd) BTFIXUP_CALL(pgd_clear)(pgd)
 
 /*
  * The following only work if pte_present() is true.
  * Undefined behaviour if not..
  */
-extern int (*pte_write)(pte_t);
-extern int (*pte_dirty)(pte_t);
-extern int (*pte_young)(pte_t);
-
-extern pte_t (*pte_wrprotect)(pte_t);
-extern pte_t (*pte_mkclean)(pte_t);
-extern pte_t (*pte_mkold)(pte_t);
-extern pte_t (*pte_mkwrite)(pte_t);
-extern pte_t (*pte_mkdirty)(pte_t);
-extern pte_t (*pte_mkyoung)(pte_t);
+BTFIXUPDEF_HALF(pte_writei)
+BTFIXUPDEF_HALF(pte_dirtyi)
+BTFIXUPDEF_HALF(pte_youngi)
+
+extern int pte_write(pte_t pte) __attribute__((const));
+extern __inline__ int pte_write(pte_t pte)
+{
+	return pte_val(pte) & BTFIXUP_HALF(pte_writei);
+}
+
+extern int pte_dirty(pte_t pte) __attribute__((const));
+extern __inline__ int pte_dirty(pte_t pte)
+{
+	return pte_val(pte) & BTFIXUP_HALF(pte_dirtyi);
+}
+
+extern int pte_young(pte_t pte) __attribute__((const));
+extern __inline__ int pte_young(pte_t pte)
+{
+	return pte_val(pte) & BTFIXUP_HALF(pte_youngi);
+}
+
+BTFIXUPDEF_HALF(pte_wrprotecti)
+BTFIXUPDEF_HALF(pte_mkcleani)
+BTFIXUPDEF_HALF(pte_mkoldi)
+
+extern pte_t pte_wrprotect(pte_t pte) __attribute__((const));
+extern __inline__ pte_t pte_wrprotect(pte_t pte)
+{
+	return __pte(pte_val(pte) & ~BTFIXUP_HALF(pte_wrprotecti));
+}
+
+extern pte_t pte_mkclean(pte_t pte) __attribute__((const));
+extern __inline__ pte_t pte_mkclean(pte_t pte)
+{
+	return __pte(pte_val(pte) & ~BTFIXUP_HALF(pte_mkcleani));
+}
+
+extern pte_t pte_mkold(pte_t pte) __attribute__((const));
+extern __inline__ pte_t pte_mkold(pte_t pte)
+{
+	return __pte(pte_val(pte) & ~BTFIXUP_HALF(pte_mkoldi));
+}
+
+BTFIXUPDEF_CALL_CONST(pte_t, pte_mkwrite, pte_t)
+BTFIXUPDEF_CALL_CONST(pte_t, pte_mkdirty, pte_t)
+BTFIXUPDEF_CALL_CONST(pte_t, pte_mkyoung, pte_t)
+
+#define pte_mkwrite(pte) BTFIXUP_CALL(pte_mkwrite)(pte)
+#define pte_mkdirty(pte) BTFIXUP_CALL(pte_mkdirty)(pte)
+#define pte_mkyoung(pte) BTFIXUP_CALL(pte_mkyoung)(pte)
 
 /*
  * Conversion functions: convert a page and protection to a page entry,
  * and a page entry and page directory to the page they refer to.
  */
-extern pte_t (*mk_pte)(unsigned long, pgprot_t);
-extern pte_t (*mk_pte_phys)(unsigned long, pgprot_t);
-extern pte_t (*mk_pte_io)(unsigned long, pgprot_t, int);
+BTFIXUPDEF_CALL_CONST(pte_t, mk_pte, unsigned long, pgprot_t)
+BTFIXUPDEF_CALL_CONST(pte_t, mk_pte_phys, unsigned long, pgprot_t)
+BTFIXUPDEF_CALL_CONST(pte_t, mk_pte_io, unsigned long, pgprot_t, int)
 
-extern void (*pgd_set)(pgd_t *, pmd_t *);
+#define mk_pte(page,pgprot) BTFIXUP_CALL(mk_pte)(page,pgprot)
+#define mk_pte_phys(page,pgprot) BTFIXUP_CALL(mk_pte_phys)(page,pgprot)
+#define mk_pte_io(page,pgprot,space) BTFIXUP_CALL(mk_pte_io)(page,pgprot,space)
 
-extern pte_t (*pte_modify)(pte_t, pgprot_t);
+BTFIXUPDEF_CALL(void, pgd_set, pgd_t *, pmd_t *)
+
+#define pgd_set(pgdp,pmdp) BTFIXUP_CALL(pgd_set)(pgdp,pmdp)
+
+BTFIXUPDEF_INT(pte_modify_mask)
+
+extern pte_t pte_modify(pte_t pte, pgprot_t newprot) __attribute__((const));
+extern __inline__ pte_t pte_modify(pte_t pte, pgprot_t newprot)
+{
+	return __pte((pte_val(pte) & BTFIXUP_INT(pte_modify_mask)) |
+		pgprot_val(newprot));
+}
+
+BTFIXUPDEF_CALL(pgd_t *, pgd_offset, struct mm_struct *, unsigned long)
+BTFIXUPDEF_CALL(pmd_t *, pmd_offset, pgd_t *, unsigned long)
+BTFIXUPDEF_CALL(pte_t *, pte_offset, pmd_t *, unsigned long)
 
 /* to find an entry in a kernel page-table-directory */
 #define pgd_offset_k(address) pgd_offset(&init_mm, address)
 
 /* to find an entry in a page-table-directory */
-extern pgd_t * (*pgd_offset)(struct mm_struct *, unsigned long);
+#define pgd_offset(mm,addr) BTFIXUP_CALL(pgd_offset)(mm,addr)
 
 /* Find an entry in the second-level page table.. */
-extern pmd_t * (*pmd_offset)(pgd_t *, unsigned long);
+#define pmd_offset(dir,addr) BTFIXUP_CALL(pmd_offset)(dir,addr)
 
 /* Find an entry in the third-level page table.. */ 
-extern pte_t * (*pte_offset)(pmd_t *, unsigned long);
+#define pte_offset(dir,addr) BTFIXUP_CALL(pte_offset)(dir,addr)
+
+extern struct pgtable_cache_struct {
+	unsigned long *pgd_cache;
+	unsigned long *pte_cache;
+	unsigned long pgtable_cache_sz;
+	unsigned long pgd_cache_sz;
+	spinlock_t pgd_spinlock;
+	spinlock_t pte_spinlock;
+} pgt_quicklists;
+#define pgd_quicklist           (pgt_quicklists.pgd_cache)
+#define pmd_quicklist           ((unsigned long *)0)
+#define pte_quicklist           (pgt_quicklists.pte_cache)
+#define pgd_spinlock		(pgt_quicklists.pgd_spinlock)
+#define pte_spinlock		(pgt_quicklists.pte_spinlock)
+#define pgtable_cache_size      (pgt_quicklists.pgtable_cache_sz)
+#define pgd_cache_size		(pgt_quicklists.pgd_cache_sz)
+
+BTFIXUPDEF_CALL(pte_t *, get_pte_fast, void)
+BTFIXUPDEF_CALL(pgd_t *, get_pgd_fast, void)
+BTFIXUPDEF_CALL(void,    free_pte_slow, pte_t *)
+BTFIXUPDEF_CALL(void,    free_pgd_slow, pgd_t *)
+
+#define get_pte_fast() BTFIXUP_CALL(get_pte_fast)()
+extern __inline__ pmd_t *get_pmd_fast(void)
+{
+	return (pmd_t *)0;
+}
+#define get_pgd_fast() BTFIXUP_CALL(get_pgd_fast)()
+#define free_pte_slow(pte) BTFIXUP_CALL(free_pte_slow)(pte)
+extern __inline__ void free_pmd_slow(pmd_t *pmd)
+{
+}
+#define free_pgd_slow(pgd) BTFIXUP_CALL(free_pgd_slow)(pgd)
 
 /*
  * Allocate and free page tables. The xxx_kernel() versions are
  * used to allocate a kernel page table - this turns on ASN bits
  * if any, and marks the page tables reserved.
  */
-extern void (*pte_free_kernel)(pte_t *);
+BTFIXUPDEF_CALL(void,    pte_free_kernel, pte_t *)
+BTFIXUPDEF_CALL(pte_t *, pte_alloc_kernel, pmd_t *, unsigned long)
 
-extern pte_t * (*pte_alloc_kernel)(pmd_t *, unsigned long);
+#define pte_free_kernel(pte) BTFIXUP_CALL(pte_free_kernel)(pte)
+#define pte_alloc_kernel(pmd,addr) BTFIXUP_CALL(pte_alloc_kernel)(pmd,addr)
 
-/*
- * allocating and freeing a pmd is trivial: the 1-entry pmd is
- * inside the pgd, so has no extra memory associated with it.
- */
-extern void (*pmd_free_kernel)(pmd_t *);
+BTFIXUPDEF_CALL(void,    pmd_free_kernel, pmd_t *)
+BTFIXUPDEF_CALL(pmd_t *, pmd_alloc_kernel, pgd_t *, unsigned long)
 
-extern pmd_t * (*pmd_alloc_kernel)(pgd_t *, unsigned long);
+#define pmd_free_kernel(pmd) BTFIXUP_CALL(pmd_free_kernel)(pmd)
+#define pmd_alloc_kernel(pgd,addr) BTFIXUP_CALL(pmd_alloc_kernel)(pgd,addr)
 
-extern void (*pte_free)(pte_t *);
+BTFIXUPDEF_CALL(void,    pte_free, pte_t *)
+BTFIXUPDEF_CALL(pte_t *, pte_alloc, pmd_t *, unsigned long)
 
-extern pte_t * (*pte_alloc)(pmd_t *, unsigned long);
+#define pte_free(pte) BTFIXUP_CALL(pte_free)(pte)
+#define pte_alloc(pmd,addr) BTFIXUP_CALL(pte_alloc)(pmd,addr)
 
-/*
- * allocating and freeing a pmd is trivial: the 1-entry pmd is
- * inside the pgd, so has no extra memory associated with it.
- */
-extern void (*pmd_free)(pmd_t *);
+BTFIXUPDEF_CALL(void,    pmd_free, pmd_t *)
+BTFIXUPDEF_CALL(pmd_t *, pmd_alloc, pgd_t *, unsigned long)
 
-extern pmd_t * (*pmd_alloc)(pgd_t *, unsigned long);
+#define pmd_free(pmd) BTFIXUP_CALL(pmd_free)(pmd)
+#define pmd_alloc(pgd,addr) BTFIXUP_CALL(pmd_alloc)(pgd,addr)
 
-extern void (*pgd_free)(pgd_t *);
+BTFIXUPDEF_CALL(void,    pgd_free, pgd_t *)
+BTFIXUPDEF_CALL(pgd_t *, pgd_alloc, void)
 
-extern pgd_t * (*pgd_alloc)(void);
+#define pgd_free(pgd) BTFIXUP_CALL(pgd_free)(pgd)
+#define pgd_alloc() BTFIXUP_CALL(pgd_alloc)()
 
 /* Fine grained cache/tlb flushing. */
 
 #ifdef __SMP__
-extern void (*local_flush_cache_all)(void);
-extern void (*local_flush_cache_mm)(struct mm_struct *);
-extern void (*local_flush_cache_range)(struct mm_struct *, unsigned long start,
-				     unsigned long end);
-extern void (*local_flush_cache_page)(struct vm_area_struct *, unsigned long address);
-
-extern void (*local_flush_tlb_all)(void);
-extern void (*local_flush_tlb_mm)(struct mm_struct *);
-extern void (*local_flush_tlb_range)(struct mm_struct *, unsigned long start,
-				     unsigned long end);
-extern void (*local_flush_tlb_page)(struct vm_area_struct *, unsigned long address);
+BTFIXUPDEF_CALL(void, local_flush_cache_all, void)
+BTFIXUPDEF_CALL(void, local_flush_cache_mm, struct mm_struct *)
+BTFIXUPDEF_CALL(void, local_flush_cache_range, struct mm_struct *, unsigned long, unsigned long)
+BTFIXUPDEF_CALL(void, local_flush_cache_page, struct vm_area_struct *, unsigned long)
+
+#define local_flush_cache_all() BTFIXUP_CALL(local_flush_cache_all)()
+#define local_flush_cache_mm(mm) BTFIXUP_CALL(local_flush_cache_mm)(mm)
+#define local_flush_cache_range(mm,start,end) BTFIXUP_CALL(local_flush_cache_range)(mm,start,end)
+#define local_flush_cache_page(vma,addr) BTFIXUP_CALL(local_flush_cache_page)(vma,addr)
+
+BTFIXUPDEF_CALL(void, local_flush_tlb_all, void)
+BTFIXUPDEF_CALL(void, local_flush_tlb_mm, struct mm_struct *)
+BTFIXUPDEF_CALL(void, local_flush_tlb_range, struct mm_struct *, unsigned long, unsigned long)
+BTFIXUPDEF_CALL(void, local_flush_tlb_page, struct vm_area_struct *, unsigned long)
+
+#define local_flush_tlb_all() BTFIXUP_CALL(local_flush_tlb_all)()
+#define local_flush_tlb_mm(mm) BTFIXUP_CALL(local_flush_tlb_mm)(mm)
+#define local_flush_tlb_range(mm,start,end) BTFIXUP_CALL(local_flush_tlb_range)(mm,start,end)
+#define local_flush_tlb_page(vma,addr) BTFIXUP_CALL(local_flush_tlb_page)(vma,addr)
 
-extern void (*local_flush_page_to_ram)(unsigned long address);
+BTFIXUPDEF_CALL(void, local_flush_page_to_ram, unsigned long)
+BTFIXUPDEF_CALL(void, local_flush_sig_insns, struct mm_struct *, unsigned long)
 
-extern void (*local_flush_sig_insns)(struct mm_struct *mm, unsigned long insn_addr);
+#define local_flush_page_to_ram(addr) BTFIXUP_CALL(local_flush_page_to_ram)(addr)
+#define local_flush_sig_insns(mm,insn_addr) BTFIXUP_CALL(local_flush_sig_insns)(mm,insn_addr)
 
 extern void smp_flush_cache_all(void);
 extern void smp_flush_cache_mm(struct mm_struct *mm);
@@ -293,47 +448,62 @@
 extern void smp_flush_sig_insns(struct mm_struct *mm, unsigned long insn_addr);
 #endif
 
-extern void (*flush_cache_all)(void);
-extern void (*flush_cache_mm)(struct mm_struct *);
-extern void (*flush_cache_range)(struct mm_struct *, unsigned long start,
-				 unsigned long end);
-extern void (*flush_cache_page)(struct vm_area_struct *, unsigned long address);
+BTFIXUPDEF_CALL(void, flush_cache_all, void)
+BTFIXUPDEF_CALL(void, flush_cache_mm, struct mm_struct *)
+BTFIXUPDEF_CALL(void, flush_cache_range, struct mm_struct *, unsigned long, unsigned long)
+BTFIXUPDEF_CALL(void, flush_cache_page, struct vm_area_struct *, unsigned long)
+
+#define flush_cache_all() BTFIXUP_CALL(flush_cache_all)()
+#define flush_cache_mm(mm) BTFIXUP_CALL(flush_cache_mm)(mm)
+#define flush_cache_range(mm,start,end) BTFIXUP_CALL(flush_cache_range)(mm,start,end)
+#define flush_cache_page(vma,addr) BTFIXUP_CALL(flush_cache_page)(vma,addr)
 #define flush_icache_range(start, end)		do { } while (0)
 
-extern void (*flush_tlb_all)(void);
-extern void (*flush_tlb_mm)(struct mm_struct *);
-extern void (*flush_tlb_range)(struct mm_struct *, unsigned long start, unsigned long end);
-extern void (*flush_tlb_page)(struct vm_area_struct *, unsigned long address);
+BTFIXUPDEF_CALL(void, flush_tlb_all, void)
+BTFIXUPDEF_CALL(void, flush_tlb_mm, struct mm_struct *)
+BTFIXUPDEF_CALL(void, flush_tlb_range, struct mm_struct *, unsigned long, unsigned long)
+BTFIXUPDEF_CALL(void, flush_tlb_page, struct vm_area_struct *, unsigned long)
+
+#define flush_tlb_all() BTFIXUP_CALL(flush_tlb_all)()
+#define flush_tlb_mm(mm) BTFIXUP_CALL(flush_tlb_mm)(mm)
+#define flush_tlb_range(mm,start,end) BTFIXUP_CALL(flush_tlb_range)(mm,start,end)
+#define flush_tlb_page(vma,addr) BTFIXUP_CALL(flush_tlb_page)(vma,addr)
 
-extern void (*flush_page_to_ram)(unsigned long page);
+BTFIXUPDEF_CALL(void, flush_page_to_ram, unsigned long)
+BTFIXUPDEF_CALL(void, flush_sig_insns, struct mm_struct *, unsigned long)
 
-extern void (*flush_sig_insns)(struct mm_struct *mm, unsigned long insn_addr);
+#define flush_page_to_ram(addr) BTFIXUP_CALL(flush_page_to_ram)(addr)
+#define flush_sig_insns(mm,insn_addr) BTFIXUP_CALL(flush_sig_insns)(mm,insn_addr)
 
 /* The permissions for pgprot_val to make a page mapped on the obio space */
 extern unsigned int pg_iobits;
 
 /* MMU context switching. */
-extern void (*switch_to_context)(struct task_struct *tsk);
+BTFIXUPDEF_CALL(void, switch_to_context, struct task_struct *)
+
+#define switch_to_context(tsk) BTFIXUP_CALL(switch_to_context)(tsk)
 
 /* Certain architectures need to do special things when pte's
  * within a page table are directly modified.  Thus, the following
  * hook is made available.
  */
 
-#if 0 /* XXX try this soon XXX */
-extern void (*set_pte)(struct vm_area_struct *vma, unsigned long address,
-		       pte_t *pteptr, pte_t pteval);
-#else
-extern void (*set_pte)(pte_t *pteptr, pte_t pteval);
-#endif
+BTFIXUPDEF_CALL(void, set_pte, pte_t *, pte_t)
+
+#define set_pte(ptep,pteval) BTFIXUP_CALL(set_pte)(ptep,pteval)
 
-extern char *(*mmu_info)(void);
+BTFIXUPDEF_CALL(int, mmu_info, char *)
+
+#define mmu_info(p) BTFIXUP_CALL(mmu_info)(p)
 
 /* Fault handler stuff... */
 #define FAULT_CODE_PROT     0x1
 #define FAULT_CODE_WRITE    0x2
 #define FAULT_CODE_USER     0x4
-extern void (*update_mmu_cache)(struct vm_area_struct *vma, unsigned long address, pte_t pte);
+
+BTFIXUPDEF_CALL(void, update_mmu_cache, struct vm_area_struct *, unsigned long, pte_t)
+
+#define update_mmu_cache(vma,addr,pte) BTFIXUP_CALL(update_mmu_cache)(vma,addr,pte)
 
 extern int invalid_segment;
 
@@ -373,6 +543,7 @@
 __get_phys (unsigned long addr)
 {
 	switch (sparc_cpu_model){
+	case sun4:
 	case sun4c:
 		return sun4c_get_pte (addr) << PAGE_SHIFT;
 	case sun4m:
@@ -387,6 +558,7 @@
 __get_iospace (unsigned long addr)
 {
 	switch (sparc_cpu_model){
+	case sun4:
 	case sun4c:
 		return -1; /* Don't check iospace on sun4c */
 	case sun4m:
@@ -399,5 +571,9 @@
 
 #define module_map      vmalloc
 #define module_unmap    vfree
+#define module_shrink	vshrink
+
+/* Needs to be defined here and not in linux/mm.h, as it is arch dependent */
+#define PageSkip(page)		(test_bit(PG_skip, &(page)->flags))
 
 #endif /* !(_SPARC_PGTABLE_H) */
diff -u --recursive --new-file v2.1.96/linux/include/asm-sparc/pgtsun4.h linux/include/asm-sparc/pgtsun4.h
--- v2.1.96/linux/include/asm-sparc/pgtsun4.h	Sat Nov  9 00:29:55 1996
+++ linux/include/asm-sparc/pgtsun4.h	Tue Apr 14 17:44:23 1998
@@ -1,10 +1,161 @@
-/* $Id: pgtsun4.h,v 1.2 1995/11/25 02:32:26 davem Exp $
- * pgtsun4.c:  Regular Sun4 MMU support goes in here.
+/* $Id: pgtsun4.h,v 1.3 1998/01/30 11:00:01 jj Exp $
+ * pgtsun4.h:  Sun4 specific pgtable.h defines and code.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
  */
+#ifndef _SPARC_PGTSUN4C_H
+#define _SPARC_PGTSUN4C_H
 
-#ifndef _SPARC_PGTSUN4_H
-#define _SPARC_PGTSUN4_H
+#include <asm/contregs.h>
+
+/* PMD_SHIFT determines the size of the area a second-level page table can map */
+#define SUN4C_PMD_SHIFT       23
+#define SUN4C_PMD_SIZE        (1UL << SUN4C_PMD_SHIFT)
+#define SUN4C_PMD_MASK        (~(SUN4C_PMD_SIZE-1))
+#define SUN4C_PMD_ALIGN(addr) (((addr)+SUN4C_PMD_SIZE-1)&SUN4C_PMD_MASK)
+
+/* PGDIR_SHIFT determines what a third-level page table entry can map */
+#define SUN4C_PGDIR_SHIFT       23
+#define SUN4C_PGDIR_SIZE        (1UL << SUN4C_PGDIR_SHIFT)
+#define SUN4C_PGDIR_MASK        (~(SUN4C_PGDIR_SIZE-1))
+#define SUN4C_PGDIR_ALIGN(addr) (((addr)+SUN4C_PGDIR_SIZE-1)&SUN4C_PGDIR_MASK)
+
+/* To represent how the sun4c mmu really lays things out. */
+#define SUN4C_REAL_PGDIR_SHIFT       18
+#define SUN4C_REAL_PGDIR_SIZE        (1UL << SUN4C_REAL_PGDIR_SHIFT)
+#define SUN4C_REAL_PGDIR_MASK        (~(SUN4C_REAL_PGDIR_SIZE-1))
+#define SUN4C_REAL_PGDIR_ALIGN(addr) (((addr)+SUN4C_REAL_PGDIR_SIZE-1)&SUN4C_REAL_PGDIR_MASK)
+
+/* 19 bit PFN on sun4 */
+#define SUN4C_PFN_MASK 0x7ffff
+ 
+/* Don't increase these unless the structures in sun4c.c are fixed */
+#define SUN4C_MAX_SEGMAPS 256
+#define SUN4C_MAX_CONTEXTS 16
+
+/*
+ * To be efficient, and not have to worry about allocating such
+ * a huge pgd, we make the kernel sun4c tables each hold 1024
+ * entries and the pgd similarly just like the i386 tables.
+ */
+#define SUN4C_PTRS_PER_PTE    1024
+#define SUN4C_PTRS_PER_PMD    1
+#define SUN4C_PTRS_PER_PGD    1024
+
+/* On the sun4 the physical ram limit is 128MB.  We set up our I/O
+ * translations at KERNBASE + 128MB for 1MB, then we begin the VMALLOC
+ * area, makes sense.  This works out to the value below.
+ */
+#define SUN4C_VMALLOC_START   (0xfe300000)
+
+/*
+ * Sparc SUN4C pte fields.
+ */
+#define _SUN4C_PAGE_VALID        0x80000000
+#define _SUN4C_PAGE_SILENT_READ  0x80000000   /* synonym */
+#define _SUN4C_PAGE_DIRTY        0x40000000
+#define _SUN4C_PAGE_SILENT_WRITE 0x40000000   /* synonym */
+#define _SUN4C_PAGE_PRIV         0x20000000   /* privileged page */
+#define _SUN4C_PAGE_NOCACHE      0x10000000   /* non-cacheable page */
+#define _SUN4C_PAGE_PRESENT      0x08000000   /* implemented in software */
+#define _SUN4C_PAGE_IO           0x04000000   /* I/O page */
+#define _SUN4C_PAGE_READ         0x00800000   /* implemented in software */
+#define _SUN4C_PAGE_WRITE        0x00400000   /* implemented in software */
+#define _SUN4C_PAGE_ACCESSED     0x00200000   /* implemented in software */
+#define _SUN4C_PAGE_MODIFIED     0x00100000   /* implemented in software */
+
+#define _SUN4C_READABLE		(_SUN4C_PAGE_READ|_SUN4C_PAGE_SILENT_READ|\
+				 _SUN4C_PAGE_ACCESSED)
+#define _SUN4C_WRITEABLE	(_SUN4C_PAGE_WRITE|_SUN4C_PAGE_SILENT_WRITE|\
+				 _SUN4C_PAGE_MODIFIED)
+
+#define _SUN4C_PAGE_CHG_MASK	(0xffff|_SUN4C_PAGE_ACCESSED|_SUN4C_PAGE_MODIFIED)
+
+#define SUN4C_PAGE_NONE		__pgprot(_SUN4C_PAGE_PRESENT)
+#define SUN4C_PAGE_SHARED	__pgprot(_SUN4C_PAGE_PRESENT|_SUN4C_READABLE|\
+					 _SUN4C_PAGE_WRITE)
+#define SUN4C_PAGE_COPY		__pgprot(_SUN4C_PAGE_PRESENT|_SUN4C_READABLE)
+#define SUN4C_PAGE_READONLY	__pgprot(_SUN4C_PAGE_PRESENT|_SUN4C_READABLE)
+#define SUN4C_PAGE_KERNEL	__pgprot(_SUN4C_READABLE|_SUN4C_WRITEABLE|\
+					 _SUN4C_PAGE_DIRTY|_SUN4C_PAGE_PRIV)
+
+#ifndef __ASSEMBLY__
+
+extern __inline__ unsigned long sun4c_get_synchronous_error(void)
+{
+	unsigned long sync_err;
+
+	__asm__ __volatile__("lda [%1] %2, %0\n\t" :
+			     "=r" (sync_err) :
+			     "r" (AC_SYNC_ERR), "i" (ASI_CONTROL));
+	return sync_err;
+}
+
+extern __inline__ unsigned long sun4c_get_synchronous_address(void)
+{
+	unsigned long sync_addr;
+
+	__asm__ __volatile__("lda [%1] %2, %0\n\t" :
+			     "=r" (sync_addr) :
+			     "r" (AC_SYNC_VA), "i" (ASI_CONTROL));
+	return sync_addr;
+}
+
+/* SUN4 pte, segmap, and context manipulation */
+extern __inline__ unsigned long sun4c_get_segmap(unsigned long addr)
+{
+  register unsigned long entry;
+
+  __asm__ __volatile__("\n\tlduha [%1] %2, %0\n\t" : 
+		       "=r" (entry) :
+		       "r" (addr), "i" (ASI_SEGMAP));
+  return entry;
+}
+
+extern __inline__ void sun4c_put_segmap(unsigned long addr, unsigned long entry)
+{
+  __asm__ __volatile__("\n\tstha %1, [%0] %2; nop; nop; nop;\n\t" : :
+		       "r" (addr), "r" (entry),
+		       "i" (ASI_SEGMAP));
+}
+
+extern __inline__ unsigned long sun4c_get_pte(unsigned long addr)
+{
+  register unsigned long entry;
+
+  __asm__ __volatile__("\n\tlda [%1] %2, %0\n\t" : 
+		       "=r" (entry) :
+		       "r" (addr), "i" (ASI_PTE));
+  return entry;
+}
+
+extern __inline__ void sun4c_put_pte(unsigned long addr, unsigned long entry)
+{
+  __asm__ __volatile__("\n\tsta %1, [%0] %2; nop; nop; nop;\n\t" : :
+		       "r" (addr), 
+		       "r" ((entry & ~(_SUN4C_PAGE_PRESENT))), "i" (ASI_PTE));
+}
+
+extern __inline__ int sun4c_get_context(void)
+{
+  register int ctx;
+
+  __asm__ __volatile__("\n\tlduba [%1] %2, %0\n\t" :
+		       "=r" (ctx) :
+		       "r" (AC_CONTEXT), "i" (ASI_CONTROL));
+
+  return ctx;
+}
+
+extern __inline__ int sun4c_set_context(int ctx)
+{
+  __asm__ __volatile__("\n\tstba %0, [%1] %2; nop; nop; nop;\n\t" : :
+		       "r" (ctx), "r" (AC_CONTEXT), "i" (ASI_CONTROL));
+
+  return ctx;
+}
+
+#endif /* !(__ASSEMBLY__) */
 
 #endif /* !(_SPARC_PGTSUN4_H) */
diff -u --recursive --new-file v2.1.96/linux/include/asm-sparc/pgtsun4c.h linux/include/asm-sparc/pgtsun4c.h
--- v2.1.96/linux/include/asm-sparc/pgtsun4c.h	Mon Apr 14 16:28:21 1997
+++ linux/include/asm-sparc/pgtsun4c.h	Tue Apr 14 17:44:23 1998
@@ -1,4 +1,4 @@
-/* $Id: pgtsun4c.h,v 1.34 1997/03/23 03:47:08 davem Exp $
+/* $Id: pgtsun4c.h,v 1.35 1998/01/30 11:00:05 jj Exp $
  * pgtsun4c.h:  Sun4c specific pgtable.h defines and code.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -25,6 +25,13 @@
 #define SUN4C_REAL_PGDIR_SIZE        (1UL << SUN4C_REAL_PGDIR_SHIFT)
 #define SUN4C_REAL_PGDIR_MASK        (~(SUN4C_REAL_PGDIR_SIZE-1))
 #define SUN4C_REAL_PGDIR_ALIGN(addr) (((addr)+SUN4C_REAL_PGDIR_SIZE-1)&SUN4C_REAL_PGDIR_MASK)
+
+/* 16 bit PFN on sun4c */
+#define SUN4C_PFN_MASK 0xffff
+
+/* Don't increase these unless the structures in sun4c.c are fixed */
+#define SUN4C_MAX_SEGMAPS 256
+#define SUN4C_MAX_CONTEXTS 16
 
 /*
  * To be efficient, and not have to worry about allocating such
diff -u --recursive --new-file v2.1.96/linux/include/asm-sparc/processor.h linux/include/asm-sparc/processor.h
--- v2.1.96/linux/include/asm-sparc/processor.h	Mon Jan 12 15:15:54 1998
+++ linux/include/asm-sparc/processor.h	Tue Apr 14 17:44:23 1998
@@ -1,4 +1,4 @@
-/* $Id: processor.h,v 1.61 1997/10/22 09:25:42 jj Exp $
+/* $Id: processor.h,v 1.62 1998/02/05 14:20:02 jj Exp $
  * include/asm-sparc/processor.h
  *
  * Copyright (C) 1994 David S. Miller (davem@caip.rutgers.edu)
@@ -14,6 +14,8 @@
 #include <asm/head.h>
 #include <asm/signal.h>
 #include <asm/segment.h>
+#include <asm/btfixup.h>
+#include <asm/page.h>
 
 /*
  * Bus types
@@ -33,7 +35,7 @@
  * That one page is used to protect kernel from intruders, so that
  * we can make our access_ok test faster
  */
-#define TASK_SIZE	(page_offset)
+#define TASK_SIZE	PAGE_OFFSET
 
 #define COPY_TASK_STRUCT(dst, src) 	\
 do {					\
@@ -155,8 +157,11 @@
 #ifdef __KERNEL__
 
 /* Allocation and freeing of basic task resources. */
-extern struct task_struct *(*alloc_task_struct)(void);
-extern void (*free_task_struct)(struct task_struct *tsk);
+BTFIXUPDEF_CALL(struct task_struct *, alloc_task_struct, void)
+BTFIXUPDEF_CALL(void, free_task_struct, struct task_struct *)
+
+#define alloc_task_struct() BTFIXUP_CALL(alloc_task_struct)()
+#define free_task_struct(tsk) BTFIXUP_CALL(free_task_struct)(tsk)
 
 #define init_task	(init_task_union.task)
 #define init_stack	(init_task_union.stack)
diff -u --recursive --new-file v2.1.96/linux/include/asm-sparc/sbi.h linux/include/asm-sparc/sbi.h
--- v2.1.96/linux/include/asm-sparc/sbi.h	Mon Jan 12 15:15:54 1998
+++ linux/include/asm-sparc/sbi.h	Tue Apr 14 17:44:23 1998
@@ -1,4 +1,4 @@
-/* $Id: sbi.h,v 1.1 1997/11/19 15:12:16 jj Exp $
+/* $Id: sbi.h,v 1.2 1998/03/09 14:04:48 jj Exp $
  * sbi.h:  SBI (Sbus Interface on sun4d) definitions
  *
  * Copyright (C) 1997 Jakub Jelinek <jj@sunsite.mff.cuni.cz>
@@ -80,6 +80,14 @@
 	__asm__ __volatile__ ("sta %0, [%1] %2" : :
 			      "r" (mask),
 			      "r" (ECSR_DEV_BASE(devid) | SBI_INTR_STATE),
+			      "i" (ASI_M_CTL));
+}
+
+extern __inline__ void set_sbi_tid(int devid, int targetid)
+{
+	__asm__ __volatile__ ("sta %0, [%1] %2" : :
+			      "r" (targetid),
+			      "r" (ECSR_DEV_BASE(devid) | SBI_INTR_TID),
 			      "i" (ASI_M_CTL));
 }
 
diff -u --recursive --new-file v2.1.96/linux/include/asm-sparc/sbus.h linux/include/asm-sparc/sbus.h
--- v2.1.96/linux/include/asm-sparc/sbus.h	Mon Jan 12 15:15:54 1998
+++ linux/include/asm-sparc/sbus.h	Tue Apr 14 17:44:23 1998
@@ -1,4 +1,4 @@
-/* $Id: sbus.h,v 1.13 1997/11/19 15:12:18 jj Exp $
+/* $Id: sbus.h,v 1.14 1998/03/09 14:04:56 jj Exp $
  * sbus.h:  Defines for the Sun SBus.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -47,8 +47,7 @@
   struct linux_sbus_device *child;     /* For ledma and espdma on sun4m */
   struct linux_sbus *my_bus;           /* Back ptr to sbus */
   int prom_node;                       /* PROM device tree node for this device */
-  char prom_name[64];                  /* PROM device name */
-  char linux_name[64];                 /* Name used internally by Linux */
+  char prom_name[32];                  /* PROM device name */
 
   struct linux_prom_registers reg_addrs[PROMREG_MAX];
   int num_registers, ranges_applied;
diff -u --recursive --new-file v2.1.96/linux/include/asm-sparc/smp.h linux/include/asm-sparc/smp.h
--- v2.1.96/linux/include/asm-sparc/smp.h	Thu Jul 31 13:09:18 1997
+++ linux/include/asm-sparc/smp.h	Tue Apr 14 17:44:23 1998
@@ -6,7 +6,9 @@
 #ifndef _SPARC_SMP_H
 #define _SPARC_SMP_H
 
+#include <linux/tasks.h>
 #include <asm/head.h>
+#include <asm/btfixup.h>
 
 #ifndef __ASSEMBLY__
 /* PROM provided per-processor information we need
@@ -25,7 +27,10 @@
 
 #ifndef __ASSEMBLY__
 
-extern struct prom_cpuinfo linux_cpus[NCPUS];
+#include <asm/ptrace.h>
+#include <asm/asi.h>
+
+extern struct prom_cpuinfo linux_cpus[NR_CPUS];
 
 /* Per processor Sparc parameters we need. */
 
@@ -36,6 +41,7 @@
 };
 
 extern struct cpuinfo_sparc cpu_data[NR_CPUS];
+extern unsigned long cpu_offset[NR_CPUS];
 
 struct klock_info {
 	unsigned char kernel_flag;
@@ -61,12 +67,28 @@
 /*
  *	General functions that each host system must provide.
  */
+ 
+void sun4m_init_smp(void);
+void sun4d_init_smp(void);
+
+void smp_callin(void);
+void smp_boot_cpus(void);
+void smp_store_cpu_info(int);
+
+BTFIXUPDEF_CALL(void, smp_cross_call, smpfunc_t, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long)
+BTFIXUPDEF_CALL(void, smp_message_pass, int, int, unsigned long, int)
+BTFIXUPDEF_CALL(int, __smp_processor_id, void)
+BTFIXUPDEF_BLACKBOX(smp_processor_id)
+BTFIXUPDEF_BLACKBOX(load_current)
+
+#define smp_cross_call(func,arg1,arg2,arg3,arg4,arg5) BTFIXUP_CALL(smp_cross_call)(func,arg1,arg2,arg3,arg4,arg5)
+#define smp_message_pass(target,msg,data,wait) BTFIXUP_CALL(smp_message_pass)(target,msg,data,wait)
 
-extern void smp_callin(void);
-extern void smp_boot_cpus(void);
-extern void smp_store_cpu_info(int id);
-extern void smp_cross_call(smpfunc_t func, unsigned long arg1, unsigned long arg2,
-			   unsigned long arg3, unsigned long arg4, unsigned long arg5);
+BTFIXUPDEF_CALL(int, smp_bogo_info, char *)
+BTFIXUPDEF_CALL(int, smp_info, char *)
+
+#define smp_bogo_info(buf) BTFIXUP_CALL(smp_bogo_info)(buf)
+#define smp_info(buf) BTFIXUP_CALL(smp_info)(buf)
 
 extern __inline__ void xc0(smpfunc_t func) { smp_cross_call(func, 0, 0, 0, 0, 0); }
 extern __inline__ void xc1(smpfunc_t func, unsigned long arg1)
@@ -84,10 +106,15 @@
 { smp_cross_call(func, arg1, arg2, arg3, arg4, arg5); }
 
 extern __volatile__ int cpu_number_map[NR_CPUS];
-extern __volatile__ int cpu_logical_map[NR_CPUS];
+extern __volatile__ int __cpu_logical_map[NR_CPUS];
 extern unsigned long smp_proc_in_lock[NR_CPUS];
 
-extern __inline__ int hard_smp_processor_id(void)
+extern __inline__ int cpu_logical_map(int cpu)
+{
+	return __cpu_logical_map[cpu];
+}
+
+extern __inline__ int hard_smp4m_processor_id(void)
 {
 	int cpuid;
 
@@ -98,6 +125,49 @@
 	return cpuid;
 }
 
+extern __inline__ int hard_smp4d_processor_id(void)
+{
+	int cpuid;
+
+	__asm__ __volatile__("lda [%%g0] %1, %0\n\t" :
+			     "=&r" (cpuid) : "i" (ASI_M_VIKING_TMP1));
+	return cpuid;
+}
+
+#ifndef MODULE
+extern __inline__ int hard_smp_processor_id(void)
+{
+	int cpuid;
+
+	/* Black box - sun4m
+		__asm__ __volatile__("rd %%tbr, %0\n\t"
+				     "srl %0, 12, %0\n\t"
+				     "and %0, 3, %0\n\t" :
+				     "=&r" (cpuid));
+	             - sun4d
+	   	__asm__ __volatile__("lda [%g0] ASI_M_VIKING_TMP1, %0\n\t"
+	   			     "nop; nop" :
+	   			     "=&r" (cpuid));
+	   See btfixup.h and btfixupprep.c to understand how a blackbox works.
+	 */
+	__asm__ __volatile__("sethi %%hi(___b_smp_processor_id), %0\n\t"
+			     "sethi %%hi(boot_cpu_id), %0\n\t"
+			     "ldub [%0 + %%lo(boot_cpu_id)], %0\n\t" :
+			     "=&r" (cpuid));
+	return cpuid;
+}
+#else
+extern __inline__ int hard_smp_processor_id(void)
+{
+	int cpuid __asm__ ("g2");
+	
+	__asm__ __volatile__("mov %%o7, %%g1\n\t"
+			     "call ___f___smp_processor_id\n\t"
+			     " nop\n\t" : "=r"(cpuid) : : "g1");
+	return cpuid;
+}
+#endif
+
 #define smp_processor_id() hard_smp_processor_id()
 
 #endif /* !(__ASSEMBLY__) */
@@ -121,6 +191,13 @@
 #define SMP_FROM_INT		1
 #define SMP_FROM_SYSCALL	2
 
+#else /* !(__SMP__) */
+#ifndef __ASSEMBLY__ 
+extern __inline__ int cpu_logical_map(int cpu)
+{
+	return cpu;
+}
+#endif 
 #endif /* !(__SMP__) */
 
 #define NO_PROC_ID            0xFF
diff -u --recursive --new-file v2.1.96/linux/include/asm-sparc/softirq.h linux/include/asm-sparc/softirq.h
--- v2.1.96/linux/include/asm-sparc/softirq.h	Wed Apr 23 19:01:28 1997
+++ linux/include/asm-sparc/softirq.h	Tue Apr 14 17:44:23 1998
@@ -72,7 +72,7 @@
 	spin_unlock_irqrestore(&global_bh_lock, flags);	\
 } while(0)
 
-#define softirq_trylock()					\
+#define softirq_trylock(cpu)					\
 ({								\
 	int ret = 1;						\
 	if(atomic_add_return(1, &__sparc_bh_counter) != 1) {	\
@@ -81,7 +81,7 @@
 	}							\
 	ret;							\
 })
-#define softirq_endlock()	atomic_dec(&__sparc_bh_counter)
+#define softirq_endlock(cpu)	atomic_dec(&__sparc_bh_counter)
 #define clear_active_bhs(mask)				\
 do {	unsigned long flags;				\
 	spin_lock_irqsave(&global_bh_lock, flags);	\
@@ -96,8 +96,8 @@
 #define start_bh_atomic()	do { __sparc_bh_counter++; barrier(); } while(0)
 #define end_bh_atomic()		do { barrier(); __sparc_bh_counter--; } while(0)
 
-#define softirq_trylock() (__sparc_bh_counter ? 0 : (__sparc_bh_counter=1))
-#define softirq_endlock() (__sparc_bh_counter = 0)
+#define softirq_trylock(cpu) (__sparc_bh_counter ? 0 : (__sparc_bh_counter=1))
+#define softirq_endlock(cpu) (__sparc_bh_counter = 0)
 #define clear_active_bhs(x)	(bh_active &= ~(x))
 
 #define init_bh(nr, routine)	\
diff -u --recursive --new-file v2.1.96/linux/include/asm-sparc/stat.h linux/include/asm-sparc/stat.h
--- v2.1.96/linux/include/asm-sparc/stat.h	Sat Nov  9 11:39:35 1996
+++ linux/include/asm-sparc/stat.h	Tue Apr 14 17:44:23 1998
@@ -1,4 +1,4 @@
-/* $Id: stat.h,v 1.7 1996/10/27 08:55:48 davem Exp $ */
+/* $Id: stat.h,v 1.8 1998/02/06 12:52:07 jj Exp $ */
 #ifndef _SPARC_STAT_H
 #define _SPARC_STAT_H
 
@@ -37,5 +37,41 @@
 	off_t   st_blocks;
 	unsigned long  __unused4[2];
 };
+
+typedef struct {
+	__u32		major;
+	__u32		minor;
+} __new_dev_t;
+
+struct stat64 {
+	__new_dev_t	st_dev;
+	__u64		st_ino;
+	__u32		st_mode;
+	__u32		st_nlink;
+	__s32		st_uid;
+	__s32		st_gid;
+	__new_dev_t	st_rdev;
+	__s64		st_size;
+	__u64		st_blocks;
+	__s32		st_atime;
+	__u32		__unused1;
+	__s32		st_mtime;
+	__u32		__unused2;
+	__s32		st_ctime;
+	__u32		__unused3;
+	__u32		st_blksize;
+	__u32		__unused4;
+};
+
+#define __XSTAT_VER_1		1
+#define __XSTAT_VER_2		2
+#define __XSTAT_VER_MASK	0xff
+
+#define __XSTAT_VER_XSTAT	0x000
+#define __XSTAT_VER_LXSTAT	0x100
+#define __XSTAT_VER_FXSTAT	0x200
+#define __XSTAT_VER_TYPEMASK	0xff00
+
+#define __XMKNOD_VER_1		1
 
 #endif
diff -u --recursive --new-file v2.1.96/linux/include/asm-sparc/string.h linux/include/asm-sparc/string.h
--- v2.1.96/linux/include/asm-sparc/string.h	Mon Jan 12 15:15:54 1998
+++ linux/include/asm-sparc/string.h	Tue Apr 14 17:44:24 1998
@@ -1,4 +1,4 @@
-/* $Id: string.h,v 1.33 1997/11/19 07:57:48 jj Exp $
+/* $Id: string.h,v 1.34 1998/01/30 10:59:55 jj Exp $
  * string.h: External definitions for optimized assembly string
  *           routines for the Linux Kernel.
  *
@@ -9,6 +9,8 @@
 #ifndef __SPARC_STRING_H__
 #define __SPARC_STRING_H__
 
+#include <asm/page.h>
+
 /* Really, userland/ksyms should not see any of this stuff. */
 
 #ifdef __KERNEL__
@@ -40,7 +42,7 @@
 		__builtin_memcpy(to, from, n);
 	} else {
 		switch(n) {
-		case 4096:
+		case PAGE_SIZE:
 			__copy_1page(to, from);
 			break;
 		default:
@@ -71,7 +73,7 @@
 	extern __kernel_size_t __bzero(void *, __kernel_size_t);
 
 	if(!c) {
-		if(count == 4096)
+		if(count == PAGE_SIZE)
 			bzero_1page(s);
 		else
 			__bzero(s, count);
diff -u --recursive --new-file v2.1.96/linux/include/asm-sparc/sun4paddr.h linux/include/asm-sparc/sun4paddr.h
--- v2.1.96/linux/include/asm-sparc/sun4paddr.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-sparc/sun4paddr.h	Tue Apr 14 17:44:24 1998
@@ -0,0 +1,19 @@
+/* $Id: sun4paddr.h,v 1.2 1998/02/09 13:26:41 jj Exp $
+ * sun4paddr.h:  Various physical addresses on sun4 machines
+ *
+ * Copyright (C) 1997 Anton Blanchard (anton@progsoc.uts.edu.au)
+ */
+
+#ifndef _SPARC_SUN4PADDR_H
+#define _SPARC_SUN4PADDR_H
+
+#define SUN4_MEMREG_PHYSADDR		0xf4000000
+#define SUN4_IE_PHYSADDR		0xf5000000
+#define SUN4_300_MOSTEK_PHYSADDR	0xf2000000
+#define SUN4_300_TIMER_PHYSADDR		0xef000000
+#define SUN4_300_ETH_PHYSADDR		0xf9000000
+#define SUN4_300_BWTWO_PHYSADDR		0xfb400000
+#define SUN4_300_DMA_PHYSADDR		0xfa001000
+#define SUN4_300_ESP_PHYSADDR		0xfa000000
+
+#endif /* !(_SPARC_SUN4PADDR_H) */
diff -u --recursive --new-file v2.1.96/linux/include/asm-sparc/sun4prom.h linux/include/asm-sparc/sun4prom.h
--- v2.1.96/linux/include/asm-sparc/sun4prom.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-sparc/sun4prom.h	Tue Apr 14 17:44:24 1998
@@ -0,0 +1,83 @@
+/*
+ * sun4prom.h -- interface to sun4 PROM monitor.  We don't use most of this,
+ *               so most of these are just placeholders.
+ */
+
+#ifndef _SUN4PROM_H_
+#define _SUN4PROM_H_
+
+/*
+ * Although this looks similar to an romvec for a OpenProm machine, it is 
+ * actually closer to what was used in the Sun2 and Sun3.
+ *
+ * V2 entries exist only in version 2 PROMs and later, V3 in version 3 and later.
+ * 
+ * Many of the function prototypes are guesses.  Some are certainly wrong.
+ * Use with care.
+ */
+
+typedef struct {
+	char		*initSP;		/* Initial system stack ptr */
+	void		(*startmon)(void);	/* Initial PC for hardware */
+	int		*diagberr;		/* Bus err handler for diags */
+	struct linux_arguments_v0 **bootParam; /* Info for bootstrapped pgm */
+ 	unsigned int	*memorysize;		/* Usable memory in bytes */
+	unsigned char	(*getchar)(void);	/* Get char from input device */ 
+	void		(*putchar)(char);	/* Put char to output device */
+	int		(*mayget)(void);	/* Maybe get char, or -1 */
+	int		(*mayput)(void);	/* Maybe put char, or -1 */
+	unsigned char	*echo;			/* Should getchar echo? */
+	unsigned char	*insource;		/* Input source selector */
+	unsigned char	*outsink;		/* Output sink selector */
+	int		(*getkey)(void);	/* Get next key if one exists */
+	void		(*initgetkey)(void);	/* Initialize get key */
+	unsigned int	*translation;		/* Kbd translation selector */
+	unsigned char	*keybid;		/* Keyboard ID byte */
+	int		*screen_x;		/* V2: Screen x pos (r/o) */
+	int		*screen_y;		/* V2: Screen y pos (r/o) */
+	struct keybuf	*keybuf;		/* Up/down keycode buffer */
+	char		*monid;			/* Monitor version ID */
+	void		(*fbwritechar)(char);	/* Write a character to FB */
+	int		*fbAddr;		/* Address of frame buffer */
+	char		**font;			/* Font table for FB */
+	void		(*fbwritestr)(char *);	/* Write string to FB */
+	void		(*reboot)(char *);	/* e.g. reboot("sd()vmlinux") */
+	unsigned char	*linebuf;		/* The line input buffer */
+	unsigned char	**lineptr;		/* Cur pointer into linebuf */
+	int		*linesize;		/* length of line in linebuf */
+	void		(*getline)(char *);	/* Get line from user */
+	unsigned char	(*getnextchar)(void);	/* Get next char from linebuf */
+	unsigned char	(*peeknextchar)(void);	/* Peek at next char */
+	int		*fbthere;		/* =1 if frame buffer there */
+	int		(*getnum)(void);	/* Grab hex num from line */
+	int		(*printf)(char *, ...);	/* See prom_printf() instead */ 
+	void		(*printhex)(int);	/* Format N digits in hex */
+	unsigned char	*leds;			/* RAM copy of LED register */
+	void		(*setLEDs)(unsigned char *);	/* Sets LED's and RAM copy */
+	void		(*NMIaddr)(void *);	/* Addr for level 7 vector */
+	void		(*abortentry)(void);	/* Entry for keyboard abort */
+	int		*nmiclock;		/* Counts up in msec */
+	int		*FBtype;		/* Frame buffer type */
+ 	unsigned int	romvecversion;		/* Version number for this romvec */
+	struct globram  *globram;		/* monitor global variables ??? */
+	void *		kbdaddr;		/* Addr of keyboard in use */
+	int		*keyrinit;		/* ms before kbd repeat */
+	unsigned char	*keyrtick; 		/* ms between repetitions */
+	unsigned int	*memoryavail;		/* V1: Main mem usable size */
+	long		*resetaddr;		/* where to jump on a reset */
+	long		*resetmap;		/* pgmap entry for resetaddr */
+	void		(*exittomon)(void);	/* Exit from user program */
+	unsigned char	**memorybitmap;		/* V1: &{0 or &bits} */
+	void		(*setcxsegmap)(int ctxt, char *va, int pmeg);	/* Set seg in any context */
+	void		(**vector_cmd)(void *);	/* V2: Handler for 'v' cmd */
+	unsigned long	*expectedtrapsig;	/* V3: Location of the expected trap signal */
+	unsigned long	*trapvectorbasetable;	/* V3: Address of the trap vector table */
+	int		unused1;
+	int		unused2;
+	int		unused3;
+	int		unused4;
+} linux_sun4_romvec;
+
+extern linux_sun4_romvec *sun4_romvec;
+
+#endif /* _SUN4PROM_H_ */
diff -u --recursive --new-file v2.1.96/linux/include/asm-sparc/system.h linux/include/asm-sparc/system.h
--- v2.1.96/linux/include/asm-sparc/system.h	Thu May 15 16:48:04 1997
+++ linux/include/asm-sparc/system.h	Tue Apr 14 17:44:24 1998
@@ -1,4 +1,4 @@
-/* $Id: system.h,v 1.65 1997/05/14 20:47:59 davem Exp $ */
+/* $Id: system.h,v 1.68 1998/03/09 14:04:51 jj Exp $ */
 #ifndef __SPARC_SYSTEM_H
 #define __SPARC_SYSTEM_H
 
@@ -11,6 +11,7 @@
 #include <asm/oplib.h>
 #include <asm/psr.h>
 #include <asm/ptrace.h>
+#include <asm/btfixup.h>
 
 #define EMPTY_PGT       (&empty_bad_page)
 #define EMPTY_PGE	(&empty_bad_page_table)
@@ -37,6 +38,14 @@
 
 extern enum sparc_cpu sparc_cpu_model;
 
+#ifndef CONFIG_SUN4
+#define ARCH_SUN4C_SUN4 (sparc_cpu_model==sun4c)
+#define ARCH_SUN4 0
+#else
+#define ARCH_SUN4C_SUN4 1
+#define ARCH_SUN4 1
+#endif
+
 extern unsigned long empty_bad_page;
 extern unsigned long empty_bad_page_table;
 extern unsigned long empty_zero_page;
@@ -98,6 +107,8 @@
 	"std	%%g4, [%%g6 + %2]\n\t"							\
 	"ldd	[%1 + %2], %%g4\n\t"							\
 	"mov	%1, %%g6\n\t"								\
+	".globl	patchme_store_new_current\n"						\
+"patchme_store_new_current:\n\t"							\
 	"st	%1, [%0]\n\t"								\
 	"wr	%%g4, 0x20, %%psr\n\t"							\
 	"nop\n\t"									\
@@ -237,9 +248,11 @@
 #define restore_flags(flags)	__global_restore_flags(flags)
 #else
 
+#error For combined sun4[md] smp, we need to get rid of the rdtbr.
+
 /* Visit arch/sparc/lib/irqlock.S for all the fun details... */
 #define cli()      __asm__ __volatile__("mov	%%o7, %%g4\n\t"			\
-					"call	___global_cli\n\t"		\
+					"call	___f_global_cli\n\t"		\
 					" rd	%%tbr, %%g7" : :		\
 					: "g1", "g2", "g3", "g4", "g5", "g7",	\
 					  "memory", "cc")
@@ -248,7 +261,7 @@
 do {	register unsigned long bits asm("g7");			\
 	bits = 0;						\
 	__asm__ __volatile__("mov	%%o7, %%g4\n\t"		\
-			     "call	___global_sti\n\t"	\
+			     "call	___f_global_sti\n\t"	\
 			     " rd	%%tbr, %%g2"		\
 			     : /* no outputs */			\
 			     : "r" (bits)			\
@@ -260,7 +273,7 @@
 do {	register unsigned long bits asm("g7");				\
 	bits = flags;							\
 	__asm__ __volatile__("mov	%%o7, %%g4\n\t"			\
-			     "call	___global_restore_flags\n\t"	\
+			     "call	___f_global_restore_flags\n\t"	\
 			     " andcc	%%g7, 0x1, %%g0"		\
 			     : "=&r" (bits)				\
 			     : "0" (bits)				\
@@ -285,6 +298,11 @@
 
 #define nop() __asm__ __volatile__ ("nop");
 
+/* This has special calling conventions */
+#ifndef __SMP__
+BTFIXUPDEF_CALL(void, ___xchg32, void)
+#endif
+
 extern __inline__ unsigned long xchg_u32(__volatile__ unsigned long *m, unsigned long val)
 {
 #ifdef __SMP__
@@ -299,10 +317,12 @@
 	ptr = (unsigned long *) m;
 	ret = val;
 
+	/* Note: this is magic and the nop there is
+	   really needed. */
 	__asm__ __volatile__("
 	mov	%%o7, %%g4
-	call	___xchg32
-	 add	%%o7, 8, %%o7
+	call	___f____xchg32
+	 nop
 "	: "=&r" (ret)
 	: "0" (ret), "r" (ptr)
 	: "g3", "g4", "g7", "memory", "cc");
diff -u --recursive --new-file v2.1.96/linux/include/asm-sparc/timer.h linux/include/asm-sparc/timer.h
--- v2.1.96/linux/include/asm-sparc/timer.h	Mon Jan 12 15:15:54 1998
+++ linux/include/asm-sparc/timer.h	Tue Apr 14 17:44:24 1998
@@ -1,4 +1,4 @@
-/* $Id: timer.h,v 1.15 1997/12/18 14:21:43 jj Exp $
+/* $Id: timer.h,v 1.16 1998/01/30 10:59:59 jj Exp $
  * timer.h:  Definitions for the timer chips on the Sparc.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -7,6 +7,7 @@
 #define _SPARC_TIMER_H
 
 #include <asm/system.h>  /* For NCPUS */
+#include <asm/sun4paddr.h>
 
 /* Timer structures. The interrupt timer has two properties which
  * are the counter (which is handled in do_timer in sched.c) and the limit.
@@ -31,6 +32,11 @@
 };
 
 #define SUN4C_TIMER_PHYSADDR   0xf3000000
+#ifdef CONFIG_SUN4
+#define SUN_TIMER_PHYSADDR SUN4_300_TIMER_PHYSADDR
+#else
+#define SUN_TIMER_PHYSADDR SUN4C_TIMER_PHYSADDR
+#endif
 
 /* A sun4m has two blocks of registers which are probably of the same
  * structure. LSI Logic's L64851 is told to _decrement_ from the limit
diff -u --recursive --new-file v2.1.96/linux/include/asm-sparc/traps.h linux/include/asm-sparc/traps.h
--- v2.1.96/linux/include/asm-sparc/traps.h	Sat Nov  9 00:30:18 1996
+++ linux/include/asm-sparc/traps.h	Tue Apr 14 17:44:24 1998
@@ -1,4 +1,4 @@
-/* $Id: traps.h,v 1.8 1996/05/17 10:38:53 davem Exp $
+/* $Id: traps.h,v 1.9 1998/03/09 14:04:53 jj Exp $
  * traps.h:  Format of entries for the Sparc trap table.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -11,7 +11,7 @@
 
 #ifndef __ASSEMBLY__
 
-/* This is for V8 and V9 compliant Sparc CPUS */
+/* This is for V8 compliant Sparc CPUS */
 struct tt_entry {
 	unsigned long inst_one;
 	unsigned long inst_two;
@@ -21,25 +21,6 @@
 
 /* We set this to _start in system setup. */
 extern struct tt_entry *sparc_ttable;
-
-/* This for V9 compliant Sparc CPUS */
-struct tt_v9_entry {
-	unsigned long inst_one;
-	unsigned long inst_two;
-	unsigned long inst_three;
-	unsigned long inst_four;
-	unsigned long inst_five;
-	unsigned long inst_six;
-	unsigned long inst_seven;
-	unsigned long inst_eight;
-};
-
-/* V9 has multiple trap tables, which one is used depends
- * upon how deep within multiple traps you are.
- * I believe the UltraSparc supports two levels now.
- */
-extern struct tt_v9_entry *sparc_v9_ttablel0;
-extern struct tt_v9_entry *sparc_v9_ttablel1;
 
 extern __inline__ unsigned long get_tbr(void)
 {
diff -u --recursive --new-file v2.1.96/linux/include/asm-sparc/uaccess.h linux/include/asm-sparc/uaccess.h
--- v2.1.96/linux/include/asm-sparc/uaccess.h	Mon Jan 12 15:15:54 1998
+++ linux/include/asm-sparc/uaccess.h	Tue Apr 14 17:44:24 1998
@@ -1,4 +1,4 @@
-/* $Id: uaccess.h,v 1.14 1997/09/18 10:42:02 rth Exp $
+/* $Id: uaccess.h,v 1.15 1998/02/05 14:19:54 jj Exp $
  * uaccess.h: User space memore access functions.
  *
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
@@ -39,7 +39,7 @@
  * No one can read/write anything from userland in the kernel space by setting
  * large size and address near to page_offset - a fault will break his intentions.
  */
-#define __user_ok(addr,size) ((addr) < stack_top)
+#define __user_ok(addr,size) ((addr) < STACK_TOP)
 #define __kernel_ok (segment_eq(get_fs(), KERNEL_DS))
 #define __access_ok(addr,size) (__user_ok((addr) & get_fs().seg,(size)))
 #define access_ok(type,addr,size) __access_ok((unsigned long)(addr),(size))
diff -u --recursive --new-file v2.1.96/linux/include/asm-sparc/unistd.h linux/include/asm-sparc/unistd.h
--- v2.1.96/linux/include/asm-sparc/unistd.h	Wed Apr  8 19:36:29 1998
+++ linux/include/asm-sparc/unistd.h	Tue Apr 14 17:44:24 1998
@@ -1,4 +1,4 @@
-/* $Id: unistd.h,v 1.36 1997/12/14 23:24:43 ecd Exp $ */
+/* $Id: unistd.h,v 1.38 1998/03/27 07:01:56 davem Exp $ */
 #ifndef _SPARC_UNISTD_H
 #define _SPARC_UNISTD_H
 
@@ -29,12 +29,12 @@
 #define __NR_unlink              10 /* Common                                      */
 #define __NR_execv               11 /* SunOS Specific                              */
 #define __NR_chdir               12 /* Common                                      */
-/* #define __NR_ni_syscall       13    ENOSYS under SunOS                          */
+#define __NR_xstat		 13 /* Linux Specific				   */
 #define __NR_mknod               14 /* Common                                      */
 #define __NR_chmod               15 /* Common                                      */
 #define __NR_chown               16 /* Common                                      */
 #define __NR_brk                 17 /* Common                                      */
-/* #define __NR_ni_syscall       18    ENOSYS under SunOS                          */
+#define __NR_xmknod		 18 /* Linux Specific				   */
 #define __NR_lseek               19 /* Common                                      */
 #define __NR_getpid              20 /* Common                                      */
 /* #define __NR_ni_syscall       21    ENOSYS under SunOS                          */
diff -u --recursive --new-file v2.1.96/linux/include/asm-sparc/user.h linux/include/asm-sparc/user.h
--- v2.1.96/linux/include/asm-sparc/user.h	Sat Nov  9 00:30:24 1996
+++ linux/include/asm-sparc/user.h	Tue Apr 14 17:44:24 1998
@@ -1,4 +1,4 @@
-/* $Id: user.h,v 1.4 1996/07/24 23:17:14 miguel Exp $
+/* $Id: user.h,v 1.5 1998/02/23 01:49:22 rth Exp $
  * asm-sparc/user.h: Core file definitions for the Sparc.
  *
  * Keep in sync with reg.h.  Actually, we could get rid of this
@@ -50,11 +50,11 @@
 	unsigned long sigcode;   /* Special sigcontext subcode, if any */
 };
 
-#define NBPG                   PAGE_SIZE
+#define NBPG                   0x2000
 #define UPAGES                 1
 #define HOST_TEXT_START_ADDR   (u.start_code)
-#define HOST_DATA_START_ADDR   (u.start_data)
-#define HOST_STACK_END_ADDR    (u.start_stack + u.u_ssize * NBPG)
+#define HOST_DATA_START_ADDR   (u.uexec.a_data)
+#define HOST_STACK_END_ADDR    (- u.u_ssize * NBPG)
 #define SUNOS_CORE_MAGIC       0x080456
 
 #endif /* !(_SPARC_USER_H) */
diff -u --recursive --new-file v2.1.96/linux/include/asm-sparc/vac-ops.h linux/include/asm-sparc/vac-ops.h
--- v2.1.96/linux/include/asm-sparc/vac-ops.h	Sat Nov  9 00:30:25 1996
+++ linux/include/asm-sparc/vac-ops.h	Tue Apr 14 17:44:24 1998
@@ -1,4 +1,4 @@
-/* $Id: vac-ops.h,v 1.12 1996/07/08 15:12:30 ecd Exp $ */
+/* $Id: vac-ops.h,v 1.13 1998/01/30 10:59:59 jj Exp $ */
 #ifndef _SPARC_VAC_OPS_H
 #define _SPARC_VAC_OPS_H
 
@@ -8,6 +8,7 @@
  * Copyright (C) 1994, David S. Miller (davem@caip.rutgers.edu)
  */
 
+#include <linux/config.h>
 #include <asm/sysen.h>
 #include <asm/contregs.h>
 #include <asm/asi.h>
@@ -60,9 +61,12 @@
 #define S4CVACTAG_TID      0x0000fffc
 
 /* Sun4c VAC Virtual Address */
+/* These aren't used, why bother? (Anton) */
+#if 0
 #define S4CVACVA_TID       0x3fff0000
 #define S4CVACVA_LINE      0x0000fff0
 #define S4CVACVA_BIL       0x0000000f
+#endif
 
 /* The indexing of cache lines creates a problem.  Because the line
  * field of a virtual address extends past the page offset within
@@ -73,7 +77,12 @@
  * not the case, and thus is a 'bad alias' we must turn off the
  * cacheable bit in the pte's of all such pages.
  */
-#define S4CVAC_BADBITS     0x0000f000
+
+#ifdef CONFIG_SUN4
+#define S4CVAC_BADBITS     0x0001e000
+#else
+#define S4CVAC_BADBITS    0x0000f000
+#endif
 
 /* The following is true if vaddr1 and vaddr2 would cause
  * a 'bad alias'.
@@ -89,6 +98,8 @@
 	unsigned int num_bytes;     /* Size of the cache */
 	unsigned int num_lines;     /* Number of cache lines */
 	unsigned int do_hwflushes;  /* Hardware flushing available? */
+	enum { NONE, WRITE_THROUGH,
+	    WRITE_BACK } type;      /* What type of VAC? */
 	unsigned int linesize;      /* Size of each line in bytes */
 	unsigned int log2lsize;     /* log2(linesize) */
 	unsigned int on;            /* VAC is enabled */
diff -u --recursive --new-file v2.1.96/linux/include/asm-sparc/winmacro.h linux/include/asm-sparc/winmacro.h
--- v2.1.96/linux/include/asm-sparc/winmacro.h	Tue May 13 22:41:18 1997
+++ linux/include/asm-sparc/winmacro.h	Tue Apr 14 17:44:24 1998
@@ -1,4 +1,4 @@
-/* $Id: winmacro.h,v 1.19 1997/05/01 01:42:05 davem Exp $
+/* $Id: winmacro.h,v 1.20 1998/03/09 14:04:54 jj Exp $
  * winmacro.h: Window loading-unloading macros.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -112,12 +112,25 @@
         st       %scratch, [%cur_reg + AOFF_task_tss + AOFF_thread_w_saved];
 
 #ifdef __SMP__
-#define LOAD_CURRENT(dest_reg, idreg) \
+#define LOAD_CURRENT4M(dest_reg, idreg) \
         rd       %tbr, %idreg; \
 	sethi    %hi(C_LABEL(current_set)), %dest_reg; \
         srl      %idreg, 10, %idreg; \
 	or       %dest_reg, %lo(C_LABEL(current_set)), %dest_reg; \
 	and      %idreg, 0xc, %idreg; \
+	ld       [%idreg + %dest_reg], %dest_reg;
+
+/* Sliiick. We have a Linux current register :) -jj */
+#define LOAD_CURRENT4D(dest_reg) \
+	lda	 [%g0] ASI_M_VIKING_TMP2, %dest_reg;
+
+/* Blackbox - take care with this... - check smp4m and smp4d before changing this. */
+#define LOAD_CURRENT(dest_reg, idreg) 					\
+	sethi	 %hi(___b_load_current), %idreg;			\
+	sethi    %hi(C_LABEL(current_set)), %dest_reg; 			\
+	sethi    %hi(C_LABEL(boot_cpu_id4)), %idreg; 			\
+	or       %dest_reg, %lo(C_LABEL(current_set)), %dest_reg; 	\
+	ldub	 [%idreg + %lo(C_LABEL(boot_cpu_id4))], %idreg;		\
 	ld       [%idreg + %dest_reg], %dest_reg;
 #else
 #define LOAD_CURRENT(dest_reg, idreg) \
diff -u --recursive --new-file v2.1.96/linux/include/asm-sparc/xstat.h linux/include/asm-sparc/xstat.h
--- v2.1.96/linux/include/asm-sparc/xstat.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-sparc/xstat.h	Tue Apr 14 17:44:24 1998
@@ -0,0 +1,35 @@
+/* $Id: xstat.h,v 1.1 1998/02/06 12:52:08 jj Exp $
+ * xstat.h: sys_xstat/xmknod architecture dependent stuff.
+ *
+ * Copyright (C) 1998  Jakub Jelinek  (jj@sunsite.mff.cuni.cz)
+ */
+ 
+extern __inline__ int cp_xstat(struct inode *inode, struct stat64 *s, unsigned long blocks, int blksize)
+{
+	struct stat64 tmp;
+	
+	memset (&tmp, 0, sizeof(tmp));
+	tmp.st_dev.major = MAJOR(inode->i_dev);
+	tmp.st_dev.minor = MINOR(inode->i_dev);
+	tmp.st_ino = inode->i_ino;
+	tmp.st_mode = inode->i_mode;
+	tmp.st_nlink = inode->i_nlink;
+	tmp.st_uid = inode->i_uid;
+	tmp.st_gid = inode->i_gid;
+	tmp.st_rdev.major = MAJOR(inode->i_rdev);
+	tmp.st_rdev.minor = MINOR(inode->i_rdev);
+	tmp.st_size = inode->i_size;
+	tmp.st_blksize = blksize;
+	tmp.st_blocks = blocks;
+	tmp.st_atime = inode->i_atime;
+	tmp.st_mtime = inode->i_mtime;
+	tmp.st_ctime = inode->i_ctime;
+	return copy_to_user(s,&tmp,sizeof(tmp));
+}
+
+extern __inline__ int get_user_new_dev_t(kdev_t *kdev, __new_dev_t *udev) {
+	__new_dev_t ndev;
+	if (copy_from_user (&ndev, udev, sizeof(__new_dev_t))) return -EFAULT;
+	*kdev = MKDEV(ndev.major, ndev.minor);
+	return 0;
+}
diff -u --recursive --new-file v2.1.96/linux/include/asm-sparc64/apb.h linux/include/asm-sparc64/apb.h
--- v2.1.96/linux/include/asm-sparc64/apb.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-sparc64/apb.h	Tue Apr 14 17:44:24 1998
@@ -0,0 +1,36 @@
+/* $Id: apb.h,v 1.2 1998/04/01 20:41:49 ecd Exp $
+ * apb.h: Advanced PCI Bridge Configuration Registers and Bits
+ *
+ * Copyright (C) 1998  Eddie C. Dost  (ecd@skynet.be)
+ */
+
+#ifndef _SPARC64_APB_H
+#define _SPARC64_APB_H
+
+#define APB_TICK_REGISTER			0xb0
+#define APB_INT_ACK				0xb8
+#define APB_PRIMARY_MASTER_RETRY_LIMIT		0xc0
+#define APB_DMA_ASFR				0xc8
+#define APB_DMA_AFAR				0xd0
+#define APB_PIO_TARGET_RETRY_LIMIT		0xd8
+#define APB_PIO_TARGET_LATENCY_TIMER		0xd9
+#define APB_DMA_TARGET_RETRY_LIMIT		0xda
+#define APB_DMA_TARGET_LATENCY_TIMER		0xdb
+#define APB_SECONDARY_MASTER_RETRY_LIMIT	0xdc
+#define APB_SECONDARY_CONTROL			0xdd
+#define APB_IO_ADDRESS_MAP			0xde
+#define APB_MEM_ADDRESS_MAP			0xdf
+
+#define APB_PCI_CONTROL_LOW			0xe0
+#  define APB_PCI_CTL_LOW_ARB_PARK			(1 << 21)
+#  define APB_PCI_CTL_LOW_ERRINT_EN			(1 << 8)
+
+#define APB_PCI_CONTROL_HIGH			0xe4
+#  define APB_PCI_CTL_HIGH_SERR				(1 << 2)
+#  define APB_PCI_CTL_HIGH_ARBITER_EN			(1 << 0)
+
+#define APB_PIO_ASFR				0xe8
+#define APB_PIO_AFAR				0xf0
+#define APB_DIAG_REGISTER			0xf8
+
+#endif /* !(_SPARC64_APB_H) */
diff -u --recursive --new-file v2.1.96/linux/include/asm-sparc64/asm_offsets.h linux/include/asm-sparc64/asm_offsets.h
--- v2.1.96/linux/include/asm-sparc64/asm_offsets.h	Mon Jan 12 15:15:54 1998
+++ linux/include/asm-sparc64/asm_offsets.h	Tue Apr 14 17:44:24 1998
@@ -2,22 +2,279 @@
 #ifndef __ASM_OFFSETS_H__
 #define __ASM_OFFSETS_H__
 
+#ifndef __SMP__
+
 #define AOFF_task_state	0x00000000
 #define ASIZ_task_state	0x00000008
-#define AOFF_task_counter	0x00000008
+#define AOFF_task_flags	0x00000008
+#define ASIZ_task_flags	0x00000008
+#define AOFF_task_sigpending	0x00000010
+#define ASIZ_task_sigpending	0x00000004
+#define AOFF_task_addr_limit	0x00000018
+#define ASIZ_task_addr_limit	0x00000008
+#define AOFF_task_exec_domain	0x00000020
+#define ASIZ_task_exec_domain	0x00000008
+#define AOFF_task_debugreg	0x00000028
+#define ASIZ_task_debugreg	0x00000040
+#define AOFF_task_counter	0x00000068
 #define ASIZ_task_counter	0x00000008
-#define AOFF_task_priority	0x00000010
+#define AOFF_task_priority	0x00000070
 #define ASIZ_task_priority	0x00000008
-#define AOFF_task_flags	0x00000018
+#define AOFF_task_binfmt	0x00000078
+#define ASIZ_task_binfmt	0x00000008
+#define AOFF_task_next_task	0x00000080
+#define ASIZ_task_next_task	0x00000008
+#define AOFF_task_prev_task	0x00000088
+#define ASIZ_task_prev_task	0x00000008
+#define AOFF_task_next_run	0x00000090
+#define ASIZ_task_next_run	0x00000008
+#define AOFF_task_prev_run	0x00000098
+#define ASIZ_task_prev_run	0x00000008
+#define AOFF_task_exit_code	0x000000a0
+#define ASIZ_task_exit_code	0x00000004
+#define AOFF_task_exit_signal	0x000000a4
+#define ASIZ_task_exit_signal	0x00000004
+#define AOFF_task_pdeath_signal	0x000000a8
+#define ASIZ_task_pdeath_signal	0x00000004
+#define AOFF_task_personality	0x000000b0
+#define ASIZ_task_personality	0x00000008
+#define AOFF_task_pid	0x000000bc
+#define ASIZ_task_pid	0x00000004
+#define AOFF_task_pgrp	0x000000c0
+#define ASIZ_task_pgrp	0x00000004
+#define AOFF_task_tty_old_pgrp	0x000000c4
+#define ASIZ_task_tty_old_pgrp	0x00000004
+#define AOFF_task_session	0x000000c8
+#define ASIZ_task_session	0x00000004
+#define AOFF_task_leader	0x000000cc
+#define ASIZ_task_leader	0x00000004
+#define AOFF_task_ngroups	0x000000d0
+#define ASIZ_task_ngroups	0x00000004
+#define AOFF_task_groups	0x000000d4
+#define ASIZ_task_groups	0x00000080
+#define AOFF_task_p_opptr	0x00000158
+#define ASIZ_task_p_opptr	0x00000008
+#define AOFF_task_p_pptr	0x00000160
+#define ASIZ_task_p_pptr	0x00000008
+#define AOFF_task_p_cptr	0x00000168
+#define ASIZ_task_p_cptr	0x00000008
+#define AOFF_task_p_ysptr	0x00000170
+#define ASIZ_task_p_ysptr	0x00000008
+#define AOFF_task_p_osptr	0x00000178
+#define ASIZ_task_p_osptr	0x00000008
+#define AOFF_task_pidhash_next	0x00000180
+#define ASIZ_task_pidhash_next	0x00000008
+#define AOFF_task_pidhash_pprev	0x00000188
+#define ASIZ_task_pidhash_pprev	0x00000008
+#define AOFF_task_tarray_ptr	0x00000190
+#define ASIZ_task_tarray_ptr	0x00000008
+#define AOFF_task_wait_chldexit	0x00000198
+#define ASIZ_task_wait_chldexit	0x00000008
+#define AOFF_task_uid	0x000001a0
+#define ASIZ_task_uid	0x00000004
+#define AOFF_task_euid	0x000001a4
+#define ASIZ_task_euid	0x00000004
+#define AOFF_task_suid	0x000001a8
+#define ASIZ_task_suid	0x00000004
+#define AOFF_task_fsuid	0x000001ac
+#define ASIZ_task_fsuid	0x00000004
+#define AOFF_task_gid	0x000001b0
+#define ASIZ_task_gid	0x00000004
+#define AOFF_task_egid	0x000001b4
+#define ASIZ_task_egid	0x00000004
+#define AOFF_task_sgid	0x000001b8
+#define ASIZ_task_sgid	0x00000004
+#define AOFF_task_fsgid	0x000001bc
+#define ASIZ_task_fsgid	0x00000004
+#define AOFF_task_timeout	0x000001c0
+#define ASIZ_task_timeout	0x00000008
+#define AOFF_task_policy	0x000001c8
+#define ASIZ_task_policy	0x00000008
+#define AOFF_task_rt_priority	0x000001d0
+#define ASIZ_task_rt_priority	0x00000008
+#define AOFF_task_it_real_value	0x000001d8
+#define ASIZ_task_it_real_value	0x00000008
+#define AOFF_task_it_prof_value	0x000001e0
+#define ASIZ_task_it_prof_value	0x00000008
+#define AOFF_task_it_virt_value	0x000001e8
+#define ASIZ_task_it_virt_value	0x00000008
+#define AOFF_task_it_real_incr	0x000001f0
+#define ASIZ_task_it_real_incr	0x00000008
+#define AOFF_task_it_prof_incr	0x000001f8
+#define ASIZ_task_it_prof_incr	0x00000008
+#define AOFF_task_it_virt_incr	0x00000200
+#define ASIZ_task_it_virt_incr	0x00000008
+#define AOFF_task_real_timer	0x00000208
+#define ASIZ_task_real_timer	0x00000028
+#define AOFF_task_times	0x00000230
+#define ASIZ_task_times	0x00000020
+#define AOFF_task_start_time	0x00000250
+#define ASIZ_task_start_time	0x00000008
+#define AOFF_task_per_cpu_utime	0x00000258
+#define ASIZ_task_per_cpu_utime	0x00000008
+#define AOFF_task_min_flt	0x00000268
+#define ASIZ_task_min_flt	0x00000008
+#define AOFF_task_maj_flt	0x00000270
+#define ASIZ_task_maj_flt	0x00000008
+#define AOFF_task_nswap	0x00000278
+#define ASIZ_task_nswap	0x00000008
+#define AOFF_task_cmin_flt	0x00000280
+#define ASIZ_task_cmin_flt	0x00000008
+#define AOFF_task_cmaj_flt	0x00000288
+#define ASIZ_task_cmaj_flt	0x00000008
+#define AOFF_task_cnswap	0x00000290
+#define ASIZ_task_cnswap	0x00000008
+#define AOFF_task_swap_address	0x000002a0
+#define ASIZ_task_swap_address	0x00000008
+#define AOFF_task_old_maj_flt	0x000002a8
+#define ASIZ_task_old_maj_flt	0x00000008
+#define AOFF_task_dec_flt	0x000002b0
+#define ASIZ_task_dec_flt	0x00000008
+#define AOFF_task_swap_cnt	0x000002b8
+#define ASIZ_task_swap_cnt	0x00000008
+#define AOFF_task_rlim	0x000002c0
+#define ASIZ_task_rlim	0x000000a0
+#define AOFF_task_used_math	0x00000360
+#define ASIZ_task_used_math	0x00000002
+#define AOFF_task_io_usage	0x00000368
+#define ASIZ_task_io_usage	0x00000008
+#define AOFF_task_comm	0x00000370
+#define ASIZ_task_comm	0x00000010
+#define AOFF_task_link_count	0x00000380
+#define ASIZ_task_link_count	0x00000004
+#define AOFF_task_tty	0x00000388
+#define ASIZ_task_tty	0x00000008
+#define AOFF_task_semundo	0x00000390
+#define ASIZ_task_semundo	0x00000008
+#define AOFF_task_semsleeping	0x00000398
+#define ASIZ_task_semsleeping	0x00000008
+#define AOFF_task_ldt	0x000003a0
+#define ASIZ_task_ldt	0x00000008
+#define AOFF_task_tss	0x000003b0
+#define ASIZ_task_tss	0x00000490
+#define AOFF_task_fs	0x00000840
+#define ASIZ_task_fs	0x00000008
+#define AOFF_task_files	0x00000848
+#define ASIZ_task_files	0x00000008
+#define AOFF_task_mm	0x00000850
+#define ASIZ_task_mm	0x00000008
+#define AOFF_task_sig	0x00000858
+#define ASIZ_task_sig	0x00000008
+#define AOFF_task_signal	0x00000860
+#define ASIZ_task_signal	0x00000008
+#define AOFF_task_blocked	0x00000868
+#define ASIZ_task_blocked	0x00000008
+#define AOFF_task_sigqueue	0x00000870
+#define ASIZ_task_sigqueue	0x00000008
+#define AOFF_task_sigqueue_tail	0x00000878
+#define ASIZ_task_sigqueue_tail	0x00000008
+#define AOFF_task_has_cpu	0x00000880
+#define ASIZ_task_has_cpu	0x00000004
+#define AOFF_task_processor	0x00000884
+#define ASIZ_task_processor	0x00000004
+#define AOFF_task_last_processor	0x00000888
+#define ASIZ_task_last_processor	0x00000004
+#define AOFF_task_lock_depth	0x0000088c
+#define ASIZ_task_lock_depth	0x00000004
+#define AOFF_task_sigmask_lock	0x00000890
+#define ASIZ_task_sigmask_lock	0x00000000
+#define AOFF_mm_mmap	0x00000000
+#define ASIZ_mm_mmap	0x00000008
+#define AOFF_mm_mmap_cache	0x00000008
+#define ASIZ_mm_mmap_cache	0x00000008
+#define AOFF_mm_pgd	0x00000010
+#define ASIZ_mm_pgd	0x00000008
+#define AOFF_mm_count	0x00000018
+#define ASIZ_mm_count	0x00000004
+#define AOFF_mm_map_count	0x0000001c
+#define ASIZ_mm_map_count	0x00000004
+#define AOFF_mm_mmap_sem	0x00000020
+#define ASIZ_mm_mmap_sem	0x00000010
+#define AOFF_mm_context	0x00000030
+#define ASIZ_mm_context	0x00000008
+#define AOFF_mm_start_code	0x00000038
+#define ASIZ_mm_start_code	0x00000008
+#define AOFF_mm_end_code	0x00000040
+#define ASIZ_mm_end_code	0x00000008
+#define AOFF_mm_start_data	0x00000048
+#define ASIZ_mm_start_data	0x00000008
+#define AOFF_mm_end_data	0x00000050
+#define ASIZ_mm_end_data	0x00000008
+#define AOFF_mm_start_brk	0x00000058
+#define ASIZ_mm_start_brk	0x00000008
+#define AOFF_mm_brk	0x00000060
+#define ASIZ_mm_brk	0x00000008
+#define AOFF_mm_start_stack	0x00000068
+#define ASIZ_mm_start_stack	0x00000008
+#define AOFF_mm_arg_start	0x00000070
+#define ASIZ_mm_arg_start	0x00000008
+#define AOFF_mm_arg_end	0x00000078
+#define ASIZ_mm_arg_end	0x00000008
+#define AOFF_mm_env_start	0x00000080
+#define ASIZ_mm_env_start	0x00000008
+#define AOFF_mm_env_end	0x00000088
+#define ASIZ_mm_env_end	0x00000008
+#define AOFF_mm_rss	0x00000090
+#define ASIZ_mm_rss	0x00000008
+#define AOFF_mm_total_vm	0x00000098
+#define ASIZ_mm_total_vm	0x00000008
+#define AOFF_mm_locked_vm	0x000000a0
+#define ASIZ_mm_locked_vm	0x00000008
+#define AOFF_mm_def_flags	0x000000a8
+#define ASIZ_mm_def_flags	0x00000008
+#define AOFF_mm_cpu_vm_mask	0x000000b0
+#define ASIZ_mm_cpu_vm_mask	0x00000008
+#define AOFF_thread_ksp	0x00000000
+#define ASIZ_thread_ksp	0x00000008
+#define AOFF_thread_kpc	0x00000008
+#define ASIZ_thread_kpc	0x00000004
+#define AOFF_thread_wstate	0x0000000c
+#define ASIZ_thread_wstate	0x00000002
+#define AOFF_thread_cwp	0x0000000e
+#define ASIZ_thread_cwp	0x00000002
+#define AOFF_thread_flags	0x00000010
+#define ASIZ_thread_flags	0x00000002
+#define AOFF_thread_ctx	0x00000012
+#define ASIZ_thread_ctx	0x00000002
+#define AOFF_thread_w_saved	0x00000014
+#define ASIZ_thread_w_saved	0x00000002
+#define AOFF_thread_new_signal	0x00000016
+#define ASIZ_thread_new_signal	0x00000002
+#define AOFF_thread_current_ds	0x00000018
+#define ASIZ_thread_current_ds	0x00000008
+#define AOFF_thread_kregs	0x00000020
+#define ASIZ_thread_kregs	0x00000008
+#define AOFF_thread_utraps	0x00000028
+#define ASIZ_thread_utraps	0x00000008
+#define AOFF_thread_reg_window	0x00000030
+#define ASIZ_thread_reg_window	0x00000400
+#define AOFF_thread_rwbuf_stkptrs	0x00000430
+#define ASIZ_thread_rwbuf_stkptrs	0x00000040
+#define AOFF_thread_sig_address	0x00000470
+#define ASIZ_thread_sig_address	0x00000008
+#define AOFF_thread_sig_desc	0x00000478
+#define ASIZ_thread_sig_desc	0x00000008
+#define AOFF_thread_sstk_info	0x00000480
+#define ASIZ_thread_sstk_info	0x00000010
+
+#else /* __SMP__ */
+
+#define AOFF_task_state	0x00000000
+#define ASIZ_task_state	0x00000008
+#define AOFF_task_flags	0x00000008
 #define ASIZ_task_flags	0x00000008
-#define AOFF_task_addr_limit	0x00000020
-#define ASIZ_task_addr_limit	0x00000008
-#define AOFF_task_sigpending	0x00000028
+#define AOFF_task_sigpending	0x00000010
 #define ASIZ_task_sigpending	0x00000004
-#define AOFF_task_debugreg	0x00000030
-#define ASIZ_task_debugreg	0x00000040
-#define AOFF_task_exec_domain	0x00000070
+#define AOFF_task_addr_limit	0x00000018
+#define ASIZ_task_addr_limit	0x00000008
+#define AOFF_task_exec_domain	0x00000020
 #define ASIZ_task_exec_domain	0x00000008
+#define AOFF_task_debugreg	0x00000028
+#define ASIZ_task_debugreg	0x00000040
+#define AOFF_task_counter	0x00000068
+#define ASIZ_task_counter	0x00000008
+#define AOFF_task_priority	0x00000070
+#define ASIZ_task_priority	0x00000008
 #define AOFF_task_binfmt	0x00000078
 #define ASIZ_task_binfmt	0x00000008
 #define AOFF_task_next_task	0x00000080
@@ -108,72 +365,74 @@
 #define ASIZ_task_times	0x00000020
 #define AOFF_task_start_time	0x00000250
 #define ASIZ_task_start_time	0x00000008
-#define AOFF_task_min_flt	0x00000258
+#define AOFF_task_per_cpu_utime	0x00000258
+#define ASIZ_task_per_cpu_utime	0x00000100
+#define AOFF_task_min_flt	0x00000458
 #define ASIZ_task_min_flt	0x00000008
-#define AOFF_task_maj_flt	0x00000260
+#define AOFF_task_maj_flt	0x00000460
 #define ASIZ_task_maj_flt	0x00000008
-#define AOFF_task_nswap	0x00000268
+#define AOFF_task_nswap	0x00000468
 #define ASIZ_task_nswap	0x00000008
-#define AOFF_task_cmin_flt	0x00000270
+#define AOFF_task_cmin_flt	0x00000470
 #define ASIZ_task_cmin_flt	0x00000008
-#define AOFF_task_cmaj_flt	0x00000278
+#define AOFF_task_cmaj_flt	0x00000478
 #define ASIZ_task_cmaj_flt	0x00000008
-#define AOFF_task_cnswap	0x00000280
+#define AOFF_task_cnswap	0x00000480
 #define ASIZ_task_cnswap	0x00000008
-#define AOFF_task_swap_address	0x00000290
+#define AOFF_task_swap_address	0x00000490
 #define ASIZ_task_swap_address	0x00000008
-#define AOFF_task_old_maj_flt	0x00000298
+#define AOFF_task_old_maj_flt	0x00000498
 #define ASIZ_task_old_maj_flt	0x00000008
-#define AOFF_task_dec_flt	0x000002a0
+#define AOFF_task_dec_flt	0x000004a0
 #define ASIZ_task_dec_flt	0x00000008
-#define AOFF_task_swap_cnt	0x000002a8
+#define AOFF_task_swap_cnt	0x000004a8
 #define ASIZ_task_swap_cnt	0x00000008
-#define AOFF_task_rlim	0x000002b0
+#define AOFF_task_rlim	0x000004b0
 #define ASIZ_task_rlim	0x000000a0
-#define AOFF_task_used_math	0x00000350
+#define AOFF_task_used_math	0x00000550
 #define ASIZ_task_used_math	0x00000002
-#define AOFF_task_io_usage	0x00000358
+#define AOFF_task_io_usage	0x00000558
 #define ASIZ_task_io_usage	0x00000008
-#define AOFF_task_comm	0x00000360
+#define AOFF_task_comm	0x00000560
 #define ASIZ_task_comm	0x00000010
-#define AOFF_task_link_count	0x00000370
+#define AOFF_task_link_count	0x00000570
 #define ASIZ_task_link_count	0x00000004
-#define AOFF_task_tty	0x00000378
+#define AOFF_task_tty	0x00000578
 #define ASIZ_task_tty	0x00000008
-#define AOFF_task_semundo	0x00000380
+#define AOFF_task_semundo	0x00000580
 #define ASIZ_task_semundo	0x00000008
-#define AOFF_task_semsleeping	0x00000388
+#define AOFF_task_semsleeping	0x00000588
 #define ASIZ_task_semsleeping	0x00000008
-#define AOFF_task_ldt	0x00000390
+#define AOFF_task_ldt	0x00000590
 #define ASIZ_task_ldt	0x00000008
-#define AOFF_task_tss	0x000003a0
+#define AOFF_task_tss	0x000005a0
 #define ASIZ_task_tss	0x00000490
-#define AOFF_task_fs	0x00000830
+#define AOFF_task_fs	0x00000a30
 #define ASIZ_task_fs	0x00000008
-#define AOFF_task_files	0x00000838
+#define AOFF_task_files	0x00000a38
 #define ASIZ_task_files	0x00000008
-#define AOFF_task_mm	0x00000840
+#define AOFF_task_mm	0x00000a40
 #define ASIZ_task_mm	0x00000008
-#define AOFF_task_sig	0x00000848
+#define AOFF_task_sig	0x00000a48
 #define ASIZ_task_sig	0x00000008
-#define AOFF_task_signal	0x00000850
+#define AOFF_task_signal	0x00000a50
 #define ASIZ_task_signal	0x00000008
-#define AOFF_task_blocked	0x00000858
+#define AOFF_task_blocked	0x00000a58
 #define ASIZ_task_blocked	0x00000008
-#define AOFF_task_sigqueue	0x00000860
+#define AOFF_task_sigqueue	0x00000a60
 #define ASIZ_task_sigqueue	0x00000008
-#define AOFF_task_sigqueue_tail	0x00000868
+#define AOFF_task_sigqueue_tail	0x00000a68
 #define ASIZ_task_sigqueue_tail	0x00000008
-#define AOFF_task_has_cpu	0x00000870
+#define AOFF_task_has_cpu	0x00000a70
 #define ASIZ_task_has_cpu	0x00000004
-#define AOFF_task_processor	0x00000874
+#define AOFF_task_processor	0x00000a74
 #define ASIZ_task_processor	0x00000004
-#define AOFF_task_last_processor	0x00000878
+#define AOFF_task_last_processor	0x00000a78
 #define ASIZ_task_last_processor	0x00000004
-#define AOFF_task_lock_depth	0x0000087c
+#define AOFF_task_lock_depth	0x00000a7c
 #define ASIZ_task_lock_depth	0x00000004
-#define AOFF_task_sigmask_lock	0x00000880
-#define ASIZ_task_sigmask_lock	0x00000000
+#define AOFF_task_sigmask_lock	0x00000a80
+#define ASIZ_task_sigmask_lock	0x00000001
 #define AOFF_mm_mmap	0x00000000
 #define ASIZ_mm_mmap	0x00000008
 #define AOFF_mm_mmap_cache	0x00000008
@@ -182,6 +441,8 @@
 #define ASIZ_mm_pgd	0x00000008
 #define AOFF_mm_count	0x00000018
 #define ASIZ_mm_count	0x00000004
+#define AOFF_mm_map_count	0x0000001c
+#define ASIZ_mm_map_count	0x00000004
 #define AOFF_mm_mmap_sem	0x00000020
 #define ASIZ_mm_mmap_sem	0x00000010
 #define AOFF_mm_context	0x00000030
@@ -200,25 +461,23 @@
 #define ASIZ_mm_brk	0x00000008
 #define AOFF_mm_start_stack	0x00000068
 #define ASIZ_mm_start_stack	0x00000008
-#define AOFF_mm_start_mmap	0x00000070
-#define ASIZ_mm_start_mmap	0x00000008
-#define AOFF_mm_arg_start	0x00000078
+#define AOFF_mm_arg_start	0x00000070
 #define ASIZ_mm_arg_start	0x00000008
-#define AOFF_mm_arg_end	0x00000080
+#define AOFF_mm_arg_end	0x00000078
 #define ASIZ_mm_arg_end	0x00000008
-#define AOFF_mm_env_start	0x00000088
+#define AOFF_mm_env_start	0x00000080
 #define ASIZ_mm_env_start	0x00000008
-#define AOFF_mm_env_end	0x00000090
+#define AOFF_mm_env_end	0x00000088
 #define ASIZ_mm_env_end	0x00000008
-#define AOFF_mm_rss	0x00000098
+#define AOFF_mm_rss	0x00000090
 #define ASIZ_mm_rss	0x00000008
-#define AOFF_mm_total_vm	0x000000a0
+#define AOFF_mm_total_vm	0x00000098
 #define ASIZ_mm_total_vm	0x00000008
-#define AOFF_mm_locked_vm	0x000000a8
+#define AOFF_mm_locked_vm	0x000000a0
 #define ASIZ_mm_locked_vm	0x00000008
-#define AOFF_mm_def_flags	0x000000b0
+#define AOFF_mm_def_flags	0x000000a8
 #define ASIZ_mm_def_flags	0x00000008
-#define AOFF_mm_cpu_vm_mask	0x000000b8
+#define AOFF_mm_cpu_vm_mask	0x000000b0
 #define ASIZ_mm_cpu_vm_mask	0x00000008
 #define AOFF_thread_ksp	0x00000000
 #define ASIZ_thread_ksp	0x00000008
@@ -252,5 +511,7 @@
 #define ASIZ_thread_sig_desc	0x00000008
 #define AOFF_thread_sstk_info	0x00000480
 #define ASIZ_thread_sstk_info	0x00000010
+
+#endif /* __SMP__ */
 
 #endif /* __ASM_OFFSETS_H__ */
diff -u --recursive --new-file v2.1.96/linux/include/asm-sparc64/ebus.h linux/include/asm-sparc64/ebus.h
--- v2.1.96/linux/include/asm-sparc64/ebus.h	Mon Jan 12 15:15:54 1998
+++ linux/include/asm-sparc64/ebus.h	Tue Apr 14 17:44:24 1998
@@ -1,4 +1,4 @@
-/* $Id: ebus.h,v 1.4 1998/01/10 18:26:08 ecd Exp $
+/* $Id: ebus.h,v 1.5 1998/03/15 10:14:46 ecd Exp $
  * ebus.h: PCI to Ebus pseudo driver software state.
  *
  * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be)
@@ -42,6 +42,9 @@
 	char				 prom_name[64];
 	struct linux_prom_ebus_ranges	 ebus_ranges[PROMREG_MAX];
 	int				 num_ebus_ranges;
+	struct linux_prom_ebus_intmap	 ebus_intmap[PROMREG_MAX];
+	int				 num_ebus_intmap;
+	struct linux_prom_ebus_intmask	 ebus_intmask;
 };
 
 struct linux_ebus_dma {
diff -u --recursive --new-file v2.1.96/linux/include/asm-sparc64/elf.h linux/include/asm-sparc64/elf.h
--- v2.1.96/linux/include/asm-sparc64/elf.h	Thu Feb 12 20:56:13 1998
+++ linux/include/asm-sparc64/elf.h	Tue Apr 14 17:44:24 1998
@@ -1,4 +1,4 @@
-/* $Id: elf.h,v 1.13 1997/10/03 18:44:14 davem Exp $ */
+/* $Id: elf.h,v 1.17 1998/03/23 10:07:06 jj Exp $ */
 #ifndef __ASM_SPARC64_ELF_H
 #define __ASM_SPARC64_ELF_H
 
@@ -20,7 +20,7 @@
  * These are used to set parameters in the core dumps.
  */
 #ifndef ELF_ARCH
-#define ELF_ARCH		EM_SPARC64
+#define ELF_ARCH		EM_SPARCV9
 #define ELF_CLASS		ELFCLASS64
 #define ELF_DATA		ELFDATA2MSB
 #endif
@@ -29,7 +29,7 @@
  * This is used to ensure we don't load something for the wrong architecture.
  */
 #ifndef elf_check_arch
-#define elf_check_arch(x) ((x) == ELF_ARCH)	/* Might be EM_SPARC64 or EM_SPARC */
+#define elf_check_arch(x) ((x) == ELF_ARCH)	/* Might be EM_SPARCV9 or EM_SPARC */
 #endif
 
 #define USE_ELF_CORE_DUMP
@@ -50,7 +50,8 @@
 
 /* On Ultra, we support all of the v8 capabilities. */
 #define ELF_HWCAP	(HWCAP_SPARC_FLUSH | HWCAP_SPARC_STBAR | \
-			 HWCAP_SPARC_SWAP | HWCAP_SPARC_MULDIV)
+			 HWCAP_SPARC_SWAP | HWCAP_SPARC_MULDIV | \
+			 HWCAP_SPARC_V9)
 
 /* This yields a string that ld.so will use to load implementation
    specific libraries for optimization.  This is more specific in
diff -u --recursive --new-file v2.1.96/linux/include/asm-sparc64/ide.h linux/include/asm-sparc64/ide.h
--- v2.1.96/linux/include/asm-sparc64/ide.h	Thu Sep  4 12:54:49 1997
+++ linux/include/asm-sparc64/ide.h	Tue Apr 14 17:44:24 1998
@@ -1,7 +1,8 @@
-/* $Id: ide.h,v 1.4 1997/08/30 16:29:29 davem Exp $
+/* $Id: ide.h,v 1.6 1998/03/15 13:29:13 ecd Exp $
  * ide.h: Ultra/PCI specific IDE glue.
  *
- * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1997  David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1998  Eddie C. Dost   (ecd@skynet.be)
  */
 
 #ifndef _SPARC64_IDE_H
@@ -14,42 +15,31 @@
 #undef  MAX_HWIFS
 #define MAX_HWIFS	2
 
-extern int sparc64_ide_ports_known;
-extern ide_ioreg_t sparc64_ide_regbase[MAX_HWIFS];
-extern ide_ioreg_t sparc64_idedma_regbase; /* one for both channels */
-extern unsigned int sparc64_ide_irq;
-
-extern void sparc64_ide_probe(void);
-
 #define	ide_sti()	sti()
 
 static __inline__ int ide_default_irq(ide_ioreg_t base)
 {
-	if(sparc64_ide_ports_known == 0)
-		sparc64_ide_probe();
-	return sparc64_ide_irq;
+	return 0;
 }
 
 static __inline__ ide_ioreg_t ide_default_io_base(int index)
 {
-	if(sparc64_ide_ports_known == 0)
-		sparc64_ide_probe();
-	return sparc64_ide_regbase[index];
+	return 0;
 }
 
 static __inline__ void ide_init_hwif_ports(ide_ioreg_t *p, ide_ioreg_t base, int *irq)
 {
-	ide_ioreg_t port = base;
-	int i = 9;
+	int i;
 
-	while(i--)
-		*p++ = port++;
-	if(base == sparc64_ide_regbase[0])
-		*p = sparc64_idedma_regbase + 0x2;
-	else
-		*p = sparc64_idedma_regbase + 0xa;
+	/* These are simply offsets from base. */
+	for (i = 0; i < 8; i++)
+		*p++ = base++;
+	/* PCI code needs to figure out these. */
+	for ( ; i < 10; i++)
+		*p++ = 0;
+	/* PCI code needs to figure out this. */
 	if(irq != NULL)
-		*irq = sparc64_ide_irq;
+		*irq = 0;
 }
 
 typedef union {
@@ -102,13 +92,7 @@
 
 static __inline__ int ide_ack_intr(ide_ioreg_t status_port, ide_ioreg_t irq_port)
 {
-	unsigned char stat = inb(irq_port);
-
-	if(stat & 0x4) {
-		outb((inb(irq_port) & 0x60) | 4, irq_port);
-		return 1;
-	}
-	return 0;
+	return 1;
 }
 
 /* From m68k code... */
diff -u --recursive --new-file v2.1.96/linux/include/asm-sparc64/io.h linux/include/asm-sparc64/io.h
--- v2.1.96/linux/include/asm-sparc64/io.h	Mon Jan 12 15:15:58 1998
+++ linux/include/asm-sparc64/io.h	Tue Apr 14 17:44:24 1998
@@ -1,4 +1,4 @@
-/* $Id: io.h,v 1.14 1997/11/01 10:23:58 ecd Exp $ */
+/* $Id: io.h,v 1.16 1998/03/24 05:54:40 ecd Exp $ */
 #ifndef __SPARC64_IO_H
 #define __SPARC64_IO_H
 
@@ -13,14 +13,22 @@
 #define __SLOW_DOWN_IO	do { } while (0)
 #define SLOW_DOWN_IO	do { } while (0)
 
+extern unsigned long pci_dvma_offset;
+extern unsigned long pci_dvma_mask;
+
 extern __inline__ unsigned long virt_to_phys(volatile void *addr)
 {
-	return ((((unsigned long)addr) - PAGE_OFFSET) | 0x80000000UL);
+	unsigned long vaddr = (unsigned long)addr;
+
+	/* Handle kernel variable pointers... */
+	if (vaddr < PAGE_OFFSET)
+		vaddr += PAGE_OFFSET - (unsigned long)&empty_zero_page;
+	return ((vaddr - PAGE_OFFSET) | pci_dvma_offset);
 }
 
 extern __inline__ void *phys_to_virt(unsigned long addr)
 {
-	return ((void *)((addr & ~0x80000000) + PAGE_OFFSET));
+	return ((void *)((addr & pci_dvma_mask) + PAGE_OFFSET));
 }
 
 #define virt_to_bus virt_to_phys
@@ -91,9 +99,9 @@
 extern void insl(unsigned long addr, void *dst, unsigned long count);
 
 /* Memory functions, same as I/O accesses on Ultra. */
-#define readb(addr)		inb((unsigned long)addr)
-#define readw(addr)		inw((unsigned long)addr)
-#define readl(addr)		inl((unsigned long)addr)
+#define readb(addr)		inb((unsigned long)(addr))
+#define readw(addr)		inw((unsigned long)(addr))
+#define readl(addr)		inl((unsigned long)(addr))
 #define writeb(b, addr)		outb((b), (unsigned long)(addr))
 #define writew(w, addr)		outw((w), (unsigned long)(addr))
 #define writel(l, addr)		outl((l), (unsigned long)(addr))
diff -u --recursive --new-file v2.1.96/linux/include/asm-sparc64/ioctls.h linux/include/asm-sparc64/ioctls.h
--- v2.1.96/linux/include/asm-sparc64/ioctls.h	Tue Feb 17 13:12:49 1998
+++ linux/include/asm-sparc64/ioctls.h	Tue Apr 14 17:44:24 1998
@@ -1,4 +1,4 @@
-/* $Id: ioctls.h,v 1.6 1997/12/16 19:20:58 davem Exp $ */
+/* $Id: ioctls.h,v 1.7 1998/02/23 02:49:41 davem Exp $ */
 #ifndef _ASM_SPARC64_IOCTLS_H
 #define _ASM_SPARC64_IOCTLS_H
 
diff -u --recursive --new-file v2.1.96/linux/include/asm-sparc64/irq.h linux/include/asm-sparc64/irq.h
--- v2.1.96/linux/include/asm-sparc64/irq.h	Mon Jan 12 15:15:58 1998
+++ linux/include/asm-sparc64/irq.h	Fri Apr 17 22:04:44 1998
@@ -1,4 +1,4 @@
-/* $Id: irq.h,v 1.7 1997/09/07 02:56:44 davem Exp $
+/* $Id: irq.h,v 1.8 1998/03/15 17:23:51 ecd Exp $
  * irq.h: IRQ registers on the 64-bit Sparc.
  *
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
@@ -39,7 +39,8 @@
 
 extern void disable_irq(unsigned int);
 extern void enable_irq(unsigned int);
-extern void init_timers(void (*lvl10_irq)(int, void *, struct pt_regs *));
+extern void init_timers(void (*lvl10_irq)(int, void *, struct pt_regs *),
+			unsigned long *);
 
 #ifdef __SMP__
 extern void set_cpu_int(int, int);
@@ -73,6 +74,11 @@
 	__asm__ __volatile__("rd	%%softint, %0"
 			     : "=r" (retval));
 	return retval;
+}
+
+static __inline__ int irq_cannonicalize(int irq)
+{
+	return irq;
 }
 
 #endif
diff -u --recursive --new-file v2.1.96/linux/include/asm-sparc64/namei.h linux/include/asm-sparc64/namei.h
--- v2.1.96/linux/include/asm-sparc64/namei.h	Mon Jan 12 15:15:58 1998
+++ linux/include/asm-sparc64/namei.h	Tue Apr 14 17:44:25 1998
@@ -1,4 +1,4 @@
-/* $Id: namei.h,v 1.10 1997/09/24 16:20:41 jj Exp $
+/* $Id: namei.h,v 1.12 1998/03/13 17:38:13 jj Exp $
  * linux/include/asm-sparc64/namei.h
  *
  * Routines to handle famous /usr/gnemul/s*.
@@ -10,7 +10,6 @@
 
 #define SPARC_BSD_EMUL "usr/gnemul/sunos/"
 #define SPARC_SOL_EMUL "usr/gnemul/solaris/"
-#define SPARC_LIN_EMUL "usr/gnemul/linux32/"
 
 static inline struct dentry *
 __sparc64_lookup_dentry(const char *name, int follow_link)
@@ -19,12 +18,13 @@
 	char *emul;
 
 	switch (current->personality) {
+#if 0
+/* Until we solve, why SunOS apps sometime crash, disable gnemul support for SunOS */
 	case PER_BSD:
 		emul = SPARC_BSD_EMUL; break;
+#endif		
 	case PER_SVR4:
 		emul = SPARC_SOL_EMUL; break;
-	case PER_LINUX32:
-		emul = SPARC_LIN_EMUL; break;
 	default:
 		return NULL;
 	}
diff -u --recursive --new-file v2.1.96/linux/include/asm-sparc64/openprom.h linux/include/asm-sparc64/openprom.h
--- v2.1.96/linux/include/asm-sparc64/openprom.h	Thu Sep  4 12:54:49 1997
+++ linux/include/asm-sparc64/openprom.h	Tue Apr 14 17:44:25 1998
@@ -1,4 +1,4 @@
-/* $Id: openprom.h,v 1.6 1997/08/17 22:40:09 ecd Exp $ */
+/* $Id: openprom.h,v 1.7 1998/03/15 10:14:47 ecd Exp $ */
 #ifndef __SPARC64_OPENPROM_H
 #define __SPARC64_OPENPROM_H
 
@@ -232,6 +232,24 @@
 	unsigned int size_lo;
 };
 
+struct linux_prom_pci_intmap {
+	unsigned int phys_hi;
+	unsigned int phys_mid;
+	unsigned int phys_lo;
+
+	unsigned int interrupt;
+
+	int          cnode;
+	unsigned int cinterrupt;
+};
+
+struct linux_prom_pci_intmask {
+	unsigned int phys_hi;
+	unsigned int phys_mid;
+	unsigned int phys_lo;
+	unsigned int interrupt;
+};
+
 struct linux_prom_ebus_ranges {
 	unsigned int child_phys_hi;
 	unsigned int child_phys_lo;
@@ -243,6 +261,21 @@
 	unsigned int size;
 };
 
+struct linux_prom_ebus_intmap {
+	unsigned int phys_hi;
+	unsigned int phys_lo;
+
+	unsigned int interrupt;
+
+	int          cnode;
+	unsigned int cinterrupt;
+};
+
+struct linux_prom_ebus_intmask {
+	unsigned int phys_hi;
+	unsigned int phys_lo;
+	unsigned int interrupt;
+};
 #endif /* !(__ASSEMBLY__) */
 
 #endif /* !(__SPARC64_OPENPROM_H) */
diff -u --recursive --new-file v2.1.96/linux/include/asm-sparc64/page.h linux/include/asm-sparc64/page.h
--- v2.1.96/linux/include/asm-sparc64/page.h	Mon Jan 12 15:15:58 1998
+++ linux/include/asm-sparc64/page.h	Tue Apr 14 17:44:25 1998
@@ -1,4 +1,4 @@
-/* $Id: page.h,v 1.16 1997/11/28 15:59:34 jj Exp $ */
+/* $Id: page.h,v 1.17 1998/01/14 17:16:28 jj Exp $ */
 
 #ifndef _SPARC64_PAGE_H
 #define _SPARC64_PAGE_H
@@ -31,16 +31,16 @@
 /* These are used to make use of C type-checking.. */
 typedef struct { unsigned long pte; } pte_t;
 typedef struct { unsigned long iopte; } iopte_t;
-typedef struct { unsigned long pmd; } pmd_t;
-typedef struct { unsigned long pgd; } pgd_t;
+typedef struct { unsigned int pmd; } pmd_t;
+typedef struct { unsigned int pgd; } pgd_t;
 typedef struct { unsigned long ctxd; } ctxd_t;
 typedef struct { unsigned long pgprot; } pgprot_t;
 typedef struct { unsigned long iopgprot; } iopgprot_t;
 
 #define pte_val(x)	((x).pte)
 #define iopte_val(x)	((x).iopte)
-#define pmd_val(x)      ((x).pmd)
-#define pgd_val(x)	((x).pgd)
+#define pmd_val(x)      ((unsigned long)(x).pmd)
+#define pgd_val(x)	((unsigned long)(x).pgd)
 #define ctxd_val(x)	((x).ctxd)
 #define pgprot_val(x)	((x).pgprot)
 #define iopgprot_val(x)	((x).iopgprot)
@@ -57,16 +57,16 @@
 /* .. while these make it easier on the compiler */
 typedef unsigned long pte_t;
 typedef unsigned long iopte_t;
-typedef unsigned long pmd_t;
-typedef unsigned long pgd_t;
+typedef unsigned int pmd_t;
+typedef unsigned int pgd_t;
 typedef unsigned long ctxd_t;
 typedef unsigned long pgprot_t;
 typedef unsigned long iopgprot_t;
 
 #define pte_val(x)	(x)
 #define iopte_val(x)	(x)
-#define pmd_val(x)      (x)
-#define pgd_val(x)	(x)
+#define pmd_val(x)      ((unsigned long)(x))
+#define pgd_val(x)	((unsigned long)(x))
 #define ctxd_val(x)	(x)
 #define pgprot_val(x)	(x)
 #define iopgprot_val(x)	(x)
@@ -83,7 +83,7 @@
 
 #define TASK_UNMAPPED_BASE	((current->tss.flags & SPARC_FLAG_32BIT) ? \
 				 (0x0000000070000000UL) : \
-				 (0x0000030000000000UL))
+				 (0xfffff80000000000UL))
 
 #endif /* !(__ASSEMBLY__) */
 
diff -u --recursive --new-file v2.1.96/linux/include/asm-sparc64/pbm.h linux/include/asm-sparc64/pbm.h
--- v2.1.96/linux/include/asm-sparc64/pbm.h	Mon Jan 12 15:15:58 1998
+++ linux/include/asm-sparc64/pbm.h	Tue Apr 14 17:44:25 1998
@@ -1,4 +1,4 @@
-/* $Id: pbm.h,v 1.8 1998/01/10 18:26:10 ecd Exp $
+/* $Id: pbm.h,v 1.12 1998/04/10 12:29:55 ecd Exp $
  * pbm.h: U2P PCI bus module pseudo driver software state.
  *
  * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
@@ -7,7 +7,6 @@
 #ifndef __SPARC64_PBM_H
 #define __SPARC64_PBM_H
 
-#include <linux/bios32.h>
 #include <linux/pci.h>
 
 #include <asm/psycho.h>
@@ -25,7 +24,7 @@
 	struct linux_pbm_info		*pbm;
 	unsigned int			start;
 	unsigned int			end;
-	unsigned int			base_reg;
+	unsigned int			offset;
 	unsigned int			_pad;
 };
 
@@ -39,6 +38,9 @@
 	char				prom_name[64];
 	struct linux_prom_pci_ranges	pbm_ranges[PROMREG_MAX];
 	int				num_pbm_ranges;
+	struct linux_prom_pci_intmap	pbm_intmap[PROMREG_MAX];
+	int				num_pbm_intmap;
+	struct linux_prom_pci_intmask	pbm_intmask;
 
 	/* Now things for the actual PCI bus probes. */
 	unsigned int			pci_first_busno;
@@ -56,6 +58,11 @@
 	int				index;
 	struct linux_pbm_info		pbm_A;
 	struct linux_pbm_info		pbm_B;
+
+	/* Now things for the actual PCI bus probes. */
+	unsigned int			pci_first_busno;
+	unsigned int			pci_last_busno;
+	struct pci_bus			*pci_bus;
 };
 
 /* PCI devices which are not bridges have this placed in their pci_dev
@@ -86,7 +93,7 @@
 #define PCI_IRQ_IDENT		0x80000000	/* This tells irq.c what we are        */
 #define PCI_IRQ_IMAP_OFF	0x7ff00000	/* Offset from first PSYCHO imap       */
 #define PCI_IRQ_IMAP_OFF_SHFT	20
-#define PCI_IRQ_BUSNO		0x000fc000	/* PSYCHO instance, currently unused   */
+#define PCI_IRQ_BUSNO		0x000fc000	/* PSYCHO instance                     */
 #define PCI_IRQ_BUSNO_SHFT	14
 #define PCI_IRQ_IGN		0x000007c0	/* PSYCHO "Int Group Number"           */
 #define PCI_IRQ_INO		0x0000003f	/* PSYCHO INO                          */
diff -u --recursive --new-file v2.1.96/linux/include/asm-sparc64/pgtable.h linux/include/asm-sparc64/pgtable.h
--- v2.1.96/linux/include/asm-sparc64/pgtable.h	Mon Jan 12 15:15:58 1998
+++ linux/include/asm-sparc64/pgtable.h	Tue Apr 14 17:44:25 1998
@@ -1,7 +1,8 @@
-/* $Id: pgtable.h,v 1.59 1997/10/12 06:20:43 davem Exp $
+/* $Id: pgtable.h,v 1.64 1998/02/16 14:06:44 jj Exp $
  * pgtable.h: SpitFire page table operations.
  *
  * Copyright 1996,1997 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
  */
 
 #ifndef _SPARC64_PGTABLE_H
@@ -34,18 +35,20 @@
 #define PMD_MASK	(~(PMD_SIZE-1))
 
 /* PGDIR_SHIFT determines what a third-level page table entry can map */
-#define PGDIR_SHIFT	(PAGE_SHIFT + 2*(PAGE_SHIFT-3))
+#define PGDIR_SHIFT	(PAGE_SHIFT + (PAGE_SHIFT-3) + (PAGE_SHIFT-2))
 #define PGDIR_SIZE	(1UL << PGDIR_SHIFT)
 #define PGDIR_MASK	(~(PGDIR_SIZE-1))
 
 /* Entries per page directory level. */
-#define PTRS_PER_PTE	(1UL << (PAGE_SHIFT-3))
-#define PTRS_PER_PMD	(1UL << (PAGE_SHIFT-3))
-#define PTRS_PER_PGD	(1UL << (PAGE_SHIFT-3))
+#define PTRS_PER_PTE		(1UL << (PAGE_SHIFT-3))
+#define PTRS_PER_PMD		(1UL << (PAGE_SHIFT-2))
+/* We cannot use the top 16G because a half of mm/ would break, so why to check it */
+#define PTRS_PER_PGD		((1UL << (PAGE_SHIFT-3))-1)
+#define USER_PTRS_PER_PGD	PTRS_PER_PGD	/* Kernel has a separate 44bit address space */
 
 #define PTE_TABLE_SIZE	0x2000	/* 1024 entries 8 bytes each */
-#define PMD_TABLE_SIZE	0x2000	/* 1024 entries 8 bytes each */
-#define PGD_TABLE_SIZE	0x2000	/* 1024 entries 8 bytes each */
+#define PMD_TABLE_SIZE	0x2000	/* 2048 entries 4 bytes each */
+#define PGD_TABLE_SIZE	0x1000	/* 1024 entries 4 bytes each */
 
 /* the no. of pointers that fit on a page */
 #define PTRS_PER_PAGE	(1UL << (PAGE_SHIFT-3))
@@ -141,7 +144,7 @@
 #define BAD_PTE		__bad_pte()
 #define BAD_PAGE	__bad_page()
 
-/* First phsical page can be anywhere, the following is needed so that
+/* First physical page can be anywhere, the following is needed so that
  * va-->pa and vice versa conversions work properly without performance
  * hit for all __pa()/__va() operations.
  */
@@ -149,8 +152,8 @@
 #define ZERO_PAGE	((unsigned long)__va(phys_base))
 
 /* This is for making TLB miss faster to process. */
-extern unsigned long null_pmd_table;
-extern unsigned long null_pte_table;
+#define null_pmd_table (null_pte_table - PAGE_SIZE)
+extern unsigned int null_pte_table;
 
 /* Allocate a block of RAM which is aligned to its size.
    This procedure can be used until the call to mem_init(). */
@@ -198,7 +201,7 @@
 	struct mm_struct *mm = vma->vm_mm;
 
 	if(mm->context != NO_CONTEXT)
-		__flush_tlb_page(mm->context & 0x1fff, page);
+		__flush_tlb_page(mm->context & 0x1fff, page & PAGE_MASK);
 }
 
 #else /* __SMP__ */
@@ -260,7 +263,7 @@
 extern inline unsigned long pgd_page(pgd_t pgd)
 { return (unsigned long) __va(pgd_val(pgd)); }
 
-#define PMD_NONE_MAGIC		0x80
+#define PMD_NONE_MAGIC		0x40
 #define PGD_NONE_MAGIC		0x40
 
 extern inline int pte_none(pte_t pte) 		{ return !pte_val(pte); }
@@ -323,18 +326,18 @@
 
 /* to find an entry in a page-table-directory. */
 extern inline pgd_t *pgd_offset(struct mm_struct *mm, unsigned long address)
-{ return mm->pgd + ((address >> PGDIR_SHIFT) & (PTRS_PER_PAGE - 1)); }
+{ return mm->pgd + ((address >> PGDIR_SHIFT) & (PTRS_PER_PGD)); }
 
 /* to find an entry in a kernel page-table-directory */
 #define pgd_offset_k(address) pgd_offset(&init_mm, address)
 
 /* Find an entry in the second-level page table.. */
 extern inline pmd_t *pmd_offset(pgd_t *dir, unsigned long address)
-{ return (pmd_t *) pgd_page(*dir) + ((address >> PMD_SHIFT) & (PTRS_PER_PAGE - 1)); }
+{ return (pmd_t *) pgd_page(*dir) + ((address >> PMD_SHIFT) & (PTRS_PER_PMD - 1)); }
 
 /* Find an entry in the third-level page table.. */
 extern inline pte_t *pte_offset(pmd_t *dir, unsigned long address)
-{ return (pte_t *) pmd_page(*dir) + ((address >> PAGE_SHIFT) & (PTRS_PER_PAGE - 1)); }
+{ return (pte_t *) pmd_page(*dir) + ((address >> PAGE_SHIFT) & (PTRS_PER_PTE - 1)); }
 
 /* Very stupidly, we used to get new pgd's and pmd's, init their contents
  * to point to the NULL versions of the next level page table, later on
@@ -345,79 +348,97 @@
 
 #ifdef __SMP__
 /* Sliiiicck */
-#define pgd_quicklist		(cpu_data[smp_processor_id()].pgd_cache)
-#define pmd_quicklist		(cpu_data[smp_processor_id()].pmd_cache)
-#define pte_quicklist		(cpu_data[smp_processor_id()].pte_cache)
-#define pgtable_cache_size	(cpu_data[smp_processor_id()].pgcache_size)
+#define pgt_quicklists	cpu_data[smp_processor_id()]
 #else
-extern unsigned long *pgd_quicklist;
-extern unsigned long *pmd_quicklist;
-extern unsigned long *pte_quicklist;
-extern unsigned long pgtable_cache_size;
+extern struct pgtable_cache_struct {
+	unsigned long *pgd_cache;
+	unsigned long *pmd_cache;
+	unsigned long *pte_cache;
+	unsigned long pgcache_size;
+} pgt_quicklists;
 #endif
+#define pgd_quicklist		(pgt_quicklists.pgd_cache)
+#define pmd_quicklist		(pgt_quicklists.pmd_cache)
+#define pte_quicklist		(pgt_quicklists.pte_cache)
+#define pgtable_cache_size	(pgt_quicklists.pgcache_size)
 
 extern pgd_t *get_pgd_slow(void);
 
 extern __inline__ pgd_t *get_pgd_fast(void)
 {
-	pgd_t *ret;
+	unsigned long *ret;
 
-	if((ret = (pgd_t *)pgd_quicklist) != NULL) {
-		pgd_quicklist = (unsigned long *)pgd_val(*ret);
-		pgd_val(ret[0]) = pgd_val(ret[1]);
-		(pgtable_cache_size)--;
+	if((ret = pgd_quicklist) != NULL) {
+		pgd_quicklist = (unsigned long *)(*ret);
+		ret[0] = ret[1];
+		pgtable_cache_size--;
 	} else
-		ret = get_pgd_slow();
-	return ret;
+		ret = (unsigned long *)get_pgd_slow();
+	return (pgd_t *)ret;
 }
 
 extern __inline__ void free_pgd_fast(pgd_t *pgd)
 {
-	pgd_val(*pgd) = (unsigned long) pgd_quicklist;
+	*(unsigned long *)pgd = (unsigned long) pgd_quicklist;
 	pgd_quicklist = (unsigned long *) pgd;
-	(pgtable_cache_size)++;
+	pgtable_cache_size++;
+}
+
+extern __inline__ void free_pgd_slow(pgd_t *pgd)
+{
+	free_page((unsigned long)pgd);
 }
 
 extern pmd_t *get_pmd_slow(pgd_t *pgd, unsigned long address_premasked);
 
 extern __inline__ pmd_t *get_pmd_fast(void)
 {
-	pmd_t *ret;
+	unsigned long *ret;
 
-	if((ret = (pmd_t *)pmd_quicklist) != NULL) {
-		pmd_quicklist = (unsigned long *)pmd_val(*ret);
-		pmd_val(ret[0]) = pmd_val(ret[1]);
-		(pgtable_cache_size)--;
+	if((ret = (unsigned long *)pmd_quicklist) != NULL) {
+		pmd_quicklist = (unsigned long *)(*ret);
+		ret[0] = ret[1];
+		pgtable_cache_size--;
 	}
-	return ret;
+	return (pmd_t *)ret;
 }
 
 extern __inline__ void free_pmd_fast(pgd_t *pmd)
 {
-	pmd_val(*pmd) = (unsigned long) pmd_quicklist;
+	*(unsigned long *)pmd = (unsigned long) pmd_quicklist;
 	pmd_quicklist = (unsigned long *) pmd;
-	(pgtable_cache_size)++;
+	pgtable_cache_size++;
+}
+
+extern __inline__ void free_pmd_slow(pmd_t *pmd)
+{
+	free_page((unsigned long)pmd);
 }
 
 extern pte_t *get_pte_slow(pmd_t *pmd, unsigned long address_preadjusted);
 
 extern __inline__ pte_t *get_pte_fast(void)
 {
-	pte_t *ret;
+	unsigned long *ret;
 
-	if((ret = (pte_t *)pte_quicklist) != NULL) {
-		pte_quicklist = (unsigned long *)pte_val(*ret);
-		pte_val(ret[0]) = pte_val(ret[1]);
-		(pgtable_cache_size)--;
+	if((ret = (unsigned long *)pte_quicklist) != NULL) {
+		pte_quicklist = (unsigned long *)(*ret);
+		ret[0] = ret[1];
+		pgtable_cache_size--;
 	}
-	return ret;
+	return (pte_t *)ret;
 }
 
 extern __inline__ void free_pte_fast(pte_t *pte)
 {
-	pte_val(*pte) = (unsigned long) pte_quicklist;
+	*(unsigned long *)pte = (unsigned long) pte_quicklist;
 	pte_quicklist = (unsigned long *) pte;
-	(pgtable_cache_size)++;
+	pgtable_cache_size++;
+}
+
+extern __inline__ void free_pte_slow(pte_t *pte)
+{
+	free_page((unsigned long)pte);
 }
 
 #define pte_free_kernel(pte)	free_pte_fast(pte)
@@ -458,6 +479,11 @@
 #define pte_alloc_kernel(pmd, addr)	pte_alloc(pmd, addr)
 #define pmd_alloc_kernel(pgd, addr)	pmd_alloc(pgd, addr)
 
+extern inline void set_pgdir(unsigned long address, pgd_t entry)
+{
+	/* Nothing to do on sparc64 :) */
+}
+
 extern pgd_t swapper_pg_dir[1024];
 
 extern inline void SET_PAGE_DIR(struct task_struct *tsk, pgd_t *pgdir)
@@ -570,6 +596,10 @@
 
 extern void * module_map (unsigned long size);
 extern void module_unmap (void *addr);
+extern void module_shrink (void *addr, unsigned long size);
+
+/* Needs to be defined here and not in linux/mm.h, as it is arch dependent */
+#define PageSkip(page)		(test_bit(PG_skip, &(page)->flags))
 
 #endif /* !(__ASSEMBLY__) */
 
diff -u --recursive --new-file v2.1.96/linux/include/asm-sparc64/processor.h linux/include/asm-sparc64/processor.h
--- v2.1.96/linux/include/asm-sparc64/processor.h	Fri Jan 16 20:36:17 1998
+++ linux/include/asm-sparc64/processor.h	Tue Apr 14 17:44:25 1998
@@ -1,4 +1,4 @@
-/* $Id: processor.h,v 1.40 1997/10/24 11:57:59 jj Exp $
+/* $Id: processor.h,v 1.42 1998/01/28 10:00:04 ecd Exp $
  * include/asm-sparc64/processor.h
  *
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
@@ -25,7 +25,11 @@
 #define wp_works_ok__is_a_macro /* for versions in ksyms.c */
 
 /* User lives in his very own context, and cannot reference us. */
-#define TASK_SIZE	((1UL << (PAGE_SHIFT - 3)) * PGDIR_SIZE)
+#ifndef __ASSEMBLY__
+#define TASK_SIZE	((unsigned long)-PGDIR_SIZE)
+#else
+#define TASK_SIZE	0xfffffffc00000000
+#endif
 
 #define COPY_TASK_STRUCT(dst, src)										\
 do {														\
diff -u --recursive --new-file v2.1.96/linux/include/asm-sparc64/psycho.h linux/include/asm-sparc64/psycho.h
--- v2.1.96/linux/include/asm-sparc64/psycho.h	Sat Aug 16 09:51:10 1997
+++ linux/include/asm-sparc64/psycho.h	Tue Apr 14 17:44:25 1998
@@ -1,4 +1,4 @@
-/* $Id: psycho.h,v 1.2 1997/08/11 14:35:40 davem Exp $
+/* $Id: psycho.h,v 1.3 1998/03/15 13:24:28 ecd Exp $
  * psycho.h: UltraSparc AX specific PCI definitions.
  *
  * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be)
@@ -42,47 +42,47 @@
 		u64	__pad4[0x13d];
 
 		/* Interrupt mapping/control registers */
-/*0x0c00*/	u64	imap_a_slot0;	/* PCI A Slot 0 Int Mapping */
-/*0x0c08*/	u64	imap_a_slot1;	/* PCI A Slot 1 Int Mapping */
-
-		u64	__pad5[0x2];
-
-/*0x0c20*/	u64	imap_b_slot0;	/* PCI B Slot 0 Int Mapping */
-/*0x0c28*/	u64	imap_b_slot1;	/* PCI B Slot 1 Int Mapping */
-/*0x0c30*/	u64	imap_b_slot2;	/* PCI B Slot 2 Int Mapping */
-/*0x0c38*/	u64	imap_b_slot3;	/* PCI B Slot 3 Int Mapping */
+/*0x0c00*/	u64	imap_a_slot0;	/* PCI A Slot 0 Int Mapping	*/
+/*0x0c08*/	u64	imap_a_slot1;	/* PCI A Slot 1 Int Mapping	*/
+/*0x0c10*/	u64	imap_a_slot2;	/* PCI A Slot 2 Int Mapping (IIi only)*/
+/*0x0c18*/	u64	imap_a_slot3;	/* PCI A Slot 3 Int Mapping (IIi only)*/
+
+/*0x0c20*/	u64	imap_b_slot0;	/* PCI B Slot 0 Int Mapping	*/
+/*0x0c28*/	u64	imap_b_slot1;	/* PCI B Slot 1 Int Mapping	*/
+/*0x0c30*/	u64	imap_b_slot2;	/* PCI B Slot 2 Int Mapping	*/
+/*0x0c38*/	u64	imap_b_slot3;	/* PCI B Slot 3 Int Mapping	*/
 
 		u64	__pad6[0x78];
 
-/*0x1000*/	u64	imap_scsi;	/* SCSI Int Mapping	*/
-/*0x1008*/	u64	imap_eth;	/* Ethernet Int Mapping	*/
-/*0x1010*/	u64	imap_bpp;	/* Parallel Port Int Mapping */
-/*0x1018*/	u64	imap_au_rec;	/* Audio Record Int Mapping */
-/*0x1020*/	u64	imap_au_play;	/* Audio Playback Int Mapping */
-/*0x1028*/	u64	imap_pfail;	/* Power Fail Int Mapping */
-/*0x1030*/	u64	imap_kms;	/* Kbd/Mouse/Ser Int Mapping */
-/*0x1038*/	u64	imap_flpy;	/* Floppy Int Mapping	 */
-/*0x1040*/	u64	imap_shw;	/* Spare HW Int Mapping	*/
-/*0x1048*/	u64	imap_kbd;	/* Kbd Only Int Mapping	*/
-/*0x1050*/	u64	imap_ms;	/* Mouse Only Int Mapping */
-/*0x1058*/	u64	imap_ser;	/* Serial Only Int Mapping */
-/*0x1060*/	u64	imap_tim0;	/* Timer 0 Int Mapping	*/
-/*0x1068*/	u64	imap_tim1;	/* Timer 1 Int Mapping	*/
-/*0x1070*/	u64	imap_ue;	/* UE Int Mapping	*/
-/*0x1078*/	u64	imap_ce;	/* CE Int Mapping	*/
-/*0x1080*/	u64	imap_a_err;	/* PCI A Err Int Mapping */
-/*0x1088*/	u64	imap_b_err;	/* PCI B Err Int Mapping */
-/*0x1090*/	u64	imap_pmgmt;	/* Power Mgmt Int Mapping */
-/*0x1098*/	u64	imap_gfx;	/* OB Graphics Int Mapping */
-/*0x10a0*/	u64	imap_eupa;	/* UPA Expansion Int Mapping */
+/*0x1000*/	u64	imap_scsi;	/* SCSI Int Mapping		*/
+/*0x1008*/	u64	imap_eth;	/* Ethernet Int Mapping		*/
+/*0x1010*/	u64	imap_bpp;	/* Parallel Port Int Mapping	*/
+/*0x1018*/	u64	imap_au_rec;	/* Audio Record Int Mapping	*/
+/*0x1020*/	u64	imap_au_play;	/* Audio Playback Int Mapping	*/
+/*0x1028*/	u64	imap_pfail;	/* Power Fail Int Mapping	*/
+/*0x1030*/	u64	imap_kms;	/* Kbd/Mouse/Ser Int Mapping	*/
+/*0x1038*/	u64	imap_flpy;	/* Floppy Int Mapping		*/
+/*0x1040*/	u64	imap_shw;	/* Spare HW Int Mapping		*/
+/*0x1048*/	u64	imap_kbd;	/* Kbd Only Int Mapping		*/
+/*0x1050*/	u64	imap_ms;	/* Mouse Only Int Mapping	*/
+/*0x1058*/	u64	imap_ser;	/* Serial Only Int Mapping	*/
+/*0x1060*/	u64	imap_tim0;	/* Timer 0 Int Mapping		*/
+/*0x1068*/	u64	imap_tim1;	/* Timer 1 Int Mapping		*/
+/*0x1070*/	u64	imap_ue;	/* UE Int Mapping		*/
+/*0x1078*/	u64	imap_ce;	/* CE Int Mapping		*/
+/*0x1080*/	u64	imap_a_err;	/* PCI A Err Int Mapping	*/
+/*0x1088*/	u64	imap_b_err;	/* PCI B Err Int Mapping	*/
+/*0x1090*/	u64	imap_pmgmt;	/* Power Mgmt Int Mapping	*/
+/*0x1098*/	u64	imap_gfx;	/* OB Graphics Int Mapping	*/
+/*0x10a0*/	u64	imap_eupa;	/* UPA Expansion Int Mapping	*/
 
 		u64	__pad7[0x6b];
 
 		/* Interrupt Clear Registers */
 /*0x1400*/	u64	iclr_a_slot0[4];	/* PCI A Slot 0 Clear Int Reg */
 /*0x1420*/	u64	iclr_a_slot1[4];	/* PCI A Slot 1 Clear Int Reg */
-
-		u64	__pad8[0x8];
+/*0x1440*/	u64	iclr_a_slot2[4];	/* PCI A Slot 2 Clear Int Reg */
+/*0x1460*/	u64	iclr_a_slot3[4];	/* PCI A Slot 3 Clear Int Reg */
 
 /*0x1480*/	u64	iclr_b_slot0[4];	/* PCI B Slot 0 Clear Int Reg */
 /*0x14a0*/	u64	iclr_b_slot1[4];	/* PCI B Slot 1 Clear Int Reg */
@@ -123,8 +123,9 @@
 /*0x1c08*/	u64	tim0_lim;
 /*0x1c10*/	u64	tim1_cnt;
 /*0x1c18*/	u64	tim1_lim;
+/*0x1c20*/	u64	pci_dma_wsync;	/* PCI DMA Write Sync Register (IIi) */
 
-		u64	__pad12[0x7c];
+		u64	__pad12[0x7b];
 
 		/* PCI Bus A Registers */
 /*0x2000*/	u64	pci_a_control;	/* PCI Bus A Control Register	*/
@@ -132,8 +133,9 @@
 /*0x2010*/	u64	pci_a_afsr;	/* PCI Bus A Async Fault Status	*/
 /*0x2018*/	u64	pci_a_afar;	/* PCI Bus A Async Fault Address*/
 /*0x2020*/	u64	pci_a_diag;	/* PCI Bus A Diag Register	*/
+/*0x2028*/	u64	pci_tasr;	/* PCI Target Address Space Reg (IIi) */
 
-		u64	__pad14[0xfb];
+		u64	__pad14[0xfa];
 
 		/* PCI Bus A/IOMMU Streaming Buffer Registers */
 /*0x2800*/	u64	sbuf_a_control;	/* StrBuffer Control		*/
diff -u --recursive --new-file v2.1.96/linux/include/asm-sparc64/sbus.h linux/include/asm-sparc64/sbus.h
--- v2.1.96/linux/include/asm-sparc64/sbus.h	Sat Aug 16 09:51:10 1997
+++ linux/include/asm-sparc64/sbus.h	Tue Apr 14 17:44:25 1998
@@ -1,4 +1,4 @@
-/* $Id: sbus.h,v 1.5 1997/08/12 04:13:16 ecd Exp $
+/* $Id: sbus.h,v 1.6 1998/03/09 14:05:03 jj Exp $
  * sbus.h:  Defines for the Sun SBus.
  *
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
@@ -49,8 +49,7 @@
   struct linux_sbus_device *child;     /* For ledma and espdma on sun4m */
   struct linux_sbus *my_bus;           /* Back ptr to sbus */
   int prom_node;                       /* PROM device tree node for this device */
-  char prom_name[64];                  /* PROM device name */
-  char linux_name[64];                 /* Name used internally by Linux */
+  char prom_name[32];                  /* PROM device name */
 
   struct linux_prom_registers reg_addrs[PROMREG_MAX];
   int num_registers, ranges_applied;
diff -u --recursive --new-file v2.1.96/linux/include/asm-sparc64/smp.h linux/include/asm-sparc64/smp.h
--- v2.1.96/linux/include/asm-sparc64/smp.h	Sat Aug 16 09:51:10 1997
+++ linux/include/asm-sparc64/smp.h	Tue Apr 14 17:44:25 1998
@@ -74,7 +74,12 @@
 extern void smp_store_cpu_info(int id);
 
 extern __volatile__ int cpu_number_map[NR_CPUS];
-extern __volatile__ int cpu_logical_map[NR_CPUS];
+extern __volatile__ int __cpu_logical_map[NR_CPUS];
+
+extern __inline__ int cpu_logical_map(int cpu)
+{
+	return __cpu_logical_map[cpu];
+}
 
 extern __inline__ int hard_smp_processor_id(void)
 {
@@ -88,10 +93,19 @@
 
 #define smp_processor_id() (current->processor)
 
+extern void smp_message_pass(int target, int msg, unsigned long data, int wait);
+
 #endif /* !(__ASSEMBLY__) */
 
 #define PROC_CHANGE_PENALTY	20
 
+#else /* !(__SMP__) */
+#ifndef __ASSEMBLY__ 
+extern __inline__ int cpu_logical_map(int cpu)
+{
+	return cpu;
+}
+#endif 
 #endif /* !(__SMP__) */
 
 #define NO_PROC_ID		0xFF
diff -u --recursive --new-file v2.1.96/linux/include/asm-sparc64/softirq.h linux/include/asm-sparc64/softirq.h
--- v2.1.96/linux/include/asm-sparc64/softirq.h	Thu Jul 31 13:09:18 1997
+++ linux/include/asm-sparc64/softirq.h	Tue Apr 14 17:44:25 1998
@@ -20,8 +20,8 @@
 
 extern int __sparc64_bh_counter;
 
-#define softirq_trylock()	(__sparc64_bh_counter ? 0 : (__sparc64_bh_counter=1))
-#define softirq_endlock()	(__sparc64_bh_counter = 0)
+#define softirq_trylock(cpu)	(__sparc64_bh_counter ? 0 : (__sparc64_bh_counter=1))
+#define softirq_endlock(cpu)	(__sparc64_bh_counter = 0)
 #define clear_active_bhs(x)	(bh_active &= ~(x))
 
 #define init_bh(nr, routine)	\
@@ -114,7 +114,7 @@
 	spin_unlock_irqrestore(&global_bh_lock, flags);	\
 } while(0)
 
-#define softirq_trylock()					\
+#define softirq_trylock(cpu)					\
 ({								\
 	int ret = 1;						\
 	if(atomic_add_return(1, &__sparc64_bh_counter) != 1) {	\
@@ -123,7 +123,7 @@
 	}							\
 	ret;							\
 })
-#define softirq_endlock()	atomic_dec(&__sparc64_bh_counter)
+#define softirq_endlock(cpu)	atomic_dec(&__sparc64_bh_counter)
 #define clear_active_bhs(mask)				\
 do {	unsigned long flags;				\
 	spin_lock_irqsave(&global_bh_lock, flags);	\
diff -u --recursive --new-file v2.1.96/linux/include/asm-sparc64/stat.h linux/include/asm-sparc64/stat.h
--- v2.1.96/linux/include/asm-sparc64/stat.h	Wed Apr 23 19:01:28 1997
+++ linux/include/asm-sparc64/stat.h	Tue Apr 14 17:44:25 1998
@@ -1,8 +1,9 @@
-/* $Id: stat.h,v 1.3 1997/04/18 14:34:43 jj Exp $ */
+/* $Id: stat.h,v 1.4 1998/02/06 12:52:14 jj Exp $ */
 #ifndef _SPARC64_STAT_H
 #define _SPARC64_STAT_H
 
 #include <linux/types.h>
+#include <linux/time.h>
 
 struct stat32 {
 	__kernel_dev_t32   st_dev;
@@ -40,5 +41,55 @@
 	off_t   st_blocks;
 	unsigned long  __unused4[2];
 };
+
+typedef __u64	__new_dev_t;
+
+struct stat64 {
+	__new_dev_t	st_dev;
+	__u64		st_ino;
+	__u32		st_mode;
+	__u32		st_nlink;
+	__s32		st_uid;
+	__s32		st_gid;
+	__new_dev_t	st_rdev;
+	__s64		st_size;
+	struct timespec	st_atim;
+	struct timespec	st_mtim;
+	struct timespec	st_ctim;
+	int		st_blksize;
+	long		st_blocks;
+	char		st_fstype[16];
+};
+
+struct stat64_32 {
+	__new_dev_t	st_dev;
+	__u64		st_ino;
+	__u32		st_mode;
+	__u32		st_nlink;
+	__s32		st_uid;
+	__s32		st_gid;
+	__new_dev_t	st_rdev;
+	__s64		st_size;
+	__u64		st_blocks;
+	__s32		st_atime;
+	__u32		__unused1;
+	__s32		st_mtime;
+	__u32		__unused2;
+	__s32		st_ctime;
+	__u32		__unused3;
+	__u32		st_blksize;
+	__u32		__unused4;
+};
+
+#define __XSTAT_VER_1		1
+#define __XSTAT_VER_2		2
+#define __XSTAT_VER_MASK	0xff
+
+#define __XSTAT_VER_XSTAT	0x000
+#define __XSTAT_VER_LXSTAT	0x100
+#define __XSTAT_VER_FXSTAT	0x200
+#define __XSTAT_VER_TYPEMASK	0xff00
+
+#define __XMKNOD_VER_1		1
 
 #endif
diff -u --recursive --new-file v2.1.96/linux/include/asm-sparc64/system.h linux/include/asm-sparc64/system.h
--- v2.1.96/linux/include/asm-sparc64/system.h	Mon Jan 12 15:15:58 1998
+++ linux/include/asm-sparc64/system.h	Tue Apr 14 17:44:25 1998
@@ -1,4 +1,4 @@
-/* $Id: system.h,v 1.37 1997/10/20 00:14:22 davem Exp $ */
+/* $Id: system.h,v 1.38 1998/01/30 11:00:11 jj Exp $ */
 #ifndef __SPARC64_SYSTEM_H
 #define __SPARC64_SYSTEM_H
 
@@ -22,7 +22,10 @@
 };
                   
 #define sparc_cpu_model sun4u
-                  
+
+/* This cannot ever be a sun4c nor sun4 :) That's just history. */
+#define ARCH_SUN4C_SUN4 0
+#define ARCH_SUN4 0
 
 extern unsigned long empty_bad_page;
 extern unsigned long empty_bad_pmd_table;
diff -u --recursive --new-file v2.1.96/linux/include/asm-sparc64/timer.h linux/include/asm-sparc64/timer.h
--- v2.1.96/linux/include/asm-sparc64/timer.h	Thu Jul 31 13:09:18 1997
+++ linux/include/asm-sparc64/timer.h	Tue Apr 14 17:44:25 1998
@@ -1,4 +1,4 @@
-/* $Id: timer.h,v 1.1 1997/07/23 10:38:00 davem Exp $
+/* $Id: timer.h,v 1.2 1998/03/15 17:23:52 ecd Exp $
  * timer.h: System timer definitions for sun5.
  *
  * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
@@ -47,5 +47,10 @@
  * gets delivered that often.
  */
 #define SUN5_HZ_TO_LIMIT(__hz)  (1000000/(__hz))
+
+#ifdef __SMP__
+extern unsigned long timer_tick_offset;
+extern void timer_tick_interrupt(struct pt_regs *);
+#endif
 
 #endif /* _SPARC64_TIMER_H */
diff -u --recursive --new-file v2.1.96/linux/include/asm-sparc64/ttable.h linux/include/asm-sparc64/ttable.h
--- v2.1.96/linux/include/asm-sparc64/ttable.h	Mon Jan 12 15:15:58 1998
+++ linux/include/asm-sparc64/ttable.h	Tue Apr 14 17:44:25 1998
@@ -1,4 +1,4 @@
-/* $Id: ttable.h,v 1.5 1997/10/14 16:21:34 jj Exp $ */
+/* $Id: ttable.h,v 1.6 1998/03/15 17:23:54 ecd Exp $ */
 #ifndef _SPARC64_TTABLE_H
 #define _SPARC64_TTABLE_H
 
@@ -133,20 +133,6 @@
 	call	routine;				\
 	 add	%sp, STACK_BIAS + REGWIN_SZ, %o1;	\
 	ba,a,pt	%xcc, rtrap_clr_l6;
-
-#ifdef __SMP__
-#define TRAP_TICK					\
-	rdpr	%pil, %g2;				\
-	wrpr	%g0, 15, %pil;				\
-	b,pt	%xcc, etrap_irq;			\
-	 rd	%pc, %g7;				\
-	call	smp_percpu_timer_interrupt;		\
-	 add	%sp, STACK_BIAS + REGWIN_SZ, %o0;	\
-	b,pt	%xcc, rtrap;				\
-	 clr	%l6;
-#else
-#define TRAP_TICK	TRAP_IRQ(handler_irq, 14)
-#endif
 
 #define TRAP_IVEC TRAP_NOSAVE(do_ivec)
 
diff -u --recursive --new-file v2.1.96/linux/include/asm-sparc64/unistd.h linux/include/asm-sparc64/unistd.h
--- v2.1.96/linux/include/asm-sparc64/unistd.h	Wed Apr  8 19:36:29 1998
+++ linux/include/asm-sparc64/unistd.h	Tue Apr 14 17:44:25 1998
@@ -1,4 +1,4 @@
-/* $Id: unistd.h,v 1.14 1997/12/11 15:16:08 jj Exp $ */
+/* $Id: unistd.h,v 1.17 1998/03/29 12:57:54 ecd Exp $ */
 #ifndef _SPARC64_UNISTD_H
 #define _SPARC64_UNISTD_H
 
@@ -29,19 +29,19 @@
 #define __NR_unlink              10 /* Common                                      */
 #define __NR_execv               11 /* SunOS Specific                              */
 #define __NR_chdir               12 /* Common                                      */
-/* #define __NR_ni_syscall       13    ENOSYS under SunOS                          */
+#define __NR_xstat		 13 /* Linux Specific				   */
 #define __NR_mknod               14 /* Common                                      */
 #define __NR_chmod               15 /* Common                                      */
 #define __NR_chown               16 /* Common                                      */
 #define __NR_brk                 17 /* Common                                      */
-/* #define __NR_ni_syscall       18    ENOSYS under SunOS                          */
+#define __NR_xmknod		 18 /* Linux Specific				   */
 #define __NR_lseek               19 /* Common                                      */
 #define __NR_getpid              20 /* Common                                      */
 /* #define __NR_ni_syscall       21    ENOSYS under SunOS                          */
 /* #define __NR_ni_syscall       22    ENOSYS under SunOS                          */
 #define __NR_setuid              23 /* Implemented via setreuid in SunOS           */
 #define __NR_getuid              24 /* Common                                      */
-/* #define __NR_ni_syscall       25    ENOSYS under SunOS                          */
+/* #define __NR_time alias	 25    ENOSYS under SunOS			   */
 #define __NR_ptrace              26 /* Common                                      */
 #define __NR_alarm               27 /* Implemented via setitimer in SunOS          */
 /* #define __NR_ni_syscall       28    ENOSYS under SunOS                          */
@@ -418,6 +418,7 @@
 static __inline__ _syscall0(int,sync)
 static __inline__ _syscall0(pid_t,setsid)
 static __inline__ _syscall3(int,write,int,fd,__const__ char *,buf,off_t,count)
+static __inline__ _syscall3(int,read,int,fd,char *,buf,off_t,count)
 static __inline__ _syscall1(int,dup,int,fd)
 static __inline__ _syscall3(int,execve,__const__ char *,file,char **,argv,char **,envp)
 static __inline__ _syscall3(int,open,__const__ char *,file,int,flag,int,mode)
@@ -443,24 +444,56 @@
 {
 	long retval;
 
-	__asm__ __volatile("mov %4, %%g2\n\t"    /* Set aside fn ptr... */
-			   "mov %5, %%g3\n\t"    /* and arg. */
-			   "mov %1, %%g1\n\t"
-			   "mov %2, %%o0\n\t"    /* Clone flags. */
-			   "mov 0, %%o1\n\t"     /* usp arg == 0 */
-			   "t 0x6d\n\t"          /* Linux/Sparc clone(). */
-			   "brz,a,pn %%o1, 1f\n\t"           /* The parent, just return. */
+	__asm__ __volatile("mov %1, %%g1\n\t"
+			   "mov %2, %%o0\n\t"	   /* Clone flags. */
+			   "mov 0, %%o1\n\t"	   /* usp arg == 0 */
+			   "t 0x6d\n\t"		   /* Linux/Sparc clone(). */
+			   "brz,a,pn %%o1, 1f\n\t" /* Parent, just return. */
 			   " mov %%o0, %0\n\t"
-			   "jmpl %%g2, %%o7\n\t" /* Call the function. */
-			   " mov %%g3, %%o0\n\t" /* Get back the arg in delay. */
+			   "jmpl %4, %%o7\n\t"	   /* Call the function. */
+			   " mov %5, %%o0\n\t"	   /* Set arg in delay. */
 			   "mov %3, %%g1\n\t"
-			   "t 0x6d\n\t"          /* Linux/Sparc exit(). */
+			   "t 0x6d\n\t"		   /* Linux/Sparc exit(). */
 			   /* Notreached by child. */
 			   "1:" :
 			   "=r" (retval) :
 			   "i" (__NR_clone), "r" (flags | CLONE_VM),
 			   "i" (__NR_exit),  "r" (fn), "r" (arg) :
-			   "g1", "g2", "g3", "o0", "o1", "memory", "cc");
+			   "g1", "o0", "o1", "memory", "cc");
+	return retval;
+}
+
+static __inline__ pid_t fork(void)
+{
+	long retval;
+
+	__asm__ __volatile("mov %1, %%g1\n\t"
+			   "t 0x6d\n\t"
+			   "brz,a,pn %%o1, 1f\n\t"
+			   " mov %%o0, %0\n\t"
+			   "mov %%g0, %0\n\t"
+			   "1:" :
+			   "=r" (retval) :
+			   "i" (__NR_fork) :
+			   "g1", "o0", "o1", "memory", "cc");
+	return retval;
+}
+
+static __inline__ pid_t clone(unsigned long flags, char *ksp)
+{
+	long retval;
+
+	__asm__ __volatile("mov %1, %%g1\n\t"
+			   "mov %2, %%o0\n\t"
+			   "mov %3, %%o1\n\t"
+			   "t 0x6d\n\t"
+			   "brz,a,pn %%o1, 1f\n\t"
+			   " mov %%o0, %0\n\t"
+			   "mov %%g0, %0\n\t"
+			   "1:" :
+			   "=r" (retval) :
+			   "i" (__NR_fork), "r" (flags), "r" (ksp) :
+			   "g1", "o0", "o1", "memory", "cc");
 	return retval;
 }
 
diff -u --recursive --new-file v2.1.96/linux/include/asm-sparc64/xstat.h linux/include/asm-sparc64/xstat.h
--- v2.1.96/linux/include/asm-sparc64/xstat.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-sparc64/xstat.h	Tue Apr 14 17:44:25 1998
@@ -0,0 +1,58 @@
+/* $Id: xstat.h,v 1.1 1998/02/06 12:52:18 jj Exp $
+ * xstat.h: sys_xstat/xmknod architecture dependent stuff.
+ *
+ * Copyright (C) 1998  Jakub Jelinek  (jj@sunsite.mff.cuni.cz)
+ */
+ 
+extern __inline__ int cp_xstat32(struct inode *inode, struct stat64_32 *s, unsigned long blocks, int blksize)
+{
+	struct stat64_32 tmp;
+	
+	memset (&tmp, 0, sizeof(tmp));
+	tmp.st_dev = (((__u64)MAJOR(inode->i_dev)) << 32) | MINOR(inode->i_dev);
+	tmp.st_ino = inode->i_ino;
+	tmp.st_mode = inode->i_mode;
+	tmp.st_nlink = inode->i_nlink;
+	tmp.st_uid = inode->i_uid;
+	tmp.st_gid = inode->i_gid;
+	tmp.st_rdev = (((__u64)MAJOR(inode->i_rdev)) << 32) | MINOR(inode->i_rdev);
+	tmp.st_size = inode->i_size;
+	tmp.st_blocks = blocks;
+	tmp.st_atime = inode->i_atime;
+	tmp.st_mtime = inode->i_mtime;
+	tmp.st_ctime = inode->i_ctime;
+	tmp.st_blksize = blksize;
+	return copy_to_user(s,&tmp,sizeof(tmp));
+}
+
+extern __inline__ int cp_xstat(struct inode *inode, struct stat64 *s, unsigned long blocks, int blksize)
+{
+	struct stat64 tmp;
+	if (current->tss.flags & SPARC_FLAG_32BIT)
+		return cp_xstat32(inode, (struct stat64_32 *)s, blocks, blksize);
+	memset (&tmp, 0, sizeof(tmp));
+	tmp.st_dev = (((__u64)MAJOR(inode->i_dev)) << 32) | MINOR(inode->i_dev);
+	tmp.st_ino = inode->i_ino;
+	tmp.st_mode = inode->i_mode;
+	tmp.st_nlink = inode->i_nlink;
+	tmp.st_uid = inode->i_uid;
+	tmp.st_gid = inode->i_gid;
+	tmp.st_rdev = (((__u64)MAJOR(inode->i_rdev)) << 32) | MINOR(inode->i_rdev);
+	tmp.st_size = inode->i_size;
+	tmp.st_atim.tv_sec = inode->i_atime;
+	tmp.st_mtim.tv_sec = inode->i_mtime;
+	tmp.st_ctim.tv_sec = inode->i_ctime;
+	tmp.st_blksize = blksize;
+	tmp.st_blocks = blocks;
+	/* Should I check if all fs names are < 16? All in the kernel tree are */
+	if (inode->i_sb)
+		strcpy(tmp.st_fstype, inode->i_sb->s_type->name);
+	return copy_to_user(s,&tmp,sizeof(tmp));
+}
+
+extern __inline__ int get_user_new_dev_t(kdev_t *kdev, __new_dev_t *udev) {
+	__new_dev_t ndev;
+	if (copy_from_user (&ndev, udev, sizeof(__new_dev_t))) return -EFAULT;
+	*kdev = MKDEV((ndev >> 32), (__u32)ndev);
+	return 0;
+}
diff -u --recursive --new-file v2.1.96/linux/include/linux/parport.h linux/include/linux/parport.h
--- v2.1.96/linux/include/linux/parport.h	Tue Apr 14 14:29:26 1998
+++ linux/include/linux/parport.h	Wed Apr 15 14:38:36 1998
@@ -283,6 +283,7 @@
 	if ((dev->port->waithead == NULL) || (timeslip < dev->timeslice))
 		return 0;
 	parport_release(dev);
+	schedule();
 	return parport_claim_or_block(dev);
 }
 
@@ -292,7 +293,7 @@
 
 #define PARPORT_FLAG_COMA		1
 
-extern void parport_parse_irqs(int, const char *, int irqval[]);
+extern void parport_parse_irqs(int, const char *[], int irqval[]);
 extern int parport_ieee1284_nibble_mode_ok(struct parport *, unsigned char);
 extern int parport_wait_peripheral(struct parport *, unsigned char, unsigned
 				   char);
diff -u --recursive --new-file v2.1.96/linux/include/linux/pci.h linux/include/linux/pci.h
--- v2.1.96/linux/include/linux/pci.h	Tue Apr 14 14:29:26 1998
+++ linux/include/linux/pci.h	Fri Apr 17 21:58:48 1998
@@ -1,5 +1,5 @@
 /*
- *	$Id: pci.h,v 1.62 1998/03/15 13:50:05 ecd Exp $
+ *	$Id: pci.h,v 1.67 1998/04/16 20:48:33 mj Exp $
  *
  *	PCI defines and function prototypes
  *	Copyright 1994, Drew Eckhardt
@@ -105,10 +105,10 @@
 #define PCI_CARDBUS_CIS		0x28
 #define PCI_SUBSYSTEM_VENDOR_ID	0x2c
 #define PCI_SUBSYSTEM_ID	0x2e  
-#define PCI_ROM_ADDRESS		0x30	/* 32 bits */
-#define  PCI_ROM_ADDRESS_ENABLE	0x01	/* Write 1 to enable ROM,
-					   bits 31..11 are address,
-					   10..2 are reserved */
+#define PCI_ROM_ADDRESS		0x30	/* Bits 31..11 are address, 10..1 reserved */
+#define  PCI_ROM_ADDRESS_ENABLE	0x01
+#define PCI_ROM_ADDRESS_MASK	(~0x7ffUL)
+
 /* 0x34-0x3b are reserved */
 #define PCI_INTERRUPT_LINE	0x3c	/* 8 bits */
 #define PCI_INTERRUPT_PIN	0x3d	/* 8 bits */
@@ -178,8 +178,8 @@
 #define PCI_CB_IO_LIMIT_1_HI	0x3a
 /* 0x3c-0x3d are same as for htype 0 */
 /* 0x3e-0x3f are same as for htype 1 */
-#define PCI_CB_SUBSYSTEM_ID	0x40
-#define PCI_CB_SUBSYSTEM_VENDOR_ID 0x42
+#define PCI_CB_SUBSYSTEM_VENDOR_ID 0x40
+#define PCI_CB_SUBSYSTEM_ID	0x42
 #define PCI_CB_LEGACY_MODE_BASE	0x44	/* 16-bit PC Card legacy mode base address (ExCa) */
 /* 0x48-0x7f reserved */
 
@@ -979,8 +979,7 @@
 #ifdef __KERNEL__
 
 /*
- * Error values that may be returned by the PCI bios.  Use
- * pcibios_strerror() to convert to a printable string.
+ * Error values that may be returned by the PCI bios.
  */
 #define PCIBIOS_SUCCESSFUL		0x00
 #define PCIBIOS_FUNC_NOT_SUPPORTED	0x81
@@ -1008,7 +1007,6 @@
 			       unsigned char where, unsigned short val);
 int pcibios_write_config_dword (unsigned char bus, unsigned char dev_fn,
 				unsigned char where, unsigned int val);
-const char *pcibios_strerror (int error);
 
 /* Don't use these in new code, use pci_find_... instead */
 
@@ -1051,6 +1049,7 @@
 	 * pcibios_fixup() as necessary.
 	 */
 	unsigned long	base_address[6];
+	unsigned long	rom_address;
 };
 
 struct pci_bus {
@@ -1076,8 +1075,9 @@
 void pci_setup(char *str, int *ints);
 void pci_quirks_init(void);
 unsigned int pci_scan_bus(struct pci_bus *bus);
-void proc_bus_pci_init(void);
+void pci_proc_init(void);
 void proc_old_pci_init(void);
+int get_pci_list(char *buf);
 
 struct pci_dev *pci_find_device (unsigned int vendor, unsigned int device, struct pci_dev *from);
 struct pci_dev *pci_find_class (unsigned int class, struct pci_dev *from);
@@ -1090,8 +1090,7 @@
 #define pci_write_config_byte(dev, where, val) pcibios_write_config_byte(dev->bus->number, dev->devfn, where, val)
 #define pci_write_config_word(dev, where, val) pcibios_write_config_word(dev->bus->number, dev->devfn, where, val)
 #define pci_write_config_dword(dev, where, val) pcibios_write_config_dword(dev->bus->number, dev->devfn, where, val)
-
-int get_pci_list (char *buf);
+void pci_set_master(struct pci_dev *);
 
 #endif /* __KERNEL__ */
 #endif /* LINUX_PCI_H */
diff -u --recursive --new-file v2.1.96/linux/include/linux/proc_fs.h linux/include/linux/proc_fs.h
--- v2.1.96/linux/include/linux/proc_fs.h	Fri Apr 10 13:03:49 1998
+++ linux/include/linux/proc_fs.h	Tue Apr 14 17:44:25 1998
@@ -182,6 +182,7 @@
 	PROC_SCSI_IDESCSI,
 	PROC_SCSI_MESH,
 	PROC_SCSI_53C94,
+	PROC_SCSI_PLUTO,
 	PROC_SCSI_SCSI_DEBUG,	
 	PROC_SCSI_NOT_PRESENT,
 	PROC_SCSI_FILE,                        /* I'm assuming here that we */
@@ -350,7 +351,7 @@
  	char name[32];
 };
 extern struct inode_operations *
-proc_openprom_register(int (*readdir)(struct inode *, struct file *, void *, filldir_t),
+proc_openprom_register(int (*readdir)(struct file *, void *, filldir_t),
 		       int (*lookup)(struct inode *, struct dentry *),
 		       void (*use)(struct inode *, int),
 		       struct openpromfs_dev ***);
diff -u --recursive --new-file v2.1.96/linux/include/linux/reboot.h linux/include/linux/reboot.h
--- v2.1.96/linux/include/linux/reboot.h	Mon Jan 12 15:31:13 1998
+++ linux/include/linux/reboot.h	Fri Apr 17 22:01:51 1998
@@ -8,6 +8,7 @@
 #define	LINUX_REBOOT_MAGIC1	0xfee1dead
 #define	LINUX_REBOOT_MAGIC2	672274793
 #define	LINUX_REBOOT_MAGIC2A	85072278
+#define	LINUX_REBOOT_MAGIC2B	369367448
 
 
 /*
diff -u --recursive --new-file v2.1.96/linux/include/linux/serial.h linux/include/linux/serial.h
--- v2.1.96/linux/include/linux/serial.h	Mon Nov 24 08:45:45 1997
+++ linux/include/linux/serial.h	Fri Apr 17 22:04:44 1998
@@ -85,8 +85,10 @@
 #define ASYNC_SPD_SHI	0x1000	/* Use 230400 instead of 38400 bps */
 #define ASYNC_SPD_WARP	0x1010	/* Use 460800 instead of 38400 bps */
 
-#define ASYNC_FLAGS	0x1FFF	/* Possible legal async flags */
-#define ASYNC_USR_MASK 0x1430	/* Legal flags that non-privileged
+#define ASYNC_LOW_LATENCY 0x2000 /* Request low latency behaviour */
+
+#define ASYNC_FLAGS	0x2FFF	/* Possible legal async flags */
+#define ASYNC_USR_MASK	0x3430	/* Legal flags that non-privileged
 				 * users can set or reset */
 
 /* Internal flags used only by kernel/chr_drv/serial.c */
diff -u --recursive --new-file v2.1.96/linux/kernel/sys.c linux/kernel/sys.c
--- v2.1.96/linux/kernel/sys.c	Tue Mar 17 22:18:15 1998
+++ linux/kernel/sys.c	Fri Apr 17 22:01:51 1998
@@ -177,7 +177,8 @@
 
 	/* For safety, we require "magic" arguments. */
 	if (magic1 != LINUX_REBOOT_MAGIC1 ||
-	    (magic2 != LINUX_REBOOT_MAGIC2 && magic2 != LINUX_REBOOT_MAGIC2A))
+	    (magic2 != LINUX_REBOOT_MAGIC2 && magic2 != LINUX_REBOOT_MAGIC2A &&
+			magic2 != LINUX_REBOOT_MAGIC2B))
 		return -EINVAL;
 
 	lock_kernel();
diff -u --recursive --new-file v2.1.96/linux/net/ipv4/ip_fragment.c linux/net/ipv4/ip_fragment.c
--- v2.1.96/linux/net/ipv4/ip_fragment.c	Thu Mar 26 15:57:13 1998
+++ linux/net/ipv4/ip_fragment.c	Fri Apr 17 20:30:13 1998
@@ -5,7 +5,7 @@
  *
  *		The IP fragmentation functionality.
  *		
- * Version:	$Id: ip_fragment.c,v 1.33 1998/03/19 08:34:08 davem Exp $
+ * Version:	$Id: ip_fragment.c,v 1.36 1998/04/18 02:13:07 davem Exp $
  *
  * Authors:	Fred N. van Kempen <waltje@uWalt.NL.Mugnet.ORG>
  *		Alan Cox <Alan.Cox@linux.org>
@@ -346,10 +346,9 @@
 	memcpy(ptr, qp->iph, qp->ihlen);
 	ptr += qp->ihlen;
 
-	count = 0;
-
 	/* Copy the data portions of all fragments into the new buffer. */
 	fp = qp->fragments;
+	count = qp->ihlen;
 	while(fp) {
 		if (fp->len < 0 || count+fp->len > skb->len) {
 			NETDEBUG(printk(KERN_ERR "Invalid fragment list: "
@@ -360,7 +359,7 @@
 			return NULL;
 		}
 		memcpy((ptr + fp->offset), fp->ptr, fp->len);
-		if (!count) {
+		if (count == qp->ihlen) {
 			skb->dst = dst_clone(fp->skb->dst);
 			skb->dev = fp->skb->dev;
 		}
@@ -376,7 +375,7 @@
 	/* Done with all fragments. Fixup the new IP header. */
 	iph = skb->nh.iph;
 	iph->frag_off = 0;
-	iph->tot_len = htons((iph->ihl * 4) + count);
+	iph->tot_len = htons(count);
 
 	ip_statistics.IpReasmOKs++;
 	return skb;
