diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/CREDITS linux/CREDITS
--- linux.orig/CREDITS	Mon Jan  7 23:41:08 2002
+++ linux/CREDITS	Wed Dec 26 15:09:46 2001
@@ -525,6 +525,16 @@
 S: Bellevue, Washington 98007
 S: USA
 
+N: Christopher L. Cheney
+E: ccheney@debian.org
+E: ccheney@cheney.cx
+W: http://www.cheney.cx
+P: 1024D/8E384AF2 2D31 1927 87D7 1F24 9FF9  1BC5 D106 5AB3 8E38 4AF2
+D: Vista Imaging usb webcam driver
+S: 314 Prince of Wales
+S: Conroe, TX 77304
+S: USA
+
 N: Stuart Cheshire
 E: cheshire@cs.stanford.edu
 D: Author of Starmode Radio IP (STRIP) driver
@@ -1264,6 +1274,13 @@
 W: http://www.carumba.com/
 D: bug toaster (A1 sauce makes all the difference)
 D: Random linux hacker
+
+N: Tim Hockin
+E: thockin@hockin.org
+W: http://www.hockin.org/~thockin
+D: Natsemi ethernet
+D: Cobalt Networks (x86) support
+D: This-and-That
 
 N: Dirk Hohndel
 E: hohndel@suse.de
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/Documentation/Configure.help linux/Documentation/Configure.help
--- linux.orig/Documentation/Configure.help	Mon Jan  7 23:41:08 2002
+++ linux/Documentation/Configure.help	Wed Dec 26 15:09:46 2001
@@ -12908,6 +12908,17 @@
   The module will be called visor.o. If you want to compile it as a
   module, say M here and read <file:Documentation/modules.txt>.
 
+USB Compaq iPAQ Driver
+CONFIG_USB_SERIAL_IPAQ
+  Say Y here if you want to connect to your Compaq iPAQ running
+  Windows CE 3.0 using a USB autosync cable. For information on using
+  the driver, read <file:Documentation/usb/usb-serial.txt>.
+
+  This code is also available as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want).
+  The module will be called ipaq.o. If you want to compile it as a
+  module, say M here and read <file:Documentation/modules.txt>.
+
 USB IR Dongle Serial Driver
 CONFIG_USB_SERIAL_IR
   Say Y here if you want to enable simple serial support for USB IrDA
@@ -13117,6 +13128,22 @@
   The module will be called io_edgeport.o.  If you want to compile it
   as a module, say M here and read <file:Documentation/modules.txt>.
 
+USB PalmConnect (and other KL5KUSB105-based) Single Port Serial Driver
+CONFIG_USB_SERIAL_KLSI
+  Say Y here if you want to use a KL5KUSB105 - based single port
+  serial adapter. The most widely known -- and currently the only
+  tested -- device in this category is the PalmConnect USB Serial
+  adapter sold by Palm Inc. for use with their Palm III and Palm V
+  series PDAs.
+
+  Please read <file:Documentation/usb/usb-serial.txt> for more
+  information.
+
+  This code is also available as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want).
+  The module will be called kl5kusb105.o. If you want to compile it as
+  a module, say M here and read <file:Documentation/modules.txt>.
+
 USB Serial Converter verbose debug
 CONFIG_USB_SERIAL_DEBUG
   Say Y here if you want verbose debug messages from the USB Serial
@@ -13223,6 +13250,54 @@
   The module will be called pwc.o.  If you want to compile it as a
   module, say M here and read <file:Documentation/modules.txt>.
 
+USB SE401 Camera support
+CONFIG_USB_SE401
+  Say Y here if you want to connect this type of camera to your
+  computer's USB port. See <file:Documentation/usb/se401.txt> for more
+  information and for a list of supported cameras.
+
+  This driver uses the Video For Linux API. You must say Y or M to
+  "Video For Linux" (under Multimedia Devices) to use this driver.
+  Information on this API and pointers to "v4l" programs may be found
+  on the WWW at <http://roadrunner.swansea.uk.linux.org/v4l.shtml>.
+
+  This code is also available as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want).
+  The module will be called se401.o. If you want to compile it as a
+  module, say M here and read <file:Documentation/modules.txt>.
+
+USB STV680 (Pencam) Camera support
+CONFIG_USB_STV680
+  Say Y here if you want to connect this type of camera to your
+  computer's USB port. This includes the Pencam line of cameras.
+  See <file:Documentation/usb/stv680.txt> for more information and for
+  a list of supported cameras.
+
+  This driver uses the Video For Linux API. You must say Y or M to
+  "Video For Linux" (under Multimedia Devices) to use this driver.
+  Information on this API and pointers to "v4l" programs may be found
+  on the WWW at <http://roadrunner.swansea.uk.linux.org/v4l.shtml>.
+
+  This code is also available as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want).
+  The module will be called stv680.o. If you want to compile it as a
+  module, say M here and read <file:Documentation/modules.txt>.
+
+Vicam
+CONFIG_USB_VICAM
+  Say Y here if you have 3com homeconnect camera (vicam).
+
+  This driver uses the Video For Linux API. You must say Y or M to
+  "Video For Linux" (under Multimedia Devices) to use this driver.
+  Information on this API and pointers to "v4l" programs may be found
+  on the WWW at <http://roadrunner.swansea.uk.linux.org/v4l.shtml>.
+
+  This code is also available as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want).
+  The module will be called vicam.o. If you want to compile it as a
+  module, say M here and read <file:Documentation/modules.txt>.
+
+
 Pegasus/Pegasus II based USB-Ethernet device support
 CONFIG_USB_PEGASUS
   Say Y here if you know you have Pegasus or Pegasus II based adapter.
@@ -17025,22 +17100,6 @@
   will issue the hlt instruction if nothing is to be done, thereby
   sending the processor to sleep and saving power.
 
-USB SE401 Camera support
-CONFIG_USB_SE401
-  Say Y here if you want to connect this type of camera to your
-  computer's USB port. See <file:Documentation/usb/se401.txt> for more
-  information and for a list of supported cameras.
-
-  This driver uses the Video For Linux API. You must say Y or M to
-  "Video For Linux" (under Multimedia Devices) to use this driver.
-  Information on this API and pointers to "v4l" programs may be found
-  on the WWW at <http://roadrunner.swansea.uk.linux.org/v4l.shtml>.
-
-  This code is also available as a module ( = code which can be
-  inserted in and removed from the running kernel whenever you want).
-  The module will be called se401.o. If you want to compile it as a
-  module, say M here and read <file:Documentation/modules.txt>.
-
 ACPI support
 CONFIG_ACPI
   ACPI/OSPM support for Linux is currently under development. As such,
@@ -21615,23 +21674,17 @@
   If you want to use the produced kernel to IPL directly from a
   device, you have to merge a bootsector specific to the device
   into the first bytes of the kernel. You will have to select the
-  IPL device.
+  IPL device on another question, that pops up, when you select
+  CONFIG_IPL.
 
-# Choice: s390_ipl
-IPL from a S/390 tape unit
+IPL from a /390 tape unit
 CONFIG_IPL_TAPE
   Select this option if you want to IPL the image from a Tape.
 
 IPL from a virtual card reader emulated by VM/ESA
-CONFIG_IPL_RDR_VM
-  Select this option if you are running under VM/ESA and want
+CONFIG_IPL_VM
+  Select this option if you are running under VM/ESA and want 
   to IPL the image from the emulated card reader.
-
-IPL from a real card reader
-CONFIG_IPL_RDR
-  Select this option if you want to IPL the image from a real
-  card reader. Maybe you still got one and want to try. We didn't
-  test.
 
 Support for DASD hard disks
 CONFIG_DASD
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/Documentation/DMA-mapping.txt linux/Documentation/DMA-mapping.txt
--- linux.orig/Documentation/DMA-mapping.txt	Mon Jan  7 23:41:08 2002
+++ linux/Documentation/DMA-mapping.txt	Sun Dec 23 16:14:57 2001
@@ -60,9 +60,6 @@
 might be mapped somewhere entirely different than the rest of physical
 memory.
 
-Also, this means that you cannot take the return of a kmap()
-call and DMA to/from that.  This is similar to vmalloc().
-
 What about block I/O and networking buffers?  The block I/O and
 networking subsystems make sure that the buffers they use are valid
 for you to DMA from/to.
@@ -688,6 +685,97 @@
 
 This is possible with the DAC interfaces purely because they are
 not translated in any way.
+
+		Optimizing Unmap State Space Consumption
+
+On many platforms, pci_unmap_{single,page}() is simply a nop.
+Therefore, keeping track of the mapping address and length is a waste
+of space.  Instead of filling your drivers up with ifdefs and the like
+to "work around" this (which would defeat the whole purpose of a
+portable API) the following facilities are provided.
+
+Actually, instead of describing the macros one by one, we'll
+transform some example code.
+
+1) Use DECLARE_PCI_UNMAP_{ADDR,LEN} in state saving structures.
+   Example, before:
+
+	struct ring_state {
+		struct sk_buff *skb;
+		dma_addr_t mapping;
+		__u32 len;
+	};
+
+   after:
+
+	struct ring_state {
+		struct sk_buff *skb;
+		DECLARE_PCI_UNMAP_ADDR(mapping)
+		DECLARE_PCI_UNMAP_LEN(len)
+	};
+
+   NOTE: DO NOT put a semicolon at the end of the DECLARE_*()
+         macro.
+
+2) Use PCI_UNMAP_{ADDR,LEN}_SET to set these values.
+   Example, before:
+
+	ringp->mapping = FOO;
+	ringp->len = BAR;
+
+   after:
+
+	PCI_UNMAP_ADDR_SET(ringp, mapping, FOO);
+	PCI_UNMAP_LEN_SET(ringp, len, BAR);
+
+3) Use PCI_UNMAP_{ADDR,LEN} to access these values.
+   Example, before:
+
+	pci_unmap_single(pdev, ringp->mapping, ringp->len,
+			 PCI_DMA_FROMDEVICE);
+
+   after:
+
+	pci_unmap_single(pdev,
+			 PCI_UNMAP_ADDR(ringp, mapping),
+			 PCI_UNMAP_LEN(ringp, len),
+			 PCI_DMA_FROMDEVICE);
+
+It really should be self-explanatory.  We treat the ADDR and LEN
+seperately, because it is possible for an implementation to only
+need the address in order to perform the unmap operation.
+
+			Platform Issues
+
+If you are just writing drivers for Linux and do not maintain
+an architecture port for the kernel, you can safely skip down
+to "Closing".
+
+1) Struct scatterlist requirements.
+
+   Struct scatterlist must contain, at a minimum, the following
+   members:
+
+	char *address;
+	struct page *page;
+	unsigned int offset;
+	unsigned int length;
+
+   The "address" member will disappear in 2.5.x
+
+   This means that your pci_{map,unmap}_sg() and all other
+   interfaces dealing with scatterlists must be able to cope
+   properly with page being non NULL.
+
+   A scatterlist is in one of two states.  The base address is
+   either specified by "address" or by a "page+offset" pair.
+   If "address" is NULL, then "page+offset" is being used.
+   If "page" is NULL, then "address" is being used.
+
+   In 2.5.x, all scatterlists will use "page+offset".  But during
+   2.4.x we still have to support the old method.
+
+2) More to come...
 
 			   Closing
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/Documentation/DocBook/kernel-hacking.tmpl linux/Documentation/DocBook/kernel-hacking.tmpl
--- linux.orig/Documentation/DocBook/kernel-hacking.tmpl	Mon Jan  7 23:41:08 2002
+++ linux/Documentation/DocBook/kernel-hacking.tmpl	Fri Dec 21 19:45:49 2001
@@ -18,8 +18,8 @@
   </authorgroup>
 
   <copyright>
-   <year>2000</year>
-   <holder>Paul Russell</holder>
+   <year>2001</year>
+   <holder>Rusty Russell</holder>
   </copyright>
 
   <legalnotice>
@@ -651,6 +651,29 @@
    </para> 
   </sect1>
  
+  <sect1 id="routines-endian">
+   <title><function>cpu_to_be32()</function>/<function>be32_to_cpu()</function>/<function>cpu_to_le32()</function>/<function>le32_to_cpu()</function>
+     <filename class=headerfile>include/asm/byteorder.h</filename> 
+   </title>
+
+   <para>
+    The <function>cpu_to_be32()</function> family (where the "32" can
+    be replaced by 64 or 16, and the "be" can be replaced by "le") are
+    the general way to do endian conversions in the kernel: they
+    return the converted value.  All variations supply the reverse as
+    well: <function>be32_to_cpu()</function>, etc.
+   </para>
+
+   <para>
+    There are two major variations of these functions: the pointer
+    variation, such as <function>cpu_to_be32p()</function>, which take
+    a pointer to the given type, and return the converted value.  The
+    other variation is the "in-situ" family, such as
+    <function>cpu_to_be32s()</function>, which convert value referred
+    to by the pointer, and return void.
+   </para> 
+  </sect1>
+
   <sect1 id="routines-local-irqs">
    <title><function>local_irq_save()</function>/<function>local_irq_restore()</function>
     <filename class=headerfile>include/asm/system.h</filename>
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/Documentation/i386/boot.txt linux/Documentation/i386/boot.txt
--- linux.orig/Documentation/i386/boot.txt	Mon Jan  7 23:41:08 2002
+++ linux/Documentation/i386/boot.txt	Wed Dec 26 17:05:14 2001
@@ -2,7 +2,7 @@
 		     ----------------------------
 
 		    H. Peter Anvin <hpa@zytor.com>
-			Last update 2000-10-29
+			Last update 2001-12-11
 
 On the i386 platform, the Linux kernel uses a rather complicated boot
 convention.  This has evolved partially due to historical aspects, as
@@ -31,6 +31,9 @@
 		BIOS entry points.  zImage deprecated but still
 		supported.
 
+Protocol 2.03:	(???) Explicitly makes the highest possible initrd address
+		available to the bootloader.
+
 
 **** MEMORY LAYOUT
 
@@ -45,7 +48,7 @@
 098000	+------------------------+	
 	|  Kernel setup		 |	The kernel real-mode code.
 090200	+------------------------+
-	|  Kernel boot sector    |	The kernel legacy boot sector.
+	|  Kernel boot sector	 |	The kernel legacy boot sector.
 090000	+------------------------+
 	|  Protected-mode kernel |	The bulk of the kernel image.
 010000	+------------------------+
@@ -123,6 +126,7 @@
 0224/2	2.01+	heap_end_ptr	Free memory after setup end
 0226/2	N/A	pad1		Unused
 0228/4	2.02+	cmd_line_ptr	32-bit pointer to the kernel command line
+022C/4	2.03+	initrd_addr_max	Highest legal initrd address
 
 For backwards compatibility, if the setup_sects field contains 0, the
 real value is 4.
@@ -181,8 +185,8 @@
 	The initrd should typically be located as high in memory as
 	possible, as it may otherwise get overwritten by the early
 	kernel initialization sequence.  However, it must never be
-	located above address 0x3C000000 if you want all kernels to
-	read it.
+	located above the address specified in the initrd_addr_max
+	field.  The initrd should be at least 4K page aligned.
 
   cmd_line_ptr:
 	If the protocol version is 2.02 or higher, this is a 32-bit
@@ -192,7 +196,15 @@
 	command line, in which case you can point this to an empty
 	string (or better yet, to the string "auto".)  If this field
 	is left at zero, the kernel will assume that your boot loader
-	does not support the 2.02 protocol.
+	does not support the 2.02+ protocol.
+
+  ramdisk_max:
+	The maximum address that may be occupied by the initrd
+	contents.  For boot protocols 2.02 or earlier, this field is
+	not present, and the maximum address is 0x37FFFFFF.  (This
+	address is defined as the address of the highest safe byte, so
+	if your ramdisk is exactly 131072 bytes long and this field is
+	0x37FFFFFF, you can start your ramdisk at 0x37FE0000.)
 
 
 **** THE KERNEL COMMAND LINE
@@ -254,14 +266,14 @@
 		if ( protocol >= 0x0202 ) {
 			cmd_line_ptr = base_ptr + 0x9000;
 		} else {
-			cmd_line_magic  = 0xA33F;
+			cmd_line_magic	= 0xA33F;
 			cmd_line_offset = 0x9000;
 			setup_move_size = 0x9100;
 		}
 	} else {
 		/* Very old kernel */
 
-		cmd_line_magic  = 0xA33F;
+		cmd_line_magic	= 0xA33F;
 		cmd_line_offset = 0x9000;
 
 		/* A very old kernel MUST have its real-mode code
@@ -411,4 +423,3 @@
 
 	After completing your hook, you should jump to the address
 	that was in this field before your boot loader overwrote it.
-
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/Documentation/networking/dl2k.txt linux/Documentation/networking/dl2k.txt
--- linux.orig/Documentation/networking/dl2k.txt	Mon Jan  7 23:41:08 2002
+++ linux/Documentation/networking/dl2k.txt	Fri Dec 21 18:30:14 2001
@@ -1,7 +1,7 @@
 
     D-Link DL2000-based Gigabit Ethernet Adapter Installation
     for Linux
-    Nov 12, 2001
+    Nov 21, 2001
 
 Contents
 ========
@@ -195,8 +195,8 @@
 				  2		10Mbps full duplex.
 				  3		100Mbps half duplex.
 				  4		100Mbps full duplex.
-				  5          	1000Mbps full duplex.
-				  6          	1000Mbps half duplex.
+				  5          	1000Mbps half duplex.
+				  6          	1000Mbps full duplex.
 
 				  By default, the NIC operates at autosense.
 				  Note that only 1000mbps_fd and 1000mbps_hd
@@ -212,11 +212,24 @@
 				  Jumbo frame usually improve the performance
 				  int gigabit.
 				  
-int_count			- Rx frame count each interrupt.
-int_timeout			- Rx DMA wait time for an interrupt. Proper 
-				  values of int_count and int_timeout bring 
-				  a conspicuous performance in the fast machine. 
-				  Ex. int_count=5 and int_timeout=750 
+rx_coalesce			- Rx frame count each interrupt.
+rx_timeout			- Rx DMA wait time for an interrupt. Proper 
+				  values of rx_coalesce and rx_timeout bring 
+				  a conspicuous performance in the fast machine.
+				  Ex. rx_coalesce=5 and rx_timeout=750 
+
+tx_coalesce			- Tx transmit count each TxComp interrupt.
+				  Setting value larger than 1 will improve 
+				  performance, but this is possible to lower 
+				  stability in slow UP machines. By default, 
+				  tx_coalesce=8. (dl2k)
+				  
+tx_flow				- Specifies the Tx flow control. If tx_flow=1, 
+				  the Tx flow control enable.
+				  
+rx_flow				- Specifies the Rx flow control. If rx_flow=1, 
+				  the Rx flow control enable.
+
 
 Configuration Script Sample
 ===========================
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/Documentation/s390/Debugging390.txt linux/Documentation/s390/Debugging390.txt
--- linux.orig/Documentation/s390/Debugging390.txt	Mon Jan  7 23:41:08 2002
+++ linux/Documentation/s390/Debugging390.txt	Fri Dec 21 16:25:30 2001
@@ -237,9 +237,10 @@
 
 
 On 390 our limitations & strengths make us slightly different.
-For backward compatibility we are only allowed use 31 bits (2GB)
-of our 32 bit addresses,however, we use entirely separate address 
-spaces for the user & kernel.
+For backward compatibility ( because of the psw address hi bit which
+indicates whether we are in 31 or 64 bit mode ) we are only allowed 
+use 31 bits (2GB) of our 32 bit addresses. However, 
+we use entirely separate address  spaces for the user & kernel.
 
 This means we can support 2GB of non Extended RAM on s/390, & more
 with the Extended memory managment swap device & 
@@ -2123,6 +2124,12 @@
 now do
 p/x (*(**$sp+56))&0x7fffffff
 & so on.
+
+Another good trick to look at addresses on the stack if you've somehow lost 
+the backchain is.
+x/500xa $sp
+This displays anything the name of any known functions above the stack pointer
+for 500 bytes.
 
 Disassembling instructions without debug info
 ---------------------------------------------
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/Documentation/sonypi.txt linux/Documentation/sonypi.txt
--- linux.orig/Documentation/sonypi.txt	Fri Dec 21 17:41:53 2001
+++ linux/Documentation/sonypi.txt	Mon Jan  7 13:03:04 2002
@@ -25,7 +25,8 @@
 can be downloaded at: <http://www.alcove-labs.org/en/software/sonypi/>
 
 This driver supports also some ioctl commands for setting the LCD screen
-brightness (some more commands may be added in the future).
+brightness and querying the batteries charge information (some more 
+commands may be added in the future).
 
 This driver can also be used to set the camera controls on Picturebook series
 (brightness, contrast etc), and is used by the video4linux driver for the 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/Documentation/usb/stv680.txt linux/Documentation/usb/stv680.txt
--- linux.orig/Documentation/usb/stv680.txt	Thu Jan  1 00:00:00 1970
+++ linux/Documentation/usb/stv680.txt	Wed Dec 26 15:32:52 2001
@@ -0,0 +1,55 @@
+Linux driver for STV0680 based USB cameras
+
+Copyright, 2001, Kevin Sisson
+
+
+INTRODUCTION:
+
+STMicroelectronics produces the STV0680B chip, which comes in two 
+types, -001 and -003. The -003 version allows the recording and downloading 
+of sound clips from the camera, and allows a flash attachment. Otherwise, 
+it uses the same commands as the -001 version. Both versions support a 
+variety of SDRAM sizes and sensors, allowing for a maximum of 26 VGA or 20 
+CIF pictures. The STV0680 supports either a serial or a usb interface, and 
+video is possible through the usb interface.
+
+The following cameras are known to work with this driver, although any 
+camera with Vendor/Product codes of 0553/0202 should work:
+
+Aiptek Pencam (various models)
+Nisis QuickPix 2
+Radio Shack 'Kid's digital camera' (#60-1207)
+At least one Trust Spycam model
+Several other European brand models
+
+WHAT YOU NEED:
+
+-	USB support
+-	VIDEO4LINUX support
+
+More information about USB support for linux can be found at:
+http://www.linux-usb.org
+
+
+MODULE OPTIONS:
+
+When the driver is compiled as a module, you can set a "swapRGB=1"
+option, if necessary, for those applications that require it 
+(such as xawtv). However, the driver should detect and set this 
+automatically, so this option should not normally be used.
+
+
+KNOWN PROBLEMS:
+
+The driver seems to work better with the usb-ohci than the usb-uhci host 
+controller driver. 
+
+HELP:
+
+The latest info on this driver can be found at:
+http://personal.clt.bellsouth.net/~kjsisson or at
+http://stv0680-usb.sourceforge.net
+
+Any questions to me can be send to:  kjsisson@bellsouth.net
+
+
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/Documentation/usb/usb-serial.txt linux/Documentation/usb/usb-serial.txt
--- linux.orig/Documentation/usb/usb-serial.txt	Mon Jan  7 23:41:08 2002
+++ linux/Documentation/usb/usb-serial.txt	Wed Dec 26 15:09:46 2001
@@ -95,6 +95,66 @@
   Kroah-Hartman at greg@kroah.com
 
 
+Compaq iPAQ driver
+
+  This driver can be used to connect to Compaq iPAQ PDAs running
+  Windows CE 3.0 using a USB autosync cable. It has been tested only on
+  the Compaq H3135. It should work with the H3600 and later models too.
+  It may work with other CE based handhelds as well.
+
+  The driver presents a serial interface (usually on /dev/ttyUSB0) over
+  which one may run ppp and establish a TCP/IP link to the iPAQ. Once this
+  is done, you can transfer files, backup, download email etc. The most
+  significant advantage of using USB is speed - you can get 73 to 113
+  kbytes/sec for download/upload to the iPAQ.
+
+  The driver works intermittently with the usb-uhci driver but quite
+  reliably with the uhci driver. Make sure you have the right driver
+  loaded - usb-uhci is often the default.
+
+  You must setup hotplug to invoke pppd as soon as the iPAQ is connected.
+  A ppp script like the one below may be used:
+
+	#!/bin/bash
+
+	MYIP=linux.box.ip
+	REMOTEIP=ipaq.ip
+	MYDNS=my.dns.server
+	killall -9 pppd
+	/usr/sbin/pppd /dev/ttyUSB0 \
+	connect "/usr/sbin/chat -v TIMEOUT 60 CLIENT 'CLIENTSERVER\c'" \
+	nocrtscts local debug passive $MYIP:$REMOTEIP ms-dns $MYDNS noauth \
+	proxyarp
+
+  You must also download and install asyncd from http://synce.sourceforge.net
+  This is required to emulate keep-alive packets which are exchanged by
+  ActiveSync and the iPAQ.
+
+  On connecting the cable, you should see the usual "Device Connected",
+  "User Authenticated" messages flash by on your iPAQ. Once connected,
+  you can use Win CE programs like ftpView, Pocket Outlook from the iPAQ
+  and other synce utilities from the Linux side. Remember to enable IP
+  forwarding. 
+
+  To use Pocket IE, follow the instructions given at
+  http://www.tekguru.co.uk/EM500/usbtonet.htm to achieve the same thing
+  on Win98. Omit the proxy server part; Linux is quite capable of forwarding
+  packets unlike Win98. Another modification is required at least for the
+  iPAQ - disable autosync by going to the Start/Settings/Connections menu
+  and unchecking the "Automatically synchronize ..." box. Go to
+  Start/Programs/Connections, connect the cable and select "usbdial" (or
+  whatever you named your new USB connection). You should finally wind
+  up with a "Connected to usbdial" window with status shown as connected.
+  Now start up PIE and browse away.
+
+  If it doesn't work for some reason, load both the usbserial and ipaq module
+  with the module parameter "debug" set to 1 and examine the system log.
+  You can also try soft-resetting your iPAQ before attempting a connection.
+
+  For any questions or problems with the driver, please contact Ganesh
+  Varadarajan <ganesh@veritas.com>
+
+
 Keyspan PDA Serial Adapter
 
   Single port DB-9 serial adapter, pushed as a PDA adapter for iMacs (mostly
@@ -301,6 +361,32 @@
   For any questions or problems with this driver, please contact Greg
   Kroah-Hartman at greg@kroah.com
   
+
+KL5KUSB105 chipset / PalmConnect USB single-port adapter
+  
+Current status:
+  The driver was put together by looking at the usb bus transactions
+  done by Palm's driver under Windows, so a lot of functionality is
+  still missing.  Notably, serial ioctls are sometimes faked or not yet
+  implemented.  Support for finding out about DSR and CTS line status is
+  however implemented (though not nicely), so your favorite autopilot(1)
+  and pilot-manager -daemon calls will work.  Baud rates up to 115200
+  are supported, but handshaking (software or hardware) is not, which is
+  why it is wise to cut down on the rate used is wise for large
+  transfers until this is settled.
+  
+Options supported:
+  If this driver is compiled as a module you can pass the following
+  options to it:
+  debug			- extra verbose debugging info
+  			  (default: 0; nonzero enables)
+  use_lowlatency	- use low_latency flag to speed up tty layer
+			  when reading from from the device.
+			  (default: 0; nonzero enables)
+
+  See http://www.uuhaus.de/linux/palmconnect.html for up-to-date
+  information on this driver.
+
 
 Generic Serial driver
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/MAINTAINERS linux/MAINTAINERS
--- linux.orig/MAINTAINERS	Mon Jan  7 23:41:08 2002
+++ linux/MAINTAINERS	Mon Jan  7 14:33:07 2002
@@ -1022,6 +1022,11 @@
 M:	andrewtv@usa.net
 S:	Maintained
 
+NATSEMI ETHERNET DRIVER (DP8381x)
+P: 	Tim Hockin
+M:	thockin@hockin.org
+S:	Maintained
+
 NCP FILESYSTEM
 P:	Petr Vandrovec
 M:	vandrove@vc.cvut.cz
@@ -1145,8 +1150,8 @@
 S:	Maintained
 
 OPL3-SA2, SA3, and SAx DRIVER
-P:	Scott Murray
-M:	scott@spiteful.org
+P:	Zwane Mwaikambo
+M:	zwane@commfireservices.com
 L:	linux-sound@vger.kernel.org
 S:	Maintained
 
@@ -1254,6 +1259,12 @@
 L:	linux-kernel@vger.kernel.org
 W:	http://www.alarsen.net/linux/qnx4fs/
 S:	Maintained
+
+RADEON FRAMEBUFFER DISPLAY DRIVER
+P:	Ani Joshi
+M:	ajoshi@shell.unixbox.com
+L:      linux-fbdev-devel@lists.sourceforge.net
+S:      Maintained
 
 RAGE128 FRAMEBUFFER DISPLAY DRIVER
 P:	Ani Joshi
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/Makefile linux/Makefile
--- linux.orig/Makefile	Mon Jan  7 23:41:08 2002
+++ linux/Makefile	Mon Jan  7 23:33:54 2002
@@ -1,7 +1,7 @@
 VERSION = 2
 PATCHLEVEL = 4
-SUBLEVEL = 17
-EXTRAVERSION =
+SUBLEVEL = 18
+EXTRAVERSION = pre2
 
 KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
 
@@ -204,7 +204,7 @@
 	drivers/scsi/aic7xxx/aicasm/aicasm_scan.c \
 	drivers/scsi/aic7xxx/aicasm/y.tab.h \
 	drivers/scsi/aic7xxx/aicasm/aicasm \
-	drivers/scsi/53c700-mem.c \
+	drivers/scsi/53c700_d.h \
 	net/khttpd/make_times_h \
 	net/khttpd/times.h \
 	submenu*
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/alpha/kernel/alpha_ksyms.c linux/arch/alpha/kernel/alpha_ksyms.c
--- linux.orig/arch/alpha/kernel/alpha_ksyms.c	Fri Dec 21 17:41:53 2001
+++ linux/arch/alpha/kernel/alpha_ksyms.c	Mon Jan  7 14:04:12 2002
@@ -109,6 +109,7 @@
 EXPORT_SYMBOL(strrchr);
 EXPORT_SYMBOL(memcmp);
 EXPORT_SYMBOL(memmove);
+EXPORT_SYMBOL(memscan);
 EXPORT_SYMBOL(__memcpy);
 EXPORT_SYMBOL(__memset);
 EXPORT_SYMBOL(__memsetw);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/alpha/kernel/pci-noop.c linux/arch/alpha/kernel/pci-noop.c
--- linux.orig/arch/alpha/kernel/pci-noop.c	Thu Sep 13 22:21:32 2001
+++ linux/arch/alpha/kernel/pci-noop.c	Mon Jan  7 14:18:34 2002
@@ -104,21 +104,23 @@
 }
 /* stubs for the routines in pci_iommu.c */
 void *
-pci_alloc_consistent(struct pci_dev *pdev, long size, dma_addr_t *dma_addrp)
+pci_alloc_consistent(struct pci_dev *pdev, size_t size, dma_addr_t *dma_addrp)
 {
+	return (void *)0;
 }
 void
-pci_free_consistent(struct pci_dev *pdev, long size, void *cpu_addr,
+pci_free_consistent(struct pci_dev *pdev, size_t size, void *cpu_addr,
 		    dma_addr_t dma_addr)
 {
 }
 dma_addr_t
-pci_map_single(struct pci_dev *pdev, void *cpu_addr, long size,
+pci_map_single(struct pci_dev *pdev, void *cpu_addr, size_t size,
 	       int direction)
 {
+	return (dma_addr_t)0;
 }
 void
-pci_unmap_single(struct pci_dev *pdev, dma_addr_t dma_addr, long size,
+pci_unmap_single(struct pci_dev *pdev, dma_addr_t dma_addr, size_t size,
 		 int direction)
 {
 }
@@ -126,6 +128,7 @@
 pci_map_sg(struct pci_dev *pdev, struct scatterlist *sg, int nents,
 	   int direction)
 {
+	return 0;
 }
 void
 pci_unmap_sg(struct pci_dev *pdev, struct scatterlist *sg, int nents,
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/alpha/kernel/pci_iommu.c linux/arch/alpha/kernel/pci_iommu.c
--- linux.orig/arch/alpha/kernel/pci_iommu.c	Mon Nov  5 17:47:41 2001
+++ linux/arch/alpha/kernel/pci_iommu.c	Mon Jan  7 14:18:34 2002
@@ -215,12 +215,20 @@
 	/* If the machine doesn't define a pci_tbi routine, we have to
 	   assume it doesn't support sg mapping.  */
 	if (! alpha_mv.mv_pci_tbi) {
-		printk(KERN_WARNING "pci_map_single failed: no hw sg\n");
-		return 0;
+		static int been_here = 0;
+		if (!been_here) {
+		    printk(KERN_WARNING "pci_map_single: no hw sg, using "
+			   "direct map when possible\n");
+		    been_here = 1;
+		}
+		if (paddr + size <= __direct_map_size)
+		    return (paddr + __direct_map_base);
+		else
+		    return 0;
 	}
 		
 	arena = hose->sg_pci;
-	if (!arena || arena->dma_base + arena->size > max_dma)
+	if (!arena || arena->dma_base + arena->size - 1 > max_dma)
 		arena = hose->sg_isa;
 
 	npages = calc_npages((paddr & ~PAGE_MASK) + size);
@@ -247,20 +255,27 @@
 dma_addr_t
 pci_map_single(struct pci_dev *pdev, void *cpu_addr, size_t size, int dir)
 {
+	int dac_allowed; 
+
 	if (dir == PCI_DMA_NONE)
 		BUG();
-	return pci_map_single_1(pdev, cpu_addr, size,
-				pdev ? (pdev->dma_mask >> 32) != 0 : 0);
+
+	dac_allowed = pdev ? pci_dac_dma_supported(pdev, pdev->dma_mask) : 0; 
+	return pci_map_single_1(pdev, cpu_addr, size, dac_allowed);
 }
 
 dma_addr_t
 pci_map_page(struct pci_dev *pdev, struct page *page, unsigned long offset,
 	     size_t size, int dir)
 {
+	int dac_allowed;
+
 	if (dir == PCI_DMA_NONE)
 		BUG();
-	return pci_map_single_1(pdev, (char *)page_address(page) + offset,
-			        size, pdev ? (pdev->dma_mask >> 32) != 0 : 0);
+
+	dac_allowed = pdev ? pci_dac_dma_supported(pdev, pdev->dma_mask) : 0; 
+	return pci_map_single_1(pdev, (char *)page_address(page) + offset, 
+				size, dac_allowed);
 }
 
 /* Unmap a single streaming mode DMA translation.  The DMA_ADDR and
@@ -558,7 +573,7 @@
 	if (direction == PCI_DMA_NONE)
 		BUG();
 
-	dac_allowed = ((pdev->dma_mask >> 32) != 0);
+	dac_allowed = pdev ? pci_dac_dma_supported(pdev, pdev->dma_mask) : 0;
 
 	/* Fast path single entry scatterlists.  */
 	if (nents == 1) {
@@ -580,7 +595,7 @@
 		hose = pdev ? pdev->sysdata : pci_isa_hose;
 		max_dma = pdev ? pdev->dma_mask : 0x00ffffff;
 		arena = hose->sg_pci;
-		if (!arena || arena->dma_base + arena->size > max_dma)
+		if (!arena || arena->dma_base + arena->size - 1 > max_dma)
 			arena = hose->sg_isa;
 	} else {
 		max_dma = -1;
@@ -643,7 +658,7 @@
 	hose = pdev ? pdev->sysdata : pci_isa_hose;
 	max_dma = pdev ? pdev->dma_mask : 0x00ffffff;
 	arena = hose->sg_pci;
-	if (!arena || arena->dma_base + arena->size > max_dma)
+	if (!arena || arena->dma_base + arena->size - 1 > max_dma)
 		arena = hose->sg_isa;
 
 	fbeg = -1, fend = 0;
@@ -710,11 +725,10 @@
 	struct pci_iommu_arena *arena;
 
 	/* If there exists a direct map, and the mask fits either
-	   MAX_DMA_ADDRESS defined such that GFP_DMA does something
-	   useful, or the total system memory as shifted by the
-	   map base.  */
+	   the entire direct mapped space or the total system memory as
+	   shifted by the map base */
 	if (__direct_map_size != 0
-	    && (__direct_map_base + MAX_DMA_ADDRESS-IDENT_ADDR-1 <= mask
+	    && (__direct_map_base + __direct_map_size - 1 <= mask
 		|| __direct_map_base + (max_low_pfn<<PAGE_SHIFT)-1 <= mask))
 		return 1;
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/alpha/lib/dec_and_lock.c linux/arch/alpha/lib/dec_and_lock.c
--- linux.orig/arch/alpha/lib/dec_and_lock.c	Fri Nov  9 21:39:57 2001
+++ linux/arch/alpha/lib/dec_and_lock.c	Mon Jan  7 14:18:34 2002
@@ -27,6 +27,7 @@
 	br	$atomic_dec_and_lock_1..ng	\n\
 	.subsection 2				\n\
 4:	br	1b				\n\
+	.previous				\n\
 	.end atomic_dec_and_lock");
 
 static int __attribute__((unused))
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/i386/boot/setup.S linux/arch/i386/boot/setup.S
--- linux.orig/arch/i386/boot/setup.S	Mon Jan  7 23:41:08 2002
+++ linux/arch/i386/boot/setup.S	Wed Dec 26 17:05:14 2001
@@ -50,7 +50,8 @@
 #include <linux/compile.h>
 #include <asm/boot.h>
 #include <asm/e820.h>
-
+#include <asm/page.h>
+	
 /* Signature words to ensure LILO loaded us right */
 #define SIG1	0xAA55
 #define SIG2	0x5A5A
@@ -79,7 +80,7 @@
 # This is the setup header, and it must start at %cs:2 (old 0x9020:2)
 
 		.ascii	"HdrS"		# header signature
-		.word	0x0202		# header version number (>= 0x0105)
+		.word	0x0203		# header version number (>= 0x0105)
 					# or else old loadlin-1.5 will fail)
 realmode_swtch:	.word	0, 0		# default_switch, SETUPSEG
 start_sys_seg:	.word	SYSSEG
@@ -152,6 +153,10 @@
 					# 0x90000 segment; the setup
 					# can be located anywhere in
 					# low memory 0x10000 or higher.
+
+ramdisk_max:	.long __MAXMEM-1	# (Header version 0x0203 or later)
+					# The highest safe address for
+					# the contents of an initrd
 
 trampoline:	call	start_of_setup
 		.space	1024
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/i386/kernel/apic.c linux/arch/i386/kernel/apic.c
--- linux.orig/arch/i386/kernel/apic.c	Fri Nov  9 22:12:55 2001
+++ linux/arch/i386/kernel/apic.c	Mon Jan  7 13:18:19 2002
@@ -56,6 +56,14 @@
 	maxlvt = get_maxlvt();
 
 	/*
+	 * Masking an LVT entry on a P6 can trigger a local APIC error
+	 * if the vector is zero. Mask LVTERR first to prevent this.
+	 */
+	if (maxlvt >= 3) {
+		v = ERROR_APIC_VECTOR; /* any non-zero vector will do */
+		apic_write_around(APIC_LVTERR, v | APIC_LVT_MASKED);
+	}
+	/*
 	 * Careful: we have to set masks only first to deassert
 	 * any level-triggered sources.
 	 */
@@ -65,10 +73,6 @@
 	apic_write_around(APIC_LVT0, v | APIC_LVT_MASKED);
 	v = apic_read(APIC_LVT1);
 	apic_write_around(APIC_LVT1, v | APIC_LVT_MASKED);
-	if (maxlvt >= 3) {
-		v = apic_read(APIC_LVTERR);
-		apic_write_around(APIC_LVTERR, v | APIC_LVT_MASKED);
-	}
 	if (maxlvt >= 4) {
 		v = apic_read(APIC_LVTPC);
 		apic_write_around(APIC_LVTPC, v | APIC_LVT_MASKED);
@@ -84,6 +88,8 @@
 		apic_write_around(APIC_LVTERR, APIC_LVT_MASKED);
 	if (maxlvt >= 4)
 		apic_write_around(APIC_LVTPC, APIC_LVT_MASKED);
+	apic_write(APIC_ESR, 0);
+	v = apic_read(APIC_ESR);
 }
 
 void __init connect_bsp_APIC(void)
@@ -480,6 +486,7 @@
 	l &= ~MSR_IA32_APICBASE_BASE;
 	l |= MSR_IA32_APICBASE_ENABLE | APIC_DEFAULT_PHYS_BASE;
 	wrmsr(MSR_IA32_APICBASE, l, h);
+	apic_write(APIC_LVTERR, ERROR_APIC_VECTOR | APIC_LVT_MASKED);
 	apic_write(APIC_ID, apic_pm_state.apic_id);
 	apic_write(APIC_DFR, apic_pm_state.apic_dfr);
 	apic_write(APIC_LDR, apic_pm_state.apic_ldr);
@@ -487,15 +494,15 @@
 	apic_write(APIC_SPIV, apic_pm_state.apic_spiv);
 	apic_write(APIC_LVT0, apic_pm_state.apic_lvt0);
 	apic_write(APIC_LVT1, apic_pm_state.apic_lvt1);
+	apic_write(APIC_LVTPC, apic_pm_state.apic_lvtpc);
+	apic_write(APIC_LVTT, apic_pm_state.apic_lvtt);
+	apic_write(APIC_TDCR, apic_pm_state.apic_tdcr);
+	apic_write(APIC_TMICT, apic_pm_state.apic_tmict);
 	apic_write(APIC_ESR, 0);
 	apic_read(APIC_ESR);
 	apic_write(APIC_LVTERR, apic_pm_state.apic_lvterr);
 	apic_write(APIC_ESR, 0);
 	apic_read(APIC_ESR);
-	apic_write(APIC_LVTPC, apic_pm_state.apic_lvtpc);
-	apic_write(APIC_LVTT, apic_pm_state.apic_lvtt);
-	apic_write(APIC_TDCR, apic_pm_state.apic_tdcr);
-	apic_write(APIC_TMICT, apic_pm_state.apic_tmict);
 	__restore_flags(flags);
 	if (apic_pm_state.perfctr_pmdev)
 		pm_send(apic_pm_state.perfctr_pmdev, PM_RESUME, data);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/i386/kernel/pci-pc.c linux/arch/i386/kernel/pci-pc.c
--- linux.orig/arch/i386/kernel/pci-pc.c	Mon Jan  7 23:41:08 2002
+++ linux/arch/i386/kernel/pci-pc.c	Wed Dec 26 15:30:16 2001
@@ -1109,22 +1109,29 @@
 }
 
 /*
- * Nobody seems to know what this does. Damn.
+ * Addresses issues with problems in the memory write queue timer in
+ * certain VIA Northbridges.  This bugfix is per VIA's specifications.
  *
- * But it does seem to fix some unspecified problem
- * with 'movntq' copies on Athlons.
- *
- * VIA 8363 chipset:
- *  - bit 7 at offset 0x55: Debug (RW)
+ * VIA 8363,8622,8361 Northbridges:
+ *  - bits  5, 6, 7 at offset 0x55 need to be turned off
+ * VIA 8367 (KT266x) Northbridges:
+ *  - bits  5, 6, 7 at offset 0x95 need to be turned off
  */
-static void __init pci_fixup_via_athlon_bug(struct pci_dev *d)
+static void __init pci_fixup_via_northbridge_bug(struct pci_dev *d)
 {
 	u8 v;
-	pci_read_config_byte(d, 0x55, &v);
-	if (v & 0x80) {
-		printk("Trying to stomp on Athlon bug...\n");
-		v &= 0x7f; /* clear bit 55.7 */
-		pci_write_config_byte(d, 0x55, v);
+	int where = 0x55;
+
+	if (d->device == PCI_DEVICE_ID_VIA_8367_0) {
+		where = 0x95; /* the memory write queue timer register is 
+				 different for the kt266x's: 0x95 not 0x55 */
+	}
+
+	pci_read_config_byte(d, where, &v);
+	if (v & 0xe0) {
+		printk("Trying to stomp on VIA Northbridge bug...\n");
+		v &= 0x1f; /* clear bits 5, 6, 7 */
+		pci_write_config_byte(d, where, v);
 	}
 }
 
@@ -1137,7 +1144,10 @@
 	{ PCI_FIXUP_HEADER,	PCI_VENDOR_ID_SI,	PCI_DEVICE_ID_SI_5597,		pci_fixup_latency },
 	{ PCI_FIXUP_HEADER,	PCI_VENDOR_ID_SI,	PCI_DEVICE_ID_SI_5598,		pci_fixup_latency },
  	{ PCI_FIXUP_HEADER,	PCI_VENDOR_ID_INTEL,	PCI_DEVICE_ID_INTEL_82371AB_3,	pci_fixup_piix4_acpi },
-	{ PCI_FIXUP_HEADER,	PCI_VENDOR_ID_VIA,	PCI_DEVICE_ID_VIA_8363_0,	pci_fixup_via_athlon_bug },
+	{ PCI_FIXUP_HEADER,	PCI_VENDOR_ID_VIA,	PCI_DEVICE_ID_VIA_8363_0,	pci_fixup_via_northbridge_bug },
+	{ PCI_FIXUP_HEADER,	PCI_VENDOR_ID_VIA,	PCI_DEVICE_ID_VIA_8622,	        pci_fixup_via_northbridge_bug },
+	{ PCI_FIXUP_HEADER,	PCI_VENDOR_ID_VIA,	PCI_DEVICE_ID_VIA_8361,	        pci_fixup_via_northbridge_bug },
+	{ PCI_FIXUP_HEADER,	PCI_VENDOR_ID_VIA,	PCI_DEVICE_ID_VIA_8367_0,	pci_fixup_via_northbridge_bug },
 	{ 0 }
 };
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/i386/kernel/setup.c linux/arch/i386/kernel/setup.c
--- linux.orig/arch/i386/kernel/setup.c	Mon Jan  7 23:41:08 2002
+++ linux/arch/i386/kernel/setup.c	Wed Dec 26 17:05:14 2001
@@ -827,10 +827,8 @@
 #define PFN_PHYS(x)	((x) << PAGE_SHIFT)
 
 /*
- * 128MB for vmalloc and initrd
+ * Reserved space for vmalloc and iomap - defined in asm/page.h
  */
-#define VMALLOC_RESERVE	(unsigned long)(128 << 20)
-#define MAXMEM		(unsigned long)(-PAGE_OFFSET-VMALLOC_RESERVE)
 #define MAXMEM_PFN	PFN_DOWN(MAXMEM)
 #define MAX_NONPAE_PFN	(1 << 20)
 
@@ -2234,7 +2232,7 @@
 			 */
 #define NR_SIBLINGS	2
 			if (smp_num_siblings != NR_SIBLINGS) {
-				printk(KERN_WARNING "CPU: Unsuppored number of the siblings %d", smp_num_siblings);
+				printk(KERN_WARNING "CPU: Unsupported number of the siblings %d", smp_num_siblings);
 				smp_num_siblings = 1;
 				goto too_many_siblings;
 			}
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/i386/kernel/signal.c linux/arch/i386/kernel/signal.c
--- linux.orig/arch/i386/kernel/signal.c	Mon Jan  7 23:41:08 2002
+++ linux/arch/i386/kernel/signal.c	Fri Dec 21 19:35:58 2001
@@ -28,7 +28,7 @@
 
 #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
 
-asmlinkage int FASTCALL(do_signal(struct pt_regs *regs, sigset_t *oldset));
+int FASTCALL(do_signal(struct pt_regs *regs, sigset_t *oldset));
 
 int copy_siginfo_to_user(siginfo_t *to, siginfo_t *from)
 {
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/i386/kernel/vm86.c linux/arch/i386/kernel/vm86.c
--- linux.orig/arch/i386/kernel/vm86.c	Mon Jan  7 23:41:08 2002
+++ linux/arch/i386/kernel/vm86.c	Fri Dec 21 19:35:58 2001
@@ -62,7 +62,7 @@
         ( (unsigned)( & (((struct kernel_vm86_regs *)0)->VM86_REGS_PART2) ) )
 #define VM86_REGS_SIZE2 (sizeof(struct kernel_vm86_regs) - VM86_REGS_SIZE1)
 
-asmlinkage struct pt_regs * FASTCALL(save_v86_state(struct kernel_vm86_regs * regs));
+struct pt_regs * FASTCALL(save_v86_state(struct kernel_vm86_regs * regs));
 struct pt_regs * save_v86_state(struct kernel_vm86_regs * regs)
 {
 	struct tss_struct *tss;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/i386/mm/fault.c linux/arch/i386/mm/fault.c
--- linux.orig/arch/i386/mm/fault.c	Mon Jan  7 23:41:08 2002
+++ linux/arch/i386/mm/fault.c	Wed Dec 26 15:25:28 2001
@@ -27,8 +27,6 @@
 
 extern void die(const char *,struct pt_regs *,long);
 
-extern int console_loglevel;
-
 /*
  * Ugly, ugly, but the goto's result in better assembly..
  */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/parisc/kernel/traps.c linux/arch/parisc/kernel/traps.c
--- linux.orig/arch/parisc/kernel/traps.c	Mon Jan  7 23:41:08 2002
+++ linux/arch/parisc/kernel/traps.c	Wed Dec 26 15:25:28 2001
@@ -43,7 +43,6 @@
 
 static inline void console_verbose(void)
 {
-	extern int console_loglevel;
 	console_loglevel = 15;
 }
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/8260_io/uart.c linux/arch/ppc/8260_io/uart.c
--- linux.orig/arch/ppc/8260_io/uart.c	Mon Jan  7 23:41:08 2002
+++ linux/arch/ppc/8260_io/uart.c	Wed Dec 26 16:28:34 2001
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.uart.c 1.6 05/17/01 18:14:20 cort
+ * BK Id: SCCS/s.uart.c 1.11 12/13/01 16:53:53 trini
  */
 /*
  *  UART driver for MPC8260 CPM SCC or SMC
@@ -2325,7 +2325,11 @@
 	__clear_user(&serial_driver,sizeof(struct tty_driver));
 	serial_driver.magic = TTY_DRIVER_MAGIC;
 	serial_driver.driver_name = "serial";
+#ifdef CONFIG_DEVFS_FS
+	serial_driver.name = "tts/%d";
+#else
 	serial_driver.name = "ttyS";
+#endif
 	serial_driver.major = TTY_MAJOR;
 	serial_driver.minor_start = 64;
 	serial_driver.num = NR_PORTS;
@@ -2363,7 +2367,11 @@
 	 * major number and the subtype code.
 	 */
 	callout_driver = serial_driver;
+#ifdef CONFIG_DEVFS_FS
+	callout_driver.name = "cua/%d";
+#else
 	callout_driver.name = "cua";
+#endif
 	callout_driver.major = TTYAUX_MAJOR;
 	callout_driver.subtype = SERIAL_TYPE_CALLOUT;
 	callout_driver.read_proc = 0;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/8xx_io/uart.c linux/arch/ppc/8xx_io/uart.c
--- linux.orig/arch/ppc/8xx_io/uart.c	Mon Jan  7 23:41:08 2002
+++ linux/arch/ppc/8xx_io/uart.c	Wed Dec 26 16:28:34 2001
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.uart.c 1.19 10/26/01 09:59:32 trini
+ * BK Id: SCCS/s.uart.c 1.21 12/13/01 16:53:53 trini
  */
 /*
  *  UART driver for MPC860 CPM SCC or SMC
@@ -2529,7 +2529,11 @@
 	__clear_user(&serial_driver,sizeof(struct tty_driver));
 	serial_driver.magic = TTY_DRIVER_MAGIC;
 	serial_driver.driver_name = "serial";
+#ifdef CONFIG_DEVFS_FS
+	serial_driver.name = "tts/%d";
+#else
 	serial_driver.name = "ttyS";
+#endif
 	serial_driver.major = TTY_MAJOR;
 	serial_driver.minor_start = 64;
 	serial_driver.num = NR_PORTS;
@@ -2567,7 +2571,11 @@
 	 * major number and the subtype code.
 	 */
 	callout_driver = serial_driver;
+#ifdef CONFIG_DEVFS_FS
+	callout_driver.name = "cua/%d";
+#else
 	callout_driver.name = "cua";
+#endif
 	callout_driver.major = TTYAUX_MAJOR;
 	callout_driver.subtype = SERIAL_TYPE_CALLOUT;
 	callout_driver.read_proc = 0;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/boot/common/misc-simple.c linux/arch/ppc/boot/common/misc-simple.c
--- linux.orig/arch/ppc/boot/common/misc-simple.c	Mon Jan  7 23:41:08 2002
+++ linux/arch/ppc/boot/common/misc-simple.c	Wed Dec 26 16:28:34 2001
@@ -45,6 +45,15 @@
 char *cmd_line = cmd_buf;
 
 unsigned long initrd_start = 0, initrd_end = 0;
+
+/* These values must be variables.  If not, the compiler optimizer
+ * will remove some code, causing the size of the code to vary
+ * when these values are zero.  This is bad because we first
+ * compile with these zero to determine the size and offsets
+ * in an image, than compile again with these set to the proper
+ * discovered value.
+ */
+unsigned int initrd_offset, initrd_size;
 char *zimage_start;
 int zimage_size;
 
@@ -69,7 +78,8 @@
 	 * were relocated to.
 	 */
 	puts("loaded at:     "); puthex(load_addr);
-	puts(" "); puthex((unsigned long)(load_addr + (4*num_words))); puts("\n");
+	puts(" "); puthex((unsigned long)(load_addr + (4*num_words)));
+	puts("\n");
 	if ( (unsigned long)load_addr != (unsigned long)&start )
 	{
 		puts("relocated to:  "); puthex((unsigned long)&start);
@@ -82,45 +92,38 @@
 	   the size of the elf header which we strip -- Cort */
 	zimage_start = (char *)(load_addr - 0x10000 + ZIMAGE_OFFSET);
 	zimage_size = ZIMAGE_SIZE;
+	initrd_offset = INITRD_OFFSET;
+	initrd_size = INITRD_SIZE;
 
-	if ( INITRD_OFFSET )
-		initrd_start = load_addr - 0x10000 + INITRD_OFFSET;
+	if ( initrd_offset )
+		initrd_start = load_addr - 0x10000 + initrd_offset;
 	else
 		initrd_start = 0;
-	initrd_end = INITRD_SIZE + initrd_start;
+	initrd_end = initrd_size + initrd_start;
 
-	/*
-	 * Find a place to stick the zimage and initrd and 
-	 * relocate them if we have to. -- Cort
-	 */
+	/* Relocate the zImage */
 	avail_ram = (char *)PAGE_ALIGN((unsigned long)_end);
 	puts("zimage at:     "); puthex((unsigned long)zimage_start);
-	puts(" "); puthex((unsigned long)(zimage_size+zimage_start)); puts("\n");
-	if ( (unsigned long)zimage_start <= 0x00800000 )
-	{
-		memcpy( (void *)avail_ram, (void *)zimage_start, zimage_size );
-		zimage_start = (char *)avail_ram;
-		puts("relocated to:  "); puthex((unsigned long)zimage_start);
-		puts(" ");
-		puthex((unsigned long)zimage_size+(unsigned long)zimage_start);
-		puts("\n");
+	puts(" "); puthex((unsigned long)(zimage_size+zimage_start));
+	puts("\n");
+	memcpy( (void *)avail_ram, (void *)zimage_start, zimage_size );
+	zimage_start = (char *)avail_ram;
+	puts("relocated to:  "); puthex((unsigned long)zimage_start);
+	puts(" ");
+	puthex((unsigned long)zimage_size+(unsigned long)zimage_start);
+	puts("\n");
 
-		/* relocate initrd */
-		if ( initrd_start )
-		{
-			puts("initrd at:     "); puthex(initrd_start);
-			puts(" "); puthex(initrd_end); puts("\n");
-			avail_ram = (char *)PAGE_ALIGN(
-				(unsigned long)zimage_size+(unsigned long)zimage_start);
-			memcpy ((void *)avail_ram, (void *)initrd_start, INITRD_SIZE );
-			initrd_start = (unsigned long)avail_ram;
-			initrd_end = initrd_start + INITRD_SIZE;
-			puts("relocated to:  "); puthex(initrd_start);
-			puts(" "); puthex(initrd_end); puts("\n");
-		}
-	} else if ( initrd_start ) {
+	if ( initrd_start ) {
 		puts("initrd at:     "); puthex(initrd_start);
 		puts(" "); puthex(initrd_end); puts("\n");
+		/* relocate initrd */
+		avail_ram = (char *)PAGE_ALIGN((unsigned long)zimage_size + 
+				(unsigned long)zimage_start);
+		memcpy( (void *)avail_ram, (void *)initrd_start, initrd_size );
+		initrd_start = (unsigned long)avail_ram;
+		initrd_end = initrd_start + initrd_size;
+		puts("relocated to:  "); puthex(initrd_start);
+		puts(" "); puthex(initrd_end); puts("\n");
 	}
 
 	avail_ram = (char *)0x00400000;
@@ -161,11 +164,9 @@
 	puts("\n");
 
 	/* mappings on early boot can only handle 16M */
-	if ( (int)(cmd_line[0]) > (16<<20))
+	if ( (u32)(cmd_line) > (16<<20))
 		puts("cmd_line located > 16M\n");
-	if ( initrd_start > (16<<20))
-		puts("initrd_start located > 16M\n");
-       
+
 	puts("Uncompressing Linux...");
 
 	gunzip(0, 0x400000, zimage_start, &zimage_size);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/boot/common/ns16550.c linux/arch/ppc/boot/common/ns16550.c
--- linux.orig/arch/ppc/boot/common/ns16550.c	Mon Jan  7 23:41:08 2002
+++ linux/arch/ppc/boot/common/ns16550.c	Wed Dec 26 16:28:34 2001
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.ns16550.c 1.9 07/30/01 17:19:40 trini
+ * BK Id: SCCS/s.ns16550.c 1.12 10/08/01 17:16:50 paulus
  */
 /*
  * COM1 NS16550 support
@@ -10,6 +10,9 @@
 #include <linux/serial_reg.h>
 #include <asm/serial.h>
 
+/* Default serial baud rate */
+#define SERIAL_BAUD	9600
+
 extern void outb(int port, unsigned char val);
 extern unsigned char inb(int port);
 extern unsigned long ISA_io;
@@ -46,13 +49,20 @@
 	outb(com_port + (UART_IER << shift), 0x00);
 	/* Access baud rate */
 	outb(com_port + (UART_LCR << shift), 0x80);
-#ifdef CONFIG_SERIAL_CONSOLE_NONSTD
-	/* Input clock. */
-	outb(com_port + (UART_DLL << shift), 
-			(BASE_BAUD / CONFIG_SERIAL_CONSOLE_BAUD));
-	outb(com_port + (UART_DLM << shift), 
-		(BASE_BAUD / CONFIG_SERIAL_CONSOLE_BAUD) >> 8);
-#endif
+	/*
+	 * Test if serial port is unconfigured.
+	 * We assume that no-one uses less than 110 baud or
+	 * less than 7 bits per character these days.
+	 *  -- paulus.
+	 */
+	if (inb(com_port + (UART_DLM << shift)) > 4
+	    || (inb(com_port + (UART_LCR << shift)) & 2) == 0) {
+		/* Input clock. */
+		outb(com_port + (UART_DLL << shift),
+		     (BASE_BAUD / SERIAL_BAUD));
+		outb(com_port + (UART_DLM << shift),
+		     (BASE_BAUD / SERIAL_BAUD) >> 8);
+	}
 	 /* 8 data, 1 stop, no parity */
 	outb(com_port + (UART_LCR << shift), 0x03);
 	/* RTS/DTR */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/boot/images/Makefile linux/arch/ppc/boot/images/Makefile
--- linux.orig/arch/ppc/boot/images/Makefile	Mon Jan  7 23:41:08 2002
+++ linux/arch/ppc/boot/images/Makefile	Wed Dec 26 16:28:34 2001
@@ -9,4 +9,4 @@
 	gzip -vf9 vmlinux
 
 clean:
-	rm -f sImage vmapus vmlinux.* miboot.image* zImage* zvmlinux.*
+	rm -f sImage vmapus vmlinux* miboot* zImage* zvmlinux*
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/boot/mbx/Makefile linux/arch/ppc/boot/mbx/Makefile
--- linux.orig/arch/ppc/boot/mbx/Makefile	Mon Jan  7 23:41:08 2002
+++ linux/arch/ppc/boot/mbx/Makefile	Wed Dec 26 16:28:34 2001
@@ -1,4 +1,4 @@
-# BK Id: SCCS/s.Makefile 1.7 06/05/01 20:20:05 paulus
+# BK Id: SCCS/s.Makefile 1.9 10/15/01 10:53:29 trini
 #
 #
 # arch/ppc/mbxboot/Makefile
@@ -73,6 +73,12 @@
 		-DZIMAGE_SIZE=0 -c -o $@ $*.c
 
 zvmlinux.initrd: $(OBJECTS) $(LIBS) ../images/vmlinux.gz
+#
+# Recompile misc.o again with more 'correct' bogus offsets
+#
+	$(CC) $(CFLAGS) -DINITRD_OFFSET=0x0008c8e3 -DINITRD_SIZE=0x0000111a \
+		-DZIMAGE_OFFSET=0x00018000 -DZIMAGE_SIZE=0x000748e2 \
+		-c -o misc.o misc.c
 	$(LD) $(ZLINKFLAGS) -o $@.tmp $(OBJECTS) $(LIBS)
 	$(OBJCOPY) $(OBJCOPY_ARGS) -R .comment \
 		--add-section=initrd=../images/ramdisk.image.gz \
@@ -88,6 +94,8 @@
 		--add-section=initrd=../images/ramdisk.image.gz \
 		--add-section=image=../images/vmlinux.gz \
 		$@.tmp ../images/$@.embedded
+# Remove zvmlinux and zvmlinux.temp, we have ../images/zvmlinux.embedded
+	rm -f $@.tmp $@
 
 zImage: zvmlinux
 ifeq ($(CONFIG_RPXCLASSIC),y)
@@ -104,6 +112,12 @@
 endif
 
 zvmlinux: $(OBJECTS) $(LIBS) ../images/vmlinux.gz
+#
+# Recompile misc.o again with more 'correct' bogus offsets
+#
+	$(CC) $(CFLAGS) -DINITRD_OFFSET=0 -DINITRD_SIZE=0 \
+		-DZIMAGE_OFFSET=0x00018000 -DZIMAGE_SIZE=0x000748e2 \
+		-c -o misc.o misc.c
 #
 # build the boot loader image and then compute the offset into it
 # for the kernel image
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/boot/mbx/misc.c linux/arch/ppc/boot/mbx/misc.c
--- linux.orig/arch/ppc/boot/mbx/misc.c	Mon Jan  7 23:41:08 2002
+++ linux/arch/ppc/boot/mbx/misc.c	Wed Dec 26 16:28:34 2001
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.misc.c 1.13 07/27/01 11:44:37 trini
+ * BK Id: SCCS/s.misc.c 1.15 10/15/01 10:53:29 trini
  */
 /*
  * Adapted for PowerPC by Gary Thomas
@@ -44,10 +44,6 @@
 char *avail_ram;
 char *end_avail;
 
-/* See comment below.....
-*/
-unsigned int initrd_offset, initrd_size;
-
 /* Because of the limited amount of memory on embedded, it presents
  * loading problems.  The biggest is that we load this boot program
  * into a relatively low memory address, and the Linux kernel Bss often
@@ -75,14 +71,13 @@
 bd_t *hold_residual = &hold_resid_buf;
 unsigned long initrd_start = 0, initrd_end = 0;
 char *zimage_start;
-int zimage_size;
 
 extern void gunzip(void *, int, unsigned char *, int *);
 
 unsigned long
 decompress_kernel(unsigned long load_addr, int num_words, unsigned long cksum, bd_t *bp)
 {
-	int timer;
+	int timer, zimage_size = ZIMAGE_SIZE;
 	extern unsigned long start;
 	char *cp, ch;
 
@@ -93,17 +88,6 @@
 	serial_init(bp);
 #endif
 
-	/* These values must be variables.  If not, the compiler optimizer
-	 * will remove some code, causing the size of the code to vary
-	 * when these values are zero.  This is bad because we first
-	 * compile with these zero to determine the size and offsets
-	 * in an image, than compile again with these set to the proper
-	 * discovered value.....Ya know, we used to read these from the
-	 * header a long time ago.....
-	 */
-	initrd_offset = INITRD_OFFSET;
-	initrd_size = INITRD_SIZE;
-
 	/* Grab some space for the command line and board info.  Since
 	 * we no longer use the ELF header, but it was loaded, grab
 	 * that space.
@@ -154,13 +138,12 @@
 	/* we have to subtract 0x10000 here to correct for objdump including the
 	   size of the elf header which we strip -- Cort */
 	zimage_start = (char *)(load_addr - 0x10000 + ZIMAGE_OFFSET);
-	zimage_size = ZIMAGE_SIZE;
 
-	if ( initrd_offset )
-		initrd_start = load_addr - 0x10000 + initrd_offset;
+	if ( INITRD_OFFSET )
+		initrd_start = load_addr - 0x10000 + INITRD_OFFSET;
 	else
 		initrd_start = 0;
-	initrd_end = initrd_size + initrd_start;
+	initrd_end = INITRD_SIZE + initrd_start;
 
 	/*
 	 * setup avail_ram - this is the first part of ram usable
@@ -201,9 +184,9 @@
 		if ((unsigned long)initrd_start > 0x01000000) {
 			memcpy ((void *)PAGE_ALIGN(-PAGE_SIZE+(unsigned long)end_avail-INITRD_SIZE),
 				(void *)initrd_start,
-				initrd_size );
+				INITRD_SIZE );
 			initrd_start = PAGE_ALIGN(-PAGE_SIZE+(unsigned long)end_avail-INITRD_SIZE);
-			initrd_end = initrd_start + initrd_size;
+			initrd_end = initrd_start + INITRD_SIZE;
 			end_avail = (char *)initrd_start;
 			puts("relocated to:  "); puthex(initrd_start);
 			puts(" "); puthex(initrd_end); puts("\n");
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/boot/pmac/Makefile linux/arch/ppc/boot/pmac/Makefile
--- linux.orig/arch/ppc/boot/pmac/Makefile	Mon Jan  7 23:41:08 2002
+++ linux/arch/ppc/boot/pmac/Makefile	Wed Dec 26 16:28:34 2001
@@ -1,4 +1,4 @@
-# BK Id: SCCS/s.Makefile 1.14 07/27/01 20:24:17 trini
+# BK Id: SCCS/s.Makefile 1.16 09/28/01 07:39:37 trini
 #
 # Makefile for making XCOFF bootable images for booting on PowerMacs
 # using Open Firmware.
@@ -48,9 +48,9 @@
 	cp ../images/vmlinux.coff $(TFTPIMAGE)
 	cp ../images/vmlinux.elf-pmac $(TFTPIMAGE).elf
 
-znetboot.initrd: vmlinux.coff.initrd vmlinux.initrd.elf-pmac
-	cp ../images/vmlinux.coff.initrd $(TFTPIMAGE)
-	cp ../images/vmlinux.elf-pmac.initrd $(TFTPIMAGE).elf
+znetboot.initrd: vmlinux.initrd.coff vmlinux.initrd.elf-pmac
+	cp ../images/vmlinux.initrd.coff $(TFTPIMAGE)
+	cp ../images/vmlinux.initrd.elf-pmac $(TFTPIMAGE).elf
 
 #floppy: zImage
 #	mount -t hfs /dev/fd0 /mnt
@@ -61,7 +61,7 @@
 	$(OBJCOPY) $(OBJCOPY_ARGS) --add-section=image=../images/vmlinux.gz \
 		dummy.o ../images/$@
 
-miboot.image.initrd: miboot.image ../images/ramdisk.image.gz
+miboot.initrd.image: miboot.image ../images/ramdisk.image.gz
 	$(OBJCOPY) $(OBJCOPY_ARGS) --add-section=initrd=../images/ramdisk.image.gz \
 		../images/miboot.image ../images/$@
 
@@ -83,11 +83,11 @@
 	rm -f coffboot
 	ln -sf vmlinux.coff ../images/zImage.pmac
 
-vmlinux.coff.initrd: coffboot.initrd $(HACKCOFF)
+vmlinux.initrd.coff: coffboot.initrd $(HACKCOFF)
 	$(OBJCOPY) $(OBJCOPY_ARGS) coffboot.initrd ../images/$@
 	$(HACKCOFF) ../images/$@
 	rm -f coffboot.initrd
-	ln -sf vmlinux.coff.initrd ../images/zImage.initrd.pmac
+	ln -sf vmlinux.initrd.coff ../images/zImage.initrd.pmac
 
 vmlinux.elf-pmac: $(CHRPOBJS) $(LIBS) ../common/no_initrd.o $(MKNOTE) ../images/vmlinux.gz
 	$(LD) $(CHRP_LD_ARGS) -o ../images/$@ $(CHRPOBJS) ../common/no_initrd.o $(LIBS)
@@ -110,6 +110,6 @@
 
 zImage: vmlinux.coff vmlinux.elf-pmac miboot.image
 
-zImage.initrd: vmlinux.coff.initrd vmlinux.initrd.elf-pmac miboot.image.initrd
+zImage.initrd: vmlinux.initrd.coff vmlinux.initrd.elf-pmac miboot.initrd.image
 
 include $(TOPDIR)/Rules.make
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/boot/prep/Makefile linux/arch/ppc/boot/prep/Makefile
--- linux.orig/arch/ppc/boot/prep/Makefile	Mon Jan  7 23:41:08 2002
+++ linux/arch/ppc/boot/prep/Makefile	Wed Dec 26 16:28:34 2001
@@ -1,4 +1,4 @@
-# BK Id: SCCS/s.Makefile 1.26 09/25/01 07:54:40 trini
+# BK Id: SCCS/s.Makefile 1.28 10/21/01 20:47:58 trini
 #
 # arch/ppc/boot/Makefile
 #
@@ -45,6 +45,12 @@
 		-DZIMAGE_SIZE=0 -c -o $@ $*.c
 
 zvmlinux.initrd: $(obj-y) $(LIBS) ../images/vmlinux.gz
+#
+# Recompile misc.oagain with more 'correct' bogus offsets
+#
+	$(CC) $(CFLAGS) -DINITRD_OFFSET=0x00138466 -DINITRD_SIZE=0x0000111a \
+		-DZIMAGE_OFFSET=0x0001b000 -DZIMAGE_SIZE=0x0011d460 \
+		-c -o misc.o misc.c
 	$(LD) $(ZLINKFLAGS) -o $@.tmp $(obj-y) $(LIBS)
 	$(OBJCOPY) $(OBJCOPY_ARGS) -R .comment \
 		--add-section=initrd=../images/ramdisk.image.gz \
@@ -60,7 +66,7 @@
 		--add-section=initrd=../images/ramdisk.image.gz \
 		--add-section=image=../images/vmlinux.gz \
 		$@.tmp $@
-	rm -f $@.tmp zvmlinux
+	rm -f $@.tmp
 
 zImage: zvmlinux $(MKPREP)
 	$(MKPREP) -pbp zvmlinux ../images/$@.prep
@@ -72,6 +78,12 @@
 
 zvmlinux: $(obj-y) $(LIBS) ../images/vmlinux.gz
 #
+# Recompile misc.oagain with more 'correct' bogus offsets
+#
+	$(CC) $(CFLAGS) -DINITRD_OFFSET=0 -DINITRD_SIZE=0 \
+		-DZIMAGE_OFFSET=0x0001b000 -DZIMAGE_SIZE=0x0011d460 \
+		-c -o misc.o misc.c
+#
 # build the boot loader image and then compute the offset into it
 # for the kernel image
 #
@@ -88,7 +100,7 @@
 	$(LD) $(ZLINKFLAGS) -o zvmlinux.tmp $(obj-y) $(LIBS)
 	$(OBJCOPY) $(OBJCOPY_ARGS) -R .comment \
 		--add-section=image=../images/vmlinux.gz $@.tmp $@
-	rm $@.tmp
+	rm -f $@.tmp
 
 floppy: zImage
 	dd if=../images/zImage.prep of=/dev/fd0H1440 bs=64b
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/boot/prep/misc.c linux/arch/ppc/boot/prep/misc.c
--- linux.orig/arch/ppc/boot/prep/misc.c	Mon Jan  7 23:41:08 2002
+++ linux/arch/ppc/boot/prep/misc.c	Wed Dec 26 16:28:34 2001
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.misc.c 1.20 09/24/01 18:42:54 trini
+ * BK Id: SCCS/s.misc.c 1.22 10/15/01 17:46:21 trini
  *
  * arch/ppc/boot/prep/misc.c
  *
@@ -48,14 +48,6 @@
 RESIDUAL *hold_residual = &hold_resid_buf;
 unsigned long initrd_start = 0, initrd_end = 0;
 
-/* These values must be variables.  If not, the compiler optimizer
- * will remove some code, causing the size of the code to vary
- * when these values are zero.  This is bad because we first
- * compile with these zero to determine the size and offsets
- * in an image, than compile again with these set to the proper
- * discovered value.
- */
-unsigned int initrd_offset, initrd_size;
 char *zimage_start;
 int zimage_size;
 
@@ -311,14 +303,12 @@
 	   size of the elf header which we strip -- Cort */
 	zimage_start = (char *)(load_addr - 0x10000 + ZIMAGE_OFFSET);
 	zimage_size = ZIMAGE_SIZE;
-	initrd_offset = INITRD_OFFSET;
-	initrd_size = INITRD_SIZE;
 
-	if ( initrd_offset )
-		initrd_start = load_addr - 0x10000 + initrd_offset;
+	if ( INITRD_OFFSET )
+		initrd_start = load_addr - 0x10000 + INITRD_OFFSET;
 	else
 		initrd_start = 0;
-	initrd_end = initrd_size + initrd_start;
+	initrd_end = INITRD_SIZE + initrd_start;
 
 	/*
 	 * Find a place to stick the zimage and initrd and 
@@ -343,9 +333,9 @@
 			puts(" "); puthex(initrd_end); puts("\n");
 			avail_ram = (char *)PAGE_ALIGN(
 				(unsigned long)zimage_size+(unsigned long)zimage_start);
-			memcpy ((void *)avail_ram, (void *)initrd_start, initrd_size );
+			memcpy ((void *)avail_ram, (void *)initrd_start, INITRD_SIZE );
 			initrd_start = (unsigned long)avail_ram;
-			initrd_end = initrd_start + initrd_size;
+			initrd_end = initrd_start + INITRD_SIZE;
 			puts("relocated to:  "); puthex(initrd_start);
 			puts(" "); puthex(initrd_end); puts("\n");
 		}
@@ -395,7 +385,7 @@
 	puts("\n");
 
 	/* mappings on early boot can only handle 16M */
-	if ( (int)(cmd_line[0]) > (16<<20))
+	if ( (int)(cmd_line) > (16<<20))
 		puts("cmd_line located > 16M\n");
 	if ( (int)hold_residual > (16<<20))
 		puts("hold_residual located > 16M\n");
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/boot/utils/mksimage.c linux/arch/ppc/boot/utils/mksimage.c
--- linux.orig/arch/ppc/boot/utils/mksimage.c	Mon Jan  7 23:41:08 2002
+++ linux/arch/ppc/boot/utils/mksimage.c	Wed Dec 26 16:28:34 2001
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.mksimage.c 1.6 05/18/01 15:16:42 cort
+ * BK Id: SCCS/s.mksimage.c 1.7 10/11/01 11:59:05 trini
  */
 /*
  *
@@ -96,7 +96,7 @@
 		die("can't open loader: %s", strerror(errno));
 
 	copy_blocks(fd, ofd, &ld_off, &ld_size);
-	len = sprintf(buffer, "bootloader: %x %x\n", ld_off, ld_size);
+	len = sprintf(buffer, "bootloader: %lx %lx\n", ld_off, ld_size);
 	close(fd);
 
 	fd = open(kernel, O_RDONLY);
@@ -104,7 +104,7 @@
 		die("can't open kernel: %s", strerror(errno));
 
 	copy_blocks(fd, ofd, &kern_off, &kern_size);
-	len += sprintf(buffer+len, "zimage: %x %x\n", kern_off, kern_size);
+	len += sprintf(buffer+len, "zimage: %lx %lx\n", kern_off, kern_size);
 	close(fd);
 	
 	if (rdimage) {
@@ -116,7 +116,7 @@
 		close(fd);
 	}
 
-	len += sprintf(buffer+len, "initrd: %x %x", rd_off, rd_size);
+	len += sprintf(buffer+len, "initrd: %lx %lx", rd_off, rd_size);
 
 	close(ofd);
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/boot/utils/offset linux/arch/ppc/boot/utils/offset
--- linux.orig/arch/ppc/boot/utils/offset	Mon Jan  7 23:41:08 2002
+++ linux/arch/ppc/boot/utils/offset	Wed Dec 26 16:28:34 2001
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/bin/sh
 
-OFFSET=`$1 -h $2  | grep $3 | grep -v zvmlinux| awk '{print $6}'`
+OFFSET=`$1 -h $2 | grep $3 | grep -v zvmlinux | awk '{print $6}'`
 echo "0x"$OFFSET
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/boot/utils/sioffset linux/arch/ppc/boot/utils/sioffset
--- linux.orig/arch/ppc/boot/utils/sioffset	Mon Jan  7 23:41:08 2002
+++ linux/arch/ppc/boot/utils/sioffset	Wed Dec 26 16:28:34 2001
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/bin/sh
 
 OFFSET=`grep $1 sImage.map | awk '{print $2}'`
 echo "0x"$OFFSET
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/boot/utils/sisize linux/arch/ppc/boot/utils/sisize
--- linux.orig/arch/ppc/boot/utils/sisize	Mon Jan  7 23:41:08 2002
+++ linux/arch/ppc/boot/utils/sisize	Wed Dec 26 16:28:34 2001
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/bin/sh
 
 OFFSET=`grep $1 sImage.map | awk '{print $3}'`
 echo "0x"$OFFSET
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/boot/utils/size linux/arch/ppc/boot/utils/size
--- linux.orig/arch/ppc/boot/utils/size	Mon Jan  7 23:41:08 2002
+++ linux/arch/ppc/boot/utils/size	Wed Dec 26 16:28:34 2001
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/bin/sh
 
-OFFSET=`$1 -h $2  | grep $3 | grep -v zvmlinux | awk '{print $3}'`
+OFFSET=`$1 -h $2 | grep $3 | grep -v zvmlinux | awk '{print $3}'`
 echo "0x"$OFFSET
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/config.in linux/arch/ppc/config.in
--- linux.orig/arch/ppc/config.in	Mon Jan  7 23:41:08 2002
+++ linux/arch/ppc/config.in	Wed Dec 26 16:28:34 2001
@@ -1,4 +1,4 @@
-# BK Id: SCCS/s.config.in 1.45 11/08/01 07:57:40 paulus
+# BK Id: SCCS/s.config.in 1.47 12/01/01 20:09:06 benh
 #
 # For a description of the syntax of this configuration file,
 # see Documentation/kbuild/config-language.txt.
@@ -328,6 +328,10 @@
   bool 'Support for PMU  based PowerMacs' CONFIG_ADB_PMU
   if [ "$CONFIG_ADB_PMU" = "y" ]; then
     bool '   Power management support for PowerBooks' CONFIG_PMAC_PBOOK
+    if [ "$CONFIG_PMAC_PBOOK" = "y" ]; then
+      define_bool CONFIG_PM y
+      tristate '     APM emulation' CONFIG_PMAC_APM_EMU
+    fi
     # made a separate option since backlight may end up beeing used
     # on non-powerbook machines (but only on PMU based ones AFAIK)
     bool '   Backlight control for LCD screens' CONFIG_PMAC_BACKLIGHT
@@ -352,6 +356,9 @@
   # layer is used.
   if [ "$CONFIG_INPUT" != "n" ]; then
     define_bool CONFIG_MAC_HID y
+  fi
+  if [ "$CONFIG_ADB_CUDA" != "n" ]; then
+    bool 'Support for ANS LCD display' CONFIG_ANSLCD
   fi
 fi
 endmenu
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/configs/common_defconfig linux/arch/ppc/configs/common_defconfig
--- linux.orig/arch/ppc/configs/common_defconfig	Mon Jan  7 23:41:08 2002
+++ linux/arch/ppc/configs/common_defconfig	Wed Dec 26 16:28:34 2001
@@ -4,6 +4,7 @@
 # CONFIG_UID16 is not set
 # CONFIG_RWSEM_GENERIC_SPINLOCK is not set
 CONFIG_RWSEM_XCHGADD_ALGORITHM=y
+CONFIG_HAVE_DEC_LOCK=y
 
 #
 # Code maturity level options
@@ -119,8 +120,6 @@
 #
 CONFIG_PACKET=y
 # CONFIG_PACKET_MMAP is not set
-CONFIG_NETLINK=y
-# CONFIG_RTNETLINK is not set
 # CONFIG_NETLINK_DEV is not set
 CONFIG_NETFILTER=y
 # CONFIG_NETFILTER_DEBUG is not set
@@ -133,6 +132,7 @@
 # CONFIG_NET_IPIP is not set
 # CONFIG_NET_IPGRE is not set
 # CONFIG_IP_MROUTE is not set
+# CONFIG_ARPD is not set
 # CONFIG_INET_ECN is not set
 CONFIG_SYN_COOKIES=y
 
@@ -343,15 +343,11 @@
 # CONFIG_SCSI_INIA100 is not set
 # CONFIG_SCSI_NCR53C406A is not set
 # CONFIG_SCSI_NCR53C7xx is not set
-CONFIG_SCSI_NCR53C8XX=y
-CONFIG_SCSI_SYM53C8XX=y
-CONFIG_SCSI_NCR53C8XX_DEFAULT_TAGS=8
-CONFIG_SCSI_NCR53C8XX_MAX_TAGS=32
-CONFIG_SCSI_NCR53C8XX_SYNC=20
-# CONFIG_SCSI_NCR53C8XX_PROFILE is not set
-# CONFIG_SCSI_NCR53C8XX_IOMAPPED is not set
-# CONFIG_SCSI_NCR53C8XX_PQS_PDS is not set
-# CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT is not set
+CONFIG_SCSI_SYM53C8XX_2=y
+CONFIG_SCSI_SYM53C8XX_DMA_ADDRESSING_MODE=0
+CONFIG_SCSI_SYM53C8XX_DEFAULT_TAGS=16
+CONFIG_SCSI_SYM53C8XX_MAX_TAGS=64
+# CONFIG_SCSI_SYM53C8XX_IOMAPPED is not set
 # CONFIG_SCSI_PAS16 is not set
 # CONFIG_SCSI_PCI2000 is not set
 # CONFIG_SCSI_PCI2220I is not set
@@ -388,7 +384,9 @@
 #
 # Appletalk devices
 #
-# CONFIG_APPLETALK is not set
+# CONFIG_LTPC is not set
+# CONFIG_COPS is not set
+# CONFIG_IPDDP is not set
 # CONFIG_DUMMY is not set
 # CONFIG_BONDING is not set
 # CONFIG_EQUALIZER is not set
@@ -443,6 +441,7 @@
 # CONFIG_SUNDANCE is not set
 # CONFIG_TLAN is not set
 # CONFIG_VIA_RHINE is not set
+# CONFIG_VIA_RHINE_MMIO is not set
 # CONFIG_WINBOND_840 is not set
 # CONFIG_NET_POCKET is not set
 
@@ -544,6 +543,7 @@
 CONFIG_FB_MATROX_MILLENIUM=y
 CONFIG_FB_MATROX_MYSTIQUE=y
 # CONFIG_FB_MATROX_G100 is not set
+# CONFIG_FB_MATROX_I2C is not set
 # CONFIG_FB_MATROX_G450 is not set
 # CONFIG_FB_MATROX_MULTIHEAD is not set
 CONFIG_FB_ATY=y
@@ -597,6 +597,7 @@
 CONFIG_MAC_ADBKEYCODES=y
 CONFIG_MAC_EMUMOUSEBTN=y
 CONFIG_MAC_HID=y
+# CONFIG_ANSLCD is not set
 
 #
 # Character devices
@@ -612,7 +613,12 @@
 #
 # I2C support
 #
-# CONFIG_I2C is not set
+CONFIG_I2C=m
+# CONFIG_I2C_ALGOBIT is not set
+# CONFIG_I2C_ALGOPCF is not set
+CONFIG_I2C_KEYWEST=m
+CONFIG_I2C_CHARDEV=m
+CONFIG_I2C_PROC=m
 
 #
 # Mice
@@ -693,11 +699,15 @@
 # CONFIG_AUTOFS4_FS is not set
 # CONFIG_REISERFS_FS is not set
 # CONFIG_REISERFS_CHECK is not set
+# CONFIG_REISERFS_PROC_INFO is not set
 # CONFIG_ADFS_FS is not set
 # CONFIG_ADFS_FS_RW is not set
 # CONFIG_AFFS_FS is not set
 CONFIG_HFS_FS=m
 # CONFIG_BFS_FS is not set
+# CONFIG_EXT3_FS is not set
+# CONFIG_JBD is not set
+# CONFIG_JBD_DEBUG is not set
 CONFIG_FAT_FS=m
 CONFIG_MSDOS_FS=m
 # CONFIG_UMSDOS_FS is not set
@@ -710,6 +720,7 @@
 # CONFIG_RAMFS is not set
 CONFIG_ISO9660_FS=y
 # CONFIG_JOLIET is not set
+# CONFIG_ZISOFS is not set
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
@@ -734,6 +745,7 @@
 # Network File Systems
 #
 # CONFIG_CODA_FS is not set
+# CONFIG_INTERMEZZO_FS is not set
 CONFIG_NFS_FS=y
 # CONFIG_NFS_V3 is not set
 # CONFIG_ROOT_NFS is not set
@@ -751,6 +763,8 @@
 # CONFIG_NCPFS_SMALLDOS is not set
 # CONFIG_NCPFS_NLS is not set
 # CONFIG_NCPFS_EXTRAS is not set
+# CONFIG_ZISOFS_FS is not set
+# CONFIG_ZLIB_FS_INFLATE is not set
 
 #
 # Partition Types
@@ -818,8 +832,10 @@
 # Sound
 #
 CONFIG_SOUND=m
-CONFIG_DMASOUND_AWACS=m
+CONFIG_DMASOUND_PMAC=m
 CONFIG_DMASOUND=m
+CONFIG_I2C=m
+CONFIG_I2C_KEYWEST=m
 # CONFIG_SOUND_BT878 is not set
 # CONFIG_SOUND_CMPCI is not set
 # CONFIG_SOUND_EMU10K1 is not set
@@ -854,7 +870,6 @@
 CONFIG_USB_DEVICEFS=y
 # CONFIG_USB_BANDWIDTH is not set
 # CONFIG_USB_LONG_TIMEOUT is not set
-# CONFIG_USB_LARGE_CONFIG is not set
 
 #
 # USB Controllers
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/configs/gemini_defconfig linux/arch/ppc/configs/gemini_defconfig
--- linux.orig/arch/ppc/configs/gemini_defconfig	Mon Jan  7 23:41:08 2002
+++ linux/arch/ppc/configs/gemini_defconfig	Wed Dec 26 16:28:34 2001
@@ -4,6 +4,7 @@
 # CONFIG_UID16 is not set
 # CONFIG_RWSEM_GENERIC_SPINLOCK is not set
 CONFIG_RWSEM_XCHGADD_ALGORITHM=y
+CONFIG_HAVE_DEC_LOCK=y
 
 #
 # Code maturity level options
@@ -109,8 +110,6 @@
 #
 CONFIG_PACKET=y
 # CONFIG_PACKET_MMAP is not set
-CONFIG_NETLINK=y
-# CONFIG_RTNETLINK is not set
 # CONFIG_NETLINK_DEV is not set
 CONFIG_NETFILTER=y
 # CONFIG_NETFILTER_DEBUG is not set
@@ -122,6 +121,7 @@
 # CONFIG_IP_PNP is not set
 # CONFIG_NET_IPIP is not set
 # CONFIG_NET_IPGRE is not set
+# CONFIG_ARPD is not set
 # CONFIG_INET_ECN is not set
 # CONFIG_SYN_COOKIES is not set
 
@@ -221,15 +221,11 @@
 # CONFIG_SCSI_INIA100 is not set
 # CONFIG_SCSI_NCR53C406A is not set
 # CONFIG_SCSI_NCR53C7xx is not set
-# CONFIG_SCSI_NCR53C8XX is not set
-CONFIG_SCSI_SYM53C8XX=y
-CONFIG_SCSI_NCR53C8XX_DEFAULT_TAGS=8
-CONFIG_SCSI_NCR53C8XX_MAX_TAGS=32
-CONFIG_SCSI_NCR53C8XX_SYNC=20
-# CONFIG_SCSI_NCR53C8XX_PROFILE is not set
-# CONFIG_SCSI_NCR53C8XX_IOMAPPED is not set
-# CONFIG_SCSI_NCR53C8XX_PQS_PDS is not set
-# CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT is not set
+CONFIG_SCSI_SYM53C8XX_2=y
+CONFIG_SCSI_SYM53C8XX_DMA_ADDRESSING_MODE=0
+CONFIG_SCSI_SYM53C8XX_DEFAULT_TAGS=16
+CONFIG_SCSI_SYM53C8XX_MAX_TAGS=64
+# CONFIG_SCSI_SYM53C8XX_IOMAPPED is not set
 # CONFIG_SCSI_PAS16 is not set
 # CONFIG_SCSI_PCI2000 is not set
 # CONFIG_SCSI_PCI2220I is not set
@@ -432,11 +428,15 @@
 # CONFIG_AUTOFS4_FS is not set
 # CONFIG_REISERFS_FS is not set
 # CONFIG_REISERFS_CHECK is not set
+# CONFIG_REISERFS_PROC_INFO is not set
 # CONFIG_ADFS_FS is not set
 # CONFIG_ADFS_FS_RW is not set
 # CONFIG_AFFS_FS is not set
 # CONFIG_HFS_FS is not set
 # CONFIG_BFS_FS is not set
+# CONFIG_EXT3_FS is not set
+# CONFIG_JBD is not set
+# CONFIG_JBD_DEBUG is not set
 # CONFIG_FAT_FS is not set
 # CONFIG_MSDOS_FS is not set
 # CONFIG_UMSDOS_FS is not set
@@ -449,6 +449,7 @@
 # CONFIG_RAMFS is not set
 CONFIG_ISO9660_FS=y
 # CONFIG_JOLIET is not set
+# CONFIG_ZISOFS is not set
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
@@ -473,6 +474,7 @@
 # Network File Systems
 #
 # CONFIG_CODA_FS is not set
+# CONFIG_INTERMEZZO_FS is not set
 CONFIG_NFS_FS=y
 # CONFIG_NFS_V3 is not set
 # CONFIG_ROOT_NFS is not set
@@ -490,6 +492,8 @@
 # CONFIG_NCPFS_SMALLDOS is not set
 # CONFIG_NCPFS_NLS is not set
 # CONFIG_NCPFS_EXTRAS is not set
+# CONFIG_ZISOFS_FS is not set
+# CONFIG_ZLIB_FS_INFLATE is not set
 
 #
 # Partition Types
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/configs/ibmchrp_defconfig linux/arch/ppc/configs/ibmchrp_defconfig
--- linux.orig/arch/ppc/configs/ibmchrp_defconfig	Mon Jan  7 23:41:08 2002
+++ linux/arch/ppc/configs/ibmchrp_defconfig	Wed Dec 26 16:28:34 2001
@@ -4,6 +4,7 @@
 # CONFIG_UID16 is not set
 # CONFIG_RWSEM_GENERIC_SPINLOCK is not set
 CONFIG_RWSEM_XCHGADD_ALGORITHM=y
+CONFIG_HAVE_DEC_LOCK=y
 
 #
 # Code maturity level options
@@ -112,8 +113,6 @@
 #
 CONFIG_PACKET=y
 # CONFIG_PACKET_MMAP is not set
-CONFIG_NETLINK=y
-# CONFIG_RTNETLINK is not set
 # CONFIG_NETLINK_DEV is not set
 CONFIG_NETFILTER=y
 # CONFIG_NETFILTER_DEBUG is not set
@@ -126,6 +125,7 @@
 # CONFIG_NET_IPIP is not set
 # CONFIG_NET_IPGRE is not set
 # CONFIG_IP_MROUTE is not set
+# CONFIG_ARPD is not set
 # CONFIG_INET_ECN is not set
 CONFIG_SYN_COOKIES=y
 
@@ -252,15 +252,11 @@
 # CONFIG_SCSI_INIA100 is not set
 # CONFIG_SCSI_NCR53C406A is not set
 # CONFIG_SCSI_NCR53C7xx is not set
-# CONFIG_SCSI_NCR53C8XX is not set
-CONFIG_SCSI_SYM53C8XX=y
-CONFIG_SCSI_NCR53C8XX_DEFAULT_TAGS=8
-CONFIG_SCSI_NCR53C8XX_MAX_TAGS=32
-CONFIG_SCSI_NCR53C8XX_SYNC=20
-# CONFIG_SCSI_NCR53C8XX_PROFILE is not set
-# CONFIG_SCSI_NCR53C8XX_IOMAPPED is not set
-# CONFIG_SCSI_NCR53C8XX_PQS_PDS is not set
-# CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT is not set
+CONFIG_SCSI_SYM53C8XX_2=y
+CONFIG_SCSI_SYM53C8XX_DMA_ADDRESSING_MODE=0
+CONFIG_SCSI_SYM53C8XX_DEFAULT_TAGS=16
+CONFIG_SCSI_SYM53C8XX_MAX_TAGS=64
+# CONFIG_SCSI_SYM53C8XX_IOMAPPED is not set
 # CONFIG_SCSI_PAS16 is not set
 # CONFIG_SCSI_PCI2000 is not set
 # CONFIG_SCSI_PCI2220I is not set
@@ -343,6 +339,7 @@
 # CONFIG_SUNDANCE is not set
 # CONFIG_TLAN is not set
 # CONFIG_VIA_RHINE is not set
+# CONFIG_VIA_RHINE_MMIO is not set
 # CONFIG_WINBOND_840 is not set
 # CONFIG_NET_POCKET is not set
 
@@ -546,7 +543,7 @@
 # CONFIG_WATCHDOG is not set
 # CONFIG_INTEL_RNG is not set
 CONFIG_NVRAM=y
-CONFIG_RTC=y
+# CONFIG_RTC is not set
 # CONFIG_DTLK is not set
 # CONFIG_R3964 is not set
 # CONFIG_APPLICOM is not set
@@ -571,11 +568,15 @@
 # CONFIG_AUTOFS4_FS is not set
 # CONFIG_REISERFS_FS is not set
 # CONFIG_REISERFS_CHECK is not set
+# CONFIG_REISERFS_PROC_INFO is not set
 # CONFIG_ADFS_FS is not set
 # CONFIG_ADFS_FS_RW is not set
 # CONFIG_AFFS_FS is not set
 # CONFIG_HFS_FS is not set
 # CONFIG_BFS_FS is not set
+# CONFIG_EXT3_FS is not set
+# CONFIG_JBD is not set
+# CONFIG_JBD_DEBUG is not set
 CONFIG_FAT_FS=m
 CONFIG_MSDOS_FS=m
 # CONFIG_UMSDOS_FS is not set
@@ -588,6 +589,7 @@
 # CONFIG_RAMFS is not set
 CONFIG_ISO9660_FS=y
 # CONFIG_JOLIET is not set
+# CONFIG_ZISOFS is not set
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
@@ -612,6 +614,7 @@
 # Network File Systems
 #
 # CONFIG_CODA_FS is not set
+# CONFIG_INTERMEZZO_FS is not set
 # CONFIG_NFS_FS is not set
 # CONFIG_NFS_V3 is not set
 # CONFIG_ROOT_NFS is not set
@@ -629,6 +632,8 @@
 # CONFIG_NCPFS_SMALLDOS is not set
 # CONFIG_NCPFS_NLS is not set
 # CONFIG_NCPFS_EXTRAS is not set
+# CONFIG_ZISOFS_FS is not set
+# CONFIG_ZLIB_FS_INFLATE is not set
 
 #
 # Partition Types
@@ -677,7 +682,7 @@
 # CONFIG_NLS_CODEPAGE_874 is not set
 # CONFIG_NLS_ISO8859_8 is not set
 # CONFIG_NLS_CODEPAGE_1251 is not set
-# CONFIG_NLS_ISO8859_1 is not set
+CONFIG_NLS_ISO8859_1=m
 # CONFIG_NLS_ISO8859_2 is not set
 # CONFIG_NLS_ISO8859_3 is not set
 # CONFIG_NLS_ISO8859_4 is not set
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/configs/pmac_defconfig linux/arch/ppc/configs/pmac_defconfig
--- linux.orig/arch/ppc/configs/pmac_defconfig	Thu Jan  1 00:00:00 1970
+++ linux/arch/ppc/configs/pmac_defconfig	Wed Dec 26 16:28:34 2001
@@ -0,0 +1,1076 @@
+#
+# Automatically generated make config: don't edit
+#
+# CONFIG_UID16 is not set
+# CONFIG_RWSEM_GENERIC_SPINLOCK is not set
+CONFIG_RWSEM_XCHGADD_ALGORITHM=y
+CONFIG_HAVE_DEC_LOCK=y
+
+#
+# Code maturity level options
+#
+CONFIG_EXPERIMENTAL=y
+
+#
+# Loadable module support
+#
+CONFIG_MODULES=y
+CONFIG_MODVERSIONS=y
+CONFIG_KMOD=y
+
+#
+# Platform support
+#
+CONFIG_PPC=y
+CONFIG_PPC32=y
+CONFIG_6xx=y
+# CONFIG_4xx is not set
+# CONFIG_POWER3 is not set
+# CONFIG_POWER4 is not set
+# CONFIG_8xx is not set
+# CONFIG_8260 is not set
+CONFIG_PPC_STD_MMU=y
+CONFIG_ALL_PPC=y
+# CONFIG_APUS is not set
+# CONFIG_GEMINI is not set
+# CONFIG_SMP is not set
+CONFIG_ALTIVEC=y
+CONFIG_TAU=y
+# CONFIG_TAU_INT is not set
+# CONFIG_TAU_AVERAGE is not set
+
+#
+# General setup
+#
+# CONFIG_HIGHMEM is not set
+# CONFIG_ISA is not set
+# CONFIG_EISA is not set
+# CONFIG_SBUS is not set
+# CONFIG_MCA is not set
+CONFIG_PCI=y
+CONFIG_NET=y
+CONFIG_SYSCTL=y
+CONFIG_SYSVIPC=y
+# CONFIG_BSD_PROCESS_ACCT is not set
+CONFIG_KCORE_ELF=y
+CONFIG_BINFMT_ELF=y
+CONFIG_KERNEL_ELF=y
+CONFIG_BINFMT_MISC=m
+CONFIG_PCI_NAMES=y
+CONFIG_HOTPLUG=y
+
+#
+# PCMCIA/CardBus support
+#
+CONFIG_PCMCIA=m
+CONFIG_CARDBUS=y
+CONFIG_I82092=y
+CONFIG_I82365=y
+CONFIG_TCIC=y
+
+#
+# Parallel port support
+#
+# CONFIG_PARPORT is not set
+CONFIG_PPC_RTC=y
+CONFIG_PPC601_SYNC_FIX=y
+CONFIG_PROC_DEVICETREE=y
+CONFIG_PPC_RTAS=y
+CONFIG_BOOTX_TEXT=y
+# CONFIG_PREP_RESIDUAL is not set
+# CONFIG_CMDLINE_BOOL is not set
+
+#
+# Memory Technology Devices (MTD)
+#
+# CONFIG_MTD is not set
+
+#
+# Plug and Play configuration
+#
+# CONFIG_PNP is not set
+# CONFIG_ISAPNP is not set
+
+#
+# Block devices
+#
+CONFIG_BLK_DEV_FD=m
+# CONFIG_BLK_DEV_XD is not set
+# CONFIG_PARIDE is not set
+# CONFIG_BLK_CPQ_DA is not set
+# CONFIG_BLK_CPQ_CISS_DA is not set
+# CONFIG_BLK_DEV_DAC960 is not set
+CONFIG_BLK_DEV_LOOP=y
+# CONFIG_BLK_DEV_NBD is not set
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_SIZE=4096
+CONFIG_BLK_DEV_INITRD=y
+
+#
+# Multi-device support (RAID and LVM)
+#
+# CONFIG_MD is not set
+# CONFIG_BLK_DEV_MD is not set
+# CONFIG_MD_LINEAR is not set
+# CONFIG_MD_RAID0 is not set
+# CONFIG_MD_RAID1 is not set
+# CONFIG_MD_RAID5 is not set
+# CONFIG_MD_MULTIPATH is not set
+# CONFIG_BLK_DEV_LVM is not set
+
+#
+# Networking options
+#
+CONFIG_PACKET=y
+# CONFIG_PACKET_MMAP is not set
+# CONFIG_NETLINK_DEV is not set
+CONFIG_NETFILTER=y
+# CONFIG_NETFILTER_DEBUG is not set
+CONFIG_FILTER=y
+CONFIG_UNIX=y
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+# CONFIG_IP_ADVANCED_ROUTER is not set
+# CONFIG_IP_PNP is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_IP_MROUTE is not set
+# CONFIG_ARPD is not set
+# CONFIG_INET_ECN is not set
+CONFIG_SYN_COOKIES=y
+
+#
+#   IP: Netfilter Configuration
+#
+CONFIG_IP_NF_CONNTRACK=m
+CONFIG_IP_NF_FTP=m
+CONFIG_IP_NF_IRC=m
+# CONFIG_IP_NF_QUEUE is not set
+CONFIG_IP_NF_IPTABLES=m
+CONFIG_IP_NF_MATCH_LIMIT=m
+CONFIG_IP_NF_MATCH_MAC=m
+CONFIG_IP_NF_MATCH_MARK=m
+CONFIG_IP_NF_MATCH_MULTIPORT=m
+CONFIG_IP_NF_MATCH_TOS=m
+CONFIG_IP_NF_MATCH_LENGTH=m
+CONFIG_IP_NF_MATCH_TTL=m
+CONFIG_IP_NF_MATCH_TCPMSS=m
+CONFIG_IP_NF_MATCH_STATE=m
+CONFIG_IP_NF_MATCH_UNCLEAN=m
+CONFIG_IP_NF_MATCH_OWNER=m
+CONFIG_IP_NF_FILTER=m
+CONFIG_IP_NF_TARGET_REJECT=m
+CONFIG_IP_NF_TARGET_MIRROR=m
+CONFIG_IP_NF_NAT=m
+CONFIG_IP_NF_NAT_NEEDED=y
+CONFIG_IP_NF_TARGET_MASQUERADE=m
+CONFIG_IP_NF_TARGET_REDIRECT=m
+CONFIG_IP_NF_NAT_SNMP_BASIC=m
+CONFIG_IP_NF_NAT_IRC=m
+CONFIG_IP_NF_NAT_FTP=m
+# CONFIG_IP_NF_MANGLE is not set
+# CONFIG_IP_NF_TARGET_LOG is not set
+CONFIG_IP_NF_TARGET_TCPMSS=m
+CONFIG_IP_NF_COMPAT_IPCHAINS=m
+CONFIG_IP_NF_NAT_NEEDED=y
+# CONFIG_IP_NF_COMPAT_IPFWADM is not set
+# CONFIG_IPV6 is not set
+# CONFIG_KHTTPD is not set
+# CONFIG_ATM is not set
+# CONFIG_VLAN_8021Q is not set
+
+#
+#  
+#
+# CONFIG_IPX is not set
+CONFIG_ATALK=m
+# CONFIG_DECNET is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_LLC is not set
+# CONFIG_NET_DIVERT is not set
+# CONFIG_ECONET is not set
+# CONFIG_WAN_ROUTER is not set
+# CONFIG_NET_FASTROUTE is not set
+# CONFIG_NET_HW_FLOWCONTROL is not set
+
+#
+# QoS and/or fair queueing
+#
+# CONFIG_NET_SCHED is not set
+
+#
+# ATA/IDE/MFM/RLL support
+#
+CONFIG_IDE=y
+
+#
+# IDE, ATA and ATAPI Block devices
+#
+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_HD is not set
+CONFIG_BLK_DEV_IDEDISK=y
+# CONFIG_IDEDISK_MULTI_MODE is not set
+# CONFIG_BLK_DEV_IDEDISK_VENDOR is not set
+# CONFIG_BLK_DEV_IDEDISK_FUJITSU is not set
+# CONFIG_BLK_DEV_IDEDISK_IBM is not set
+# CONFIG_BLK_DEV_IDEDISK_MAXTOR is not set
+# CONFIG_BLK_DEV_IDEDISK_QUANTUM is not set
+# CONFIG_BLK_DEV_IDEDISK_SEAGATE is not set
+# CONFIG_BLK_DEV_IDEDISK_WD is not set
+# CONFIG_BLK_DEV_COMMERIAL is not set
+# CONFIG_BLK_DEV_TIVO is not set
+CONFIG_BLK_DEV_IDECS=m
+CONFIG_BLK_DEV_IDECD=y
+# CONFIG_BLK_DEV_IDETAPE is not set
+CONFIG_BLK_DEV_IDEFLOPPY=y
+CONFIG_BLK_DEV_IDESCSI=y
+
+#
+# IDE chipset support/bugfixes
+#
+# CONFIG_BLK_DEV_CMD640 is not set
+# CONFIG_BLK_DEV_CMD640_ENHANCED is not set
+# CONFIG_BLK_DEV_ISAPNP is not set
+# CONFIG_BLK_DEV_RZ1000 is not set
+CONFIG_BLK_DEV_IDEPCI=y
+CONFIG_IDEPCI_SHARE_IRQ=y
+CONFIG_BLK_DEV_IDEDMA_PCI=y
+CONFIG_BLK_DEV_ADMA=y
+# CONFIG_BLK_DEV_OFFBOARD is not set
+CONFIG_IDEDMA_PCI_AUTO=y
+CONFIG_BLK_DEV_IDEDMA=y
+# CONFIG_IDEDMA_PCI_WIP is not set
+# CONFIG_IDEDMA_NEW_DRIVE_LISTINGS is not set
+# CONFIG_BLK_DEV_AEC62XX is not set
+# CONFIG_AEC62XX_TUNING is not set
+# CONFIG_BLK_DEV_ALI15X3 is not set
+# CONFIG_WDC_ALI15X3 is not set
+# CONFIG_BLK_DEV_AMD74XX is not set
+# CONFIG_AMD74XX_OVERRIDE is not set
+CONFIG_BLK_DEV_CMD64X=y
+# CONFIG_BLK_DEV_CY82C693 is not set
+# CONFIG_BLK_DEV_CS5530 is not set
+# CONFIG_BLK_DEV_HPT34X is not set
+# CONFIG_HPT34X_AUTODMA is not set
+# CONFIG_BLK_DEV_HPT366 is not set
+# CONFIG_BLK_DEV_NS87415 is not set
+# CONFIG_BLK_DEV_OPTI621 is not set
+# CONFIG_BLK_DEV_PDC202XX is not set
+# CONFIG_PDC202XX_BURST is not set
+# CONFIG_PDC202XX_FORCE is not set
+# CONFIG_BLK_DEV_SVWKS is not set
+# CONFIG_BLK_DEV_SIS5513 is not set
+# CONFIG_BLK_DEV_SLC90E66 is not set
+# CONFIG_BLK_DEV_TRM290 is not set
+# CONFIG_BLK_DEV_VIA82CXXX is not set
+CONFIG_BLK_DEV_SL82C105=y
+CONFIG_BLK_DEV_IDE_PMAC=y
+CONFIG_BLK_DEV_IDEDMA_PMAC=y
+CONFIG_BLK_DEV_IDEDMA_PMAC_AUTO=y
+CONFIG_BLK_DEV_IDEDMA=y
+CONFIG_BLK_DEV_IDEPCI=y
+# CONFIG_IDE_CHIPSETS is not set
+CONFIG_IDEDMA_AUTO=y
+# CONFIG_IDEDMA_IVB is not set
+# CONFIG_DMA_NONPCI is not set
+CONFIG_BLK_DEV_IDE_MODES=y
+# CONFIG_BLK_DEV_ATARAID is not set
+# CONFIG_BLK_DEV_ATARAID_PDC is not set
+# CONFIG_BLK_DEV_ATARAID_HPT is not set
+
+#
+# SCSI support
+#
+CONFIG_SCSI=y
+
+#
+# SCSI support type (disk, tape, CD-ROM)
+#
+CONFIG_BLK_DEV_SD=y
+CONFIG_SD_EXTRA_DEVS=40
+CONFIG_CHR_DEV_ST=y
+# CONFIG_CHR_DEV_OSST is not set
+CONFIG_BLK_DEV_SR=y
+CONFIG_BLK_DEV_SR_VENDOR=y
+CONFIG_SR_EXTRA_DEVS=2
+CONFIG_CHR_DEV_SG=y
+
+#
+# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
+#
+# CONFIG_SCSI_DEBUG_QUEUES is not set
+# CONFIG_SCSI_MULTI_LUN is not set
+CONFIG_SCSI_CONSTANTS=y
+# CONFIG_SCSI_LOGGING is not set
+
+#
+# SCSI low-level drivers
+#
+# CONFIG_BLK_DEV_3W_XXXX_RAID is not set
+# CONFIG_SCSI_7000FASST is not set
+# CONFIG_SCSI_ACARD 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_CMDS_PER_DEVICE=253
+CONFIG_AIC7XXX_RESET_DELAY_MS=15000
+# CONFIG_AIC7XXX_BUILD_FIRMWARE is not set
+CONFIG_SCSI_AIC7XXX_OLD=m
+# CONFIG_AIC7XXX_OLD_TCQ_ON_BY_DEFAULT is not set
+CONFIG_AIC7XXX_OLD_CMDS_PER_DEVICE=8
+CONFIG_AIC7XXX_OLD_PROC_STATS=y
+# CONFIG_SCSI_DPT_I2O is not set
+CONFIG_SCSI_ADVANSYS=m
+# CONFIG_SCSI_IN2000 is not set
+# CONFIG_SCSI_AM53C974 is not set
+# CONFIG_SCSI_MEGARAID is not set
+# CONFIG_SCSI_BUSLOGIC is not set
+# CONFIG_SCSI_CPQFCTS is not set
+# CONFIG_SCSI_DMX3191D is not set
+# CONFIG_SCSI_DTC3280 is not set
+# CONFIG_SCSI_EATA is not set
+# CONFIG_SCSI_EATA_DMA is not set
+# CONFIG_SCSI_EATA_PIO 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_INITIO is not set
+# CONFIG_SCSI_INIA100 is not set
+# CONFIG_SCSI_NCR53C406A is not set
+# CONFIG_SCSI_NCR53C7xx is not set
+CONFIG_SCSI_SYM53C8XX_2=y
+CONFIG_SCSI_SYM53C8XX_DMA_ADDRESSING_MODE=0
+CONFIG_SCSI_SYM53C8XX_DEFAULT_TAGS=16
+CONFIG_SCSI_SYM53C8XX_MAX_TAGS=64
+# CONFIG_SCSI_SYM53C8XX_IOMAPPED 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_QLOGIC_FC is not set
+# CONFIG_SCSI_QLOGIC_1280 is not set
+# CONFIG_SCSI_SIM710 is not set
+# CONFIG_SCSI_SYM53C416 is not set
+# CONFIG_SCSI_DC390T is not set
+# CONFIG_SCSI_T128 is not set
+# CONFIG_SCSI_U14_34F is not set
+# CONFIG_SCSI_DEBUG is not set
+CONFIG_SCSI_MESH=y
+CONFIG_SCSI_MESH_SYNC_RATE=5
+CONFIG_SCSI_MAC53C94=y
+
+#
+# PCMCIA SCSI adapter support
+#
+# CONFIG_SCSI_PCMCIA is not set
+
+#
+# IEEE 1394 (FireWire) support (EXPERIMENTAL)
+#
+CONFIG_IEEE1394=m
+
+#
+# Device Drivers
+#
+CONFIG_IEEE1394_PCILYNX=m
+# CONFIG_IEEE1394_PCILYNX_LOCALRAM is not set
+# CONFIG_IEEE1394_PCILYNX_PORTS is not set
+CONFIG_IEEE1394_OHCI1394=m
+
+#
+# Protocol Drivers
+#
+CONFIG_IEEE1394_VIDEO1394=m
+CONFIG_IEEE1394_SBP2=m
+CONFIG_IEEE1394_RAWIO=m
+# CONFIG_IEEE1394_VERBOSEDEBUG is not set
+
+#
+# Network device support
+#
+CONFIG_NETDEVICES=y
+
+#
+# ARCnet devices
+#
+# CONFIG_ARCNET is not set
+
+#
+# Appletalk devices
+#
+# CONFIG_LTPC is not set
+# CONFIG_COPS is not set
+# CONFIG_IPDDP is not set
+# CONFIG_DUMMY is not set
+# CONFIG_BONDING is not set
+# CONFIG_EQUALIZER is not set
+# CONFIG_TUN is not set
+# CONFIG_ETHERTAP is not set
+
+#
+# Ethernet (10 or 100Mbit)
+#
+CONFIG_NET_ETHERNET=y
+CONFIG_MACE=y
+# CONFIG_MACE_AAUI_PORT is not set
+CONFIG_BMAC=y
+# CONFIG_GMAC is not set
+# CONFIG_SUNLANCE is not set
+# CONFIG_HAPPYMEAL is not set
+# CONFIG_SUNBMAC is not set
+# CONFIG_SUNQE is not set
+# CONFIG_SUNLANCE is not set
+CONFIG_SUNGEM=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_HP100 is not set
+# CONFIG_NET_ISA is not set
+CONFIG_NET_PCI=y
+CONFIG_PCNET32=y
+# CONFIG_ADAPTEC_STARFIRE is not set
+# CONFIG_APRICOT is not set
+# CONFIG_CS89x0 is not set
+CONFIG_TULIP=y
+# CONFIG_TULIP_MWI is not set
+CONFIG_TULIP_MMIO=y
+CONFIG_DE4X5=m
+# CONFIG_DGRS is not set
+# CONFIG_DM9102 is not set
+# CONFIG_EEPRO100 is not set
+# CONFIG_LNE390 is not set
+# CONFIG_FEALNX is not set
+# CONFIG_NATSEMI is not set
+# CONFIG_NE2K_PCI is not set
+# CONFIG_NE3210 is not set
+# CONFIG_ES3210 is not set
+# CONFIG_8139CP is not set
+# CONFIG_8139TOO is not set
+# CONFIG_8139TOO_PIO is not set
+# CONFIG_8139TOO_TUNE_TWISTER is not set
+# CONFIG_8139TOO_8129 is not set
+# CONFIG_SIS900 is not set
+# CONFIG_EPIC100 is not set
+# CONFIG_SUNDANCE is not set
+# CONFIG_TLAN is not set
+# CONFIG_VIA_RHINE is not set
+# CONFIG_VIA_RHINE_MMIO is not set
+# CONFIG_WINBOND_840 is not set
+# CONFIG_NET_POCKET is not set
+
+#
+# Ethernet (1000 Mbit)
+#
+# CONFIG_ACENIC is not set
+# CONFIG_DL2K is not set
+# CONFIG_MYRI_SBUS is not set
+# CONFIG_NS83820 is not set
+# CONFIG_HAMACHI is not set
+# CONFIG_YELLOWFIN is not set
+# CONFIG_SK98LIN is not set
+# CONFIG_FDDI is not set
+# CONFIG_HIPPI is not set
+# CONFIG_PLIP is not set
+CONFIG_PPP=y
+CONFIG_PPP_MULTILINK=y
+# CONFIG_PPP_FILTER is not set
+CONFIG_PPP_ASYNC=y
+CONFIG_PPP_SYNC_TTY=m
+CONFIG_PPP_DEFLATE=y
+CONFIG_PPP_BSDCOMP=m
+# CONFIG_PPPOE is not set
+# CONFIG_SLIP is not set
+
+#
+# Wireless LAN (non-hamradio)
+#
+CONFIG_NET_RADIO=y
+# CONFIG_STRIP is not set
+# CONFIG_WAVELAN is not set
+# CONFIG_ARLAN is not set
+# CONFIG_AIRONET4500 is not set
+# CONFIG_AIRONET4500_NONCS is not set
+# CONFIG_AIRONET4500_PROC is not set
+# CONFIG_AIRO is not set
+CONFIG_HERMES=m
+CONFIG_APPLE_AIRPORT=m
+# CONFIG_PLX_HERMES is not set
+
+#
+# Wireless Pcmcia cards support
+#
+CONFIG_PCMCIA_HERMES=m
+# CONFIG_AIRO_CS is not set
+CONFIG_NET_WIRELESS=y
+
+#
+# Token Ring devices
+#
+# CONFIG_TR is not set
+# CONFIG_NET_FC is not set
+# CONFIG_RCPCI is not set
+# CONFIG_SHAPER is not set
+
+#
+# Wan interfaces
+#
+# CONFIG_WAN is not set
+
+#
+# PCMCIA network device support
+#
+CONFIG_NET_PCMCIA=y
+# CONFIG_PCMCIA_3C589 is not set
+# CONFIG_PCMCIA_3C574 is not set
+# CONFIG_PCMCIA_FMVJ18X is not set
+# CONFIG_PCMCIA_PCNET is not set
+# CONFIG_PCMCIA_AXNET is not set
+# CONFIG_PCMCIA_NMCLAN is not set
+# CONFIG_PCMCIA_SMC91C92 is not set
+# CONFIG_PCMCIA_XIRC2PS is not set
+# CONFIG_ARCNET_COM20020_CS is not set
+# CONFIG_PCMCIA_IBMTR is not set
+# CONFIG_PCMCIA_XIRCOM is not set
+# CONFIG_PCMCIA_XIRTULIP is not set
+CONFIG_NET_PCMCIA_RADIO=y
+# CONFIG_PCMCIA_RAYCS is not set
+# CONFIG_PCMCIA_NETWAVE is not set
+# CONFIG_PCMCIA_WAVELAN is not set
+# CONFIG_AIRONET4500_CS is not set
+
+#
+# Amateur Radio support
+#
+# CONFIG_HAMRADIO is not set
+
+#
+# IrDA (infrared) support
+#
+CONFIG_IRDA=m
+
+#
+# IrDA protocols
+#
+CONFIG_IRLAN=m
+CONFIG_IRNET=m
+CONFIG_IRCOMM=m
+# CONFIG_IRDA_ULTRA is not set
+# CONFIG_IRDA_OPTIONS is not set
+
+#
+# Infrared-port device drivers
+#
+
+#
+# SIR device drivers
+#
+CONFIG_IRTTY_SIR=m
+# CONFIG_IRPORT_SIR is not set
+
+#
+# Dongle support
+#
+# CONFIG_DONGLE is not set
+
+#
+# FIR device drivers
+#
+# CONFIG_USB_IRDA is not set
+# CONFIG_NSC_FIR is not set
+# CONFIG_WINBOND_FIR is not set
+# CONFIG_TOSHIBA_FIR is not set
+# CONFIG_SMC_IRCC_FIR is not set
+# CONFIG_ALI_FIR is not set
+# CONFIG_VLSI_FIR is not set
+
+#
+# ISDN subsystem
+#
+# CONFIG_ISDN is not set
+
+#
+# Old CD-ROM drivers (not SCSI, not IDE)
+#
+# CONFIG_CD_NO_IDESCSI is not set
+
+#
+# Console drivers
+#
+# CONFIG_VGA_CONSOLE is not set
+
+#
+# Frame-buffer support
+#
+CONFIG_FB=y
+CONFIG_DUMMY_CONSOLE=y
+# CONFIG_FB_RIVA is not set
+# CONFIG_FB_CLGEN is not set
+# CONFIG_FB_PM2 is not set
+# CONFIG_FB_CYBER2000 is not set
+CONFIG_FB_OF=y
+CONFIG_FB_CONTROL=y
+CONFIG_FB_PLATINUM=y
+CONFIG_FB_VALKYRIE=y
+CONFIG_FB_CT65550=y
+CONFIG_FB_IMSTT=y
+# CONFIG_FB_S3TRIO is not set
+# CONFIG_FB_VGA16 is not set
+CONFIG_FB_MATROX=y
+CONFIG_FB_MATROX_MILLENIUM=y
+CONFIG_FB_MATROX_MYSTIQUE=y
+# CONFIG_FB_MATROX_G100 is not set
+# CONFIG_FB_MATROX_I2C is not set
+# CONFIG_FB_MATROX_G450 is not set
+# CONFIG_FB_MATROX_MULTIHEAD is not set
+CONFIG_FB_ATY=y
+CONFIG_FB_ATY_GX=y
+CONFIG_FB_ATY_CT=y
+CONFIG_FB_RADEON=y
+CONFIG_FB_ATY128=y
+# CONFIG_FB_SIS is not set
+CONFIG_FB_3DFX=y
+# CONFIG_FB_VOODOO1 is not set
+# CONFIG_FB_VIRTUAL is not set
+# CONFIG_FBCON_ADVANCED is not set
+CONFIG_FBCON_CFB8=y
+CONFIG_FBCON_CFB16=y
+CONFIG_FBCON_CFB24=y
+CONFIG_FBCON_CFB32=y
+# CONFIG_FBCON_FONTWIDTH8_ONLY is not set
+CONFIG_FBCON_FONTS=y
+# CONFIG_FONT_8x8 is not set
+CONFIG_FONT_8x16=y
+CONFIG_FONT_SUN8x16=y
+CONFIG_FONT_SUN12x22=y
+# CONFIG_FONT_6x11 is not set
+# CONFIG_FONT_PEARL_8x8 is not set
+# CONFIG_FONT_ACORN_8x8 is not set
+CONFIG_FB_COMPAT_XPMAC=y
+
+#
+# Input core support
+#
+CONFIG_INPUT=y
+CONFIG_INPUT_KEYBDEV=y
+CONFIG_INPUT_MOUSEDEV=y
+CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024
+CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768
+# CONFIG_INPUT_JOYDEV is not set
+CONFIG_INPUT_EVDEV=y
+
+#
+# Macintosh device drivers
+#
+CONFIG_ADB_CUDA=y
+CONFIG_ADB_PMU=y
+CONFIG_PMAC_PBOOK=y
+CONFIG_PM=y
+CONFIG_PMAC_APM_EMU=y
+CONFIG_PMAC_BACKLIGHT=y
+CONFIG_MAC_FLOPPY=y
+CONFIG_MAC_SERIAL=y
+# CONFIG_SERIAL_CONSOLE is not set
+CONFIG_ADB=y
+CONFIG_ADB_MACIO=y
+CONFIG_INPUT_ADBHID=y
+CONFIG_MAC_ADBKEYCODES=y
+CONFIG_MAC_EMUMOUSEBTN=y
+CONFIG_MAC_HID=y
+# CONFIG_ANSLCD is not set
+
+#
+# Character devices
+#
+CONFIG_VT=y
+CONFIG_VT_CONSOLE=y
+CONFIG_SERIAL=m
+# CONFIG_SERIAL_EXTENDED is not set
+# CONFIG_SERIAL_NONSTANDARD is not set
+CONFIG_UNIX98_PTYS=y
+CONFIG_UNIX98_PTY_COUNT=256
+
+#
+# I2C support
+#
+CONFIG_I2C=m
+# CONFIG_I2C_ALGOBIT is not set
+# CONFIG_I2C_ALGOPCF is not set
+CONFIG_I2C_KEYWEST=m
+CONFIG_I2C_CHARDEV=m
+CONFIG_I2C_PROC=m
+
+#
+# Mice
+#
+CONFIG_BUSMOUSE=y
+# CONFIG_ATIXL_BUSMOUSE is not set
+# CONFIG_LOGIBUSMOUSE is not set
+# CONFIG_MS_BUSMOUSE is not set
+CONFIG_MOUSE=y
+CONFIG_PSMOUSE=y
+# CONFIG_82C710_MOUSE is not set
+# CONFIG_PC110_PAD is not set
+
+#
+# Joysticks
+#
+# CONFIG_INPUT_GAMEPORT is not set
+# CONFIG_INPUT_NS558 is not set
+# CONFIG_INPUT_LIGHTNING is not set
+# CONFIG_INPUT_PCIGAME is not set
+# CONFIG_INPUT_CS461X is not set
+# CONFIG_INPUT_EMU10K1 is not set
+# CONFIG_INPUT_SERIO is not set
+# CONFIG_INPUT_SERPORT is not set
+
+#
+# Joysticks
+#
+# CONFIG_INPUT_ANALOG is not set
+# CONFIG_INPUT_A3D is not set
+# CONFIG_INPUT_ADI is not set
+# CONFIG_INPUT_COBRA is not set
+# CONFIG_INPUT_GF2K is not set
+# CONFIG_INPUT_GRIP is not set
+# CONFIG_INPUT_INTERACT is not set
+# CONFIG_INPUT_TMDC is not set
+# CONFIG_INPUT_SIDEWINDER is not set
+# CONFIG_INPUT_IFORCE_USB is not set
+# CONFIG_INPUT_IFORCE_232 is not set
+# CONFIG_INPUT_WARRIOR is not set
+# CONFIG_INPUT_MAGELLAN is not set
+# CONFIG_INPUT_SPACEORB is not set
+# CONFIG_INPUT_SPACEBALL is not set
+# CONFIG_INPUT_STINGER is not set
+# CONFIG_INPUT_DB9 is not set
+# CONFIG_INPUT_GAMECON is not set
+# CONFIG_INPUT_TURBOGRAFX is not set
+# CONFIG_QIC02_TAPE is not set
+
+#
+# Watchdog Cards
+#
+# CONFIG_WATCHDOG is not set
+# CONFIG_INTEL_RNG is not set
+CONFIG_NVRAM=y
+# CONFIG_RTC is not set
+# CONFIG_DTLK is not set
+# CONFIG_R3964 is not set
+# CONFIG_APPLICOM is not set
+
+#
+# Ftape, the floppy tape device driver
+#
+# CONFIG_FTAPE is not set
+# CONFIG_AGP is not set
+# CONFIG_DRM is not set
+
+#
+# PCMCIA character devices
+#
+# CONFIG_PCMCIA_SERIAL_CS is not set
+
+#
+# Multimedia devices
+#
+# CONFIG_VIDEO_DEV is not set
+
+#
+# File systems
+#
+# CONFIG_QUOTA is not set
+# CONFIG_AUTOFS_FS is not set
+# CONFIG_AUTOFS4_FS is not set
+# CONFIG_REISERFS_FS is not set
+# CONFIG_REISERFS_CHECK is not set
+# CONFIG_REISERFS_PROC_INFO is not set
+# CONFIG_ADFS_FS is not set
+# CONFIG_ADFS_FS_RW is not set
+# CONFIG_AFFS_FS is not set
+CONFIG_HFS_FS=m
+# CONFIG_BFS_FS is not set
+CONFIG_EXT3_FS=y
+CONFIG_JBD=y
+# CONFIG_JBD_DEBUG is not set
+CONFIG_FAT_FS=m
+CONFIG_MSDOS_FS=m
+# CONFIG_UMSDOS_FS is not set
+CONFIG_VFAT_FS=m
+# CONFIG_EFS_FS is not set
+# CONFIG_JFFS_FS is not set
+# CONFIG_JFFS2_FS is not set
+# CONFIG_CRAMFS is not set
+CONFIG_TMPFS=y
+# CONFIG_RAMFS is not set
+CONFIG_ISO9660_FS=y
+# CONFIG_JOLIET is not set
+# CONFIG_ZISOFS is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_VXFS_FS is not set
+# CONFIG_NTFS_FS is not set
+# CONFIG_NTFS_RW is not set
+# CONFIG_HPFS_FS is not set
+CONFIG_PROC_FS=y
+CONFIG_DEVFS_FS=y
+# CONFIG_DEVFS_MOUNT is not set
+# CONFIG_DEVFS_DEBUG is not set
+CONFIG_DEVPTS_FS=y
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_QNX4FS_RW is not set
+# CONFIG_ROMFS_FS is not set
+CONFIG_EXT2_FS=y
+# CONFIG_SYSV_FS is not set
+# CONFIG_UDF_FS is not set
+# CONFIG_UDF_RW is not set
+# CONFIG_UFS_FS is not set
+# CONFIG_UFS_FS_WRITE is not set
+
+#
+# Network File Systems
+#
+# CONFIG_CODA_FS is not set
+# CONFIG_INTERMEZZO_FS is not set
+CONFIG_NFS_FS=y
+# CONFIG_NFS_V3 is not set
+# CONFIG_ROOT_NFS is not set
+CONFIG_NFSD=y
+# CONFIG_NFSD_V3 is not set
+CONFIG_SUNRPC=y
+CONFIG_LOCKD=y
+CONFIG_SMB_FS=m
+# CONFIG_SMB_NLS_DEFAULT is not set
+# CONFIG_NCP_FS is not set
+# 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_SMALLDOS is not set
+# CONFIG_NCPFS_NLS is not set
+# CONFIG_NCPFS_EXTRAS is not set
+# CONFIG_ZISOFS_FS is not set
+# CONFIG_ZLIB_FS_INFLATE is not set
+
+#
+# Partition Types
+#
+CONFIG_PARTITION_ADVANCED=y
+# CONFIG_ACORN_PARTITION is not set
+# CONFIG_OSF_PARTITION is not set
+# CONFIG_AMIGA_PARTITION is not set
+# CONFIG_ATARI_PARTITION is not set
+CONFIG_MAC_PARTITION=y
+CONFIG_MSDOS_PARTITION=y
+# CONFIG_BSD_DISKLABEL is not set
+# CONFIG_MINIX_SUBPARTITION is not set
+# CONFIG_SOLARIS_X86_PARTITION is not set
+# CONFIG_UNIXWARE_DISKLABEL is not set
+# CONFIG_LDM_PARTITION is not set
+# CONFIG_SGI_PARTITION is not set
+# CONFIG_ULTRIX_PARTITION is not set
+# CONFIG_SUN_PARTITION is not set
+CONFIG_SMB_NLS=y
+CONFIG_NLS=y
+
+#
+# Native Language Support
+#
+CONFIG_NLS_DEFAULT="iso8859-1"
+# 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_936 is not set
+# CONFIG_NLS_CODEPAGE_950 is not set
+# CONFIG_NLS_CODEPAGE_932 is not set
+# CONFIG_NLS_CODEPAGE_949 is not set
+# CONFIG_NLS_CODEPAGE_874 is not set
+# CONFIG_NLS_ISO8859_8 is not set
+# CONFIG_NLS_CODEPAGE_1251 is not set
+CONFIG_NLS_ISO8859_1=m
+# 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_9 is not set
+# CONFIG_NLS_ISO8859_13 is not set
+# CONFIG_NLS_ISO8859_14 is not set
+# CONFIG_NLS_ISO8859_15 is not set
+# CONFIG_NLS_KOI8_R is not set
+# CONFIG_NLS_KOI8_U is not set
+# CONFIG_NLS_UTF8 is not set
+
+#
+# Sound
+#
+CONFIG_SOUND=m
+CONFIG_DMASOUND_PMAC=m
+CONFIG_DMASOUND=m
+CONFIG_I2C=m
+CONFIG_I2C_KEYWEST=m
+# CONFIG_SOUND_BT878 is not set
+# CONFIG_SOUND_CMPCI is not set
+# CONFIG_SOUND_EMU10K1 is not set
+# CONFIG_MIDI_EMU10K1 is not set
+# CONFIG_SOUND_FUSION is not set
+# CONFIG_SOUND_CS4281 is not set
+# CONFIG_SOUND_ES1370 is not set
+# CONFIG_SOUND_ES1371 is not set
+# CONFIG_SOUND_ESSSOLO1 is not set
+# CONFIG_SOUND_MAESTRO is not set
+# CONFIG_SOUND_MAESTRO3 is not set
+# CONFIG_SOUND_ICH is not set
+# CONFIG_SOUND_RME96XX is not set
+# CONFIG_SOUND_SONICVIBES is not set
+# CONFIG_SOUND_TRIDENT is not set
+# CONFIG_SOUND_MSNDCLAS is not set
+# CONFIG_SOUND_MSNDPIN is not set
+# CONFIG_SOUND_VIA82CXXX is not set
+# CONFIG_MIDI_VIA82CXXX is not set
+# CONFIG_SOUND_OSS is not set
+# CONFIG_SOUND_TVMIXER is not set
+
+#
+# USB support
+#
+CONFIG_USB=y
+# CONFIG_USB_DEBUG is not set
+
+#
+# Miscellaneous USB options
+#
+CONFIG_USB_DEVICEFS=y
+# CONFIG_USB_BANDWIDTH is not set
+# CONFIG_USB_LONG_TIMEOUT is not set
+
+#
+# USB Controllers
+#
+# CONFIG_USB_UHCI is not set
+# CONFIG_USB_UHCI_ALT is not set
+CONFIG_USB_OHCI=y
+
+#
+# USB Device Class drivers
+#
+# CONFIG_USB_AUDIO is not set
+# CONFIG_USB_BLUETOOTH is not set
+# CONFIG_USB_STORAGE is not set
+# CONFIG_USB_STORAGE_DEBUG is not set
+# CONFIG_USB_STORAGE_DATAFAB is not set
+# CONFIG_USB_STORAGE_FREECOM is not set
+# CONFIG_USB_STORAGE_ISD200 is not set
+# CONFIG_USB_STORAGE_DPCM is not set
+# CONFIG_USB_STORAGE_HP8200e is not set
+# CONFIG_USB_STORAGE_SDDR09 is not set
+# CONFIG_USB_STORAGE_JUMPSHOT is not set
+CONFIG_USB_ACM=m
+CONFIG_USB_PRINTER=m
+
+#
+# USB Human Interface Devices (HID)
+#
+CONFIG_USB_HID=y
+# CONFIG_USB_HIDDEV is not set
+# CONFIG_USB_WACOM is not set
+
+#
+# USB Imaging devices
+#
+# CONFIG_USB_DC2XX is not set
+# CONFIG_USB_MDC800 is not set
+CONFIG_USB_SCANNER=m
+# CONFIG_USB_MICROTEK is not set
+# CONFIG_USB_HPUSBSCSI is not set
+
+#
+# USB Multimedia devices
+#
+
+#
+#   Video4Linux support is needed for USB Multimedia device support
+#
+
+#
+# USB Network adaptors
+#
+# CONFIG_USB_PEGASUS is not set
+# CONFIG_USB_KAWETH is not set
+# CONFIG_USB_CATC is not set
+# CONFIG_USB_CDCETHER is not set
+# CONFIG_USB_USBNET is not set
+
+#
+# USB port drivers
+#
+# CONFIG_USB_USS720 is not set
+
+#
+# USB Serial Converter support
+#
+CONFIG_USB_SERIAL=m
+# CONFIG_USB_SERIAL_GENERIC is not set
+# CONFIG_USB_SERIAL_BELKIN is not set
+# CONFIG_USB_SERIAL_WHITEHEAT is not set
+# CONFIG_USB_SERIAL_DIGI_ACCELEPORT is not set
+# CONFIG_USB_SERIAL_EMPEG is not set
+# CONFIG_USB_SERIAL_FTDI_SIO is not set
+CONFIG_USB_SERIAL_VISOR=m
+# CONFIG_USB_SERIAL_IR is not set
+# CONFIG_USB_SERIAL_EDGEPORT is not set
+# CONFIG_USB_SERIAL_KEYSPAN_PDA is not set
+# CONFIG_USB_SERIAL_KEYSPAN is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA28 is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA28X is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA28XA is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA28XB is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA19 is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA18X is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA19W is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA49W is not set
+# CONFIG_USB_SERIAL_MCT_U232 is not set
+# CONFIG_USB_SERIAL_PL2303 is not set
+# CONFIG_USB_SERIAL_CYBERJACK is not set
+# CONFIG_USB_SERIAL_XIRCOM is not set
+# CONFIG_USB_SERIAL_OMNINET is not set
+
+#
+# USB Miscellaneous drivers
+#
+# CONFIG_USB_RIO500 is not set
+
+#
+# Bluetooth support
+#
+# CONFIG_BLUEZ is not set
+
+#
+# Kernel hacking
+#
+CONFIG_MAGIC_SYSRQ=y
+# CONFIG_KGDB is not set
+CONFIG_XMON=y
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/configs/power3_defconfig linux/arch/ppc/configs/power3_defconfig
--- linux.orig/arch/ppc/configs/power3_defconfig	Mon Jan  7 23:41:08 2002
+++ linux/arch/ppc/configs/power3_defconfig	Wed Dec 26 16:28:34 2001
@@ -4,6 +4,7 @@
 # CONFIG_UID16 is not set
 # CONFIG_RWSEM_GENERIC_SPINLOCK is not set
 CONFIG_RWSEM_XCHGADD_ALGORITHM=y
+CONFIG_HAVE_DEC_LOCK=y
 
 #
 # Code maturity level options
@@ -66,6 +67,7 @@
 # CONFIG_PARPORT_AMIGA is not set
 # CONFIG_PARPORT_MFC3 is not set
 # CONFIG_PARPORT_ATARI is not set
+# CONFIG_PARPORT_GSC is not set
 # CONFIG_PARPORT_SUNBPP is not set
 # CONFIG_PARPORT_OTHER is not set
 # CONFIG_PARPORT_1284 is not set
@@ -119,8 +121,6 @@
 #
 CONFIG_PACKET=y
 # CONFIG_PACKET_MMAP is not set
-CONFIG_NETLINK=y
-# CONFIG_RTNETLINK is not set
 # CONFIG_NETLINK_DEV is not set
 # CONFIG_NETFILTER is not set
 # CONFIG_FILTER is not set
@@ -132,6 +132,7 @@
 # CONFIG_NET_IPIP is not set
 # CONFIG_NET_IPGRE is not set
 # CONFIG_IP_MROUTE is not set
+# CONFIG_ARPD is not set
 # CONFIG_INET_ECN is not set
 CONFIG_SYN_COOKIES=y
 # CONFIG_IPV6 is not set
@@ -224,15 +225,11 @@
 # CONFIG_SCSI_IMM is not set
 # CONFIG_SCSI_NCR53C406A is not set
 # CONFIG_SCSI_NCR53C7xx is not set
-# CONFIG_SCSI_NCR53C8XX is not set
-CONFIG_SCSI_SYM53C8XX=y
-CONFIG_SCSI_NCR53C8XX_DEFAULT_TAGS=8
-CONFIG_SCSI_NCR53C8XX_MAX_TAGS=32
-CONFIG_SCSI_NCR53C8XX_SYNC=20
-# CONFIG_SCSI_NCR53C8XX_PROFILE is not set
-# CONFIG_SCSI_NCR53C8XX_IOMAPPED is not set
-# CONFIG_SCSI_NCR53C8XX_PQS_PDS is not set
-# CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT is not set
+CONFIG_SCSI_SYM53C8XX_2=y
+CONFIG_SCSI_SYM53C8XX_DMA_ADDRESSING_MODE=0
+CONFIG_SCSI_SYM53C8XX_DEFAULT_TAGS=16
+CONFIG_SCSI_SYM53C8XX_MAX_TAGS=64
+# CONFIG_SCSI_SYM53C8XX_IOMAPPED is not set
 # CONFIG_SCSI_PAS16 is not set
 # CONFIG_SCSI_PCI2000 is not set
 # CONFIG_SCSI_PCI2220I is not set
@@ -315,6 +312,7 @@
 # CONFIG_SUNDANCE is not set
 # CONFIG_TLAN is not set
 # CONFIG_VIA_RHINE is not set
+# CONFIG_VIA_RHINE_MMIO is not set
 # CONFIG_WINBOND_840 is not set
 # CONFIG_NET_POCKET is not set
 
@@ -474,6 +472,7 @@
 # CONFIG_I2C_VELLEMAN is not set
 CONFIG_I2C_ALGOPCF=y
 # CONFIG_I2C_ELEKTOR is not set
+# CONFIG_I2C_KEYWEST is not set
 CONFIG_I2C_CHARDEV=y
 # CONFIG_I2C_PROC is not set
 
@@ -553,11 +552,15 @@
 # CONFIG_AUTOFS4_FS is not set
 # CONFIG_REISERFS_FS is not set
 # CONFIG_REISERFS_CHECK is not set
+# CONFIG_REISERFS_PROC_INFO is not set
 # CONFIG_ADFS_FS is not set
 # CONFIG_ADFS_FS_RW is not set
 # CONFIG_AFFS_FS is not set
 # CONFIG_HFS_FS is not set
 # CONFIG_BFS_FS is not set
+# CONFIG_EXT3_FS is not set
+# CONFIG_JBD is not set
+# CONFIG_JBD_DEBUG is not set
 CONFIG_FAT_FS=y
 CONFIG_MSDOS_FS=y
 # CONFIG_UMSDOS_FS is not set
@@ -570,6 +573,7 @@
 # CONFIG_RAMFS is not set
 CONFIG_ISO9660_FS=y
 CONFIG_JOLIET=y
+# CONFIG_ZISOFS is not set
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
@@ -594,6 +598,7 @@
 # Network File Systems
 #
 # CONFIG_CODA_FS is not set
+# CONFIG_INTERMEZZO_FS is not set
 CONFIG_NFS_FS=y
 # CONFIG_NFS_V3 is not set
 # CONFIG_ROOT_NFS is not set
@@ -611,6 +616,8 @@
 # CONFIG_NCPFS_SMALLDOS is not set
 # CONFIG_NCPFS_NLS is not set
 # CONFIG_NCPFS_EXTRAS is not set
+# CONFIG_ZISOFS_FS is not set
+# CONFIG_ZLIB_FS_INFLATE is not set
 
 #
 # Partition Types
@@ -665,7 +672,7 @@
 # Sound
 #
 CONFIG_SOUND=y
-# CONFIG_DMASOUND_AWACS is not set
+# CONFIG_DMASOUND_PMAC is not set
 # CONFIG_SOUND_BT878 is not set
 # CONFIG_SOUND_CMPCI is not set
 # CONFIG_SOUND_EMU10K1 is not set
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/defconfig linux/arch/ppc/defconfig
--- linux.orig/arch/ppc/defconfig	Mon Jan  7 23:41:08 2002
+++ linux/arch/ppc/defconfig	Wed Dec 26 16:28:34 2001
@@ -4,6 +4,7 @@
 # CONFIG_UID16 is not set
 # CONFIG_RWSEM_GENERIC_SPINLOCK is not set
 CONFIG_RWSEM_XCHGADD_ALGORITHM=y
+CONFIG_HAVE_DEC_LOCK=y
 
 #
 # Code maturity level options
@@ -119,8 +120,6 @@
 #
 CONFIG_PACKET=y
 # CONFIG_PACKET_MMAP is not set
-CONFIG_NETLINK=y
-# CONFIG_RTNETLINK is not set
 # CONFIG_NETLINK_DEV is not set
 CONFIG_NETFILTER=y
 # CONFIG_NETFILTER_DEBUG is not set
@@ -133,6 +132,7 @@
 # CONFIG_NET_IPIP is not set
 # CONFIG_NET_IPGRE is not set
 # CONFIG_IP_MROUTE is not set
+# CONFIG_ARPD is not set
 # CONFIG_INET_ECN is not set
 CONFIG_SYN_COOKIES=y
 
@@ -343,15 +343,11 @@
 # CONFIG_SCSI_INIA100 is not set
 # CONFIG_SCSI_NCR53C406A is not set
 # CONFIG_SCSI_NCR53C7xx is not set
-CONFIG_SCSI_NCR53C8XX=y
-CONFIG_SCSI_SYM53C8XX=y
-CONFIG_SCSI_NCR53C8XX_DEFAULT_TAGS=8
-CONFIG_SCSI_NCR53C8XX_MAX_TAGS=32
-CONFIG_SCSI_NCR53C8XX_SYNC=20
-# CONFIG_SCSI_NCR53C8XX_PROFILE is not set
-# CONFIG_SCSI_NCR53C8XX_IOMAPPED is not set
-# CONFIG_SCSI_NCR53C8XX_PQS_PDS is not set
-# CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT is not set
+CONFIG_SCSI_SYM53C8XX_2=y
+CONFIG_SCSI_SYM53C8XX_DMA_ADDRESSING_MODE=0
+CONFIG_SCSI_SYM53C8XX_DEFAULT_TAGS=16
+CONFIG_SCSI_SYM53C8XX_MAX_TAGS=64
+# CONFIG_SCSI_SYM53C8XX_IOMAPPED is not set
 # CONFIG_SCSI_PAS16 is not set
 # CONFIG_SCSI_PCI2000 is not set
 # CONFIG_SCSI_PCI2220I is not set
@@ -388,7 +384,9 @@
 #
 # Appletalk devices
 #
-# CONFIG_APPLETALK is not set
+# CONFIG_LTPC is not set
+# CONFIG_COPS is not set
+# CONFIG_IPDDP is not set
 # CONFIG_DUMMY is not set
 # CONFIG_BONDING is not set
 # CONFIG_EQUALIZER is not set
@@ -443,6 +441,7 @@
 # CONFIG_SUNDANCE is not set
 # CONFIG_TLAN is not set
 # CONFIG_VIA_RHINE is not set
+# CONFIG_VIA_RHINE_MMIO is not set
 # CONFIG_WINBOND_840 is not set
 # CONFIG_NET_POCKET is not set
 
@@ -544,6 +543,7 @@
 CONFIG_FB_MATROX_MILLENIUM=y
 CONFIG_FB_MATROX_MYSTIQUE=y
 # CONFIG_FB_MATROX_G100 is not set
+# CONFIG_FB_MATROX_I2C is not set
 # CONFIG_FB_MATROX_G450 is not set
 # CONFIG_FB_MATROX_MULTIHEAD is not set
 CONFIG_FB_ATY=y
@@ -597,6 +597,7 @@
 CONFIG_MAC_ADBKEYCODES=y
 CONFIG_MAC_EMUMOUSEBTN=y
 CONFIG_MAC_HID=y
+# CONFIG_ANSLCD is not set
 
 #
 # Character devices
@@ -612,7 +613,12 @@
 #
 # I2C support
 #
-# CONFIG_I2C is not set
+CONFIG_I2C=m
+# CONFIG_I2C_ALGOBIT is not set
+# CONFIG_I2C_ALGOPCF is not set
+CONFIG_I2C_KEYWEST=m
+CONFIG_I2C_CHARDEV=m
+CONFIG_I2C_PROC=m
 
 #
 # Mice
@@ -693,11 +699,15 @@
 # CONFIG_AUTOFS4_FS is not set
 # CONFIG_REISERFS_FS is not set
 # CONFIG_REISERFS_CHECK is not set
+# CONFIG_REISERFS_PROC_INFO is not set
 # CONFIG_ADFS_FS is not set
 # CONFIG_ADFS_FS_RW is not set
 # CONFIG_AFFS_FS is not set
 CONFIG_HFS_FS=m
 # CONFIG_BFS_FS is not set
+# CONFIG_EXT3_FS is not set
+# CONFIG_JBD is not set
+# CONFIG_JBD_DEBUG is not set
 CONFIG_FAT_FS=m
 CONFIG_MSDOS_FS=m
 # CONFIG_UMSDOS_FS is not set
@@ -710,6 +720,7 @@
 # CONFIG_RAMFS is not set
 CONFIG_ISO9660_FS=y
 # CONFIG_JOLIET is not set
+# CONFIG_ZISOFS is not set
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
@@ -734,6 +745,7 @@
 # Network File Systems
 #
 # CONFIG_CODA_FS is not set
+# CONFIG_INTERMEZZO_FS is not set
 CONFIG_NFS_FS=y
 # CONFIG_NFS_V3 is not set
 # CONFIG_ROOT_NFS is not set
@@ -751,6 +763,8 @@
 # CONFIG_NCPFS_SMALLDOS is not set
 # CONFIG_NCPFS_NLS is not set
 # CONFIG_NCPFS_EXTRAS is not set
+# CONFIG_ZISOFS_FS is not set
+# CONFIG_ZLIB_FS_INFLATE is not set
 
 #
 # Partition Types
@@ -818,8 +832,10 @@
 # Sound
 #
 CONFIG_SOUND=m
-CONFIG_DMASOUND_AWACS=m
+CONFIG_DMASOUND_PMAC=m
 CONFIG_DMASOUND=m
+CONFIG_I2C=m
+CONFIG_I2C_KEYWEST=m
 # CONFIG_SOUND_BT878 is not set
 # CONFIG_SOUND_CMPCI is not set
 # CONFIG_SOUND_EMU10K1 is not set
@@ -854,7 +870,6 @@
 CONFIG_USB_DEVICEFS=y
 # CONFIG_USB_BANDWIDTH is not set
 # CONFIG_USB_LONG_TIMEOUT is not set
-# CONFIG_USB_LARGE_CONFIG is not set
 
 #
 # USB Controllers
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/kernel/Makefile linux/arch/ppc/kernel/Makefile
--- linux.orig/arch/ppc/kernel/Makefile	Mon Jan  7 23:41:08 2002
+++ linux/arch/ppc/kernel/Makefile	Wed Dec 26 16:28:34 2001
@@ -1,4 +1,4 @@
-# BK Id: SCCS/s.Makefile 1.34 10/16/01 15:58:42 trini
+# BK Id: SCCS/s.Makefile 1.36 12/01/01 20:09:06 benh
 #
 #
 # Makefile for the linux kernel.
@@ -56,7 +56,7 @@
 obj-$(CONFIG_PCI)		+= apus_pci.o
 endif
 obj-$(CONFIG_ALL_PPC)		+= pmac_pic.o pmac_setup.o pmac_time.o prom.o \
-					feature.o pmac_pci.o chrp_setup.o \
+					pmac_feature.o pmac_pci.o chrp_setup.o \
 					chrp_time.o chrp_pci.o 	open_pic.o \
 					indirect_pci.o i8259.o prep_pci.o \
 					prep_time.o prep_nvram.o prep_setup.o
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/kernel/btext.c linux/arch/ppc/kernel/btext.c
--- linux.orig/arch/ppc/kernel/btext.c	Mon Jan  7 23:41:08 2002
+++ linux/arch/ppc/kernel/btext.c	Wed Dec 26 16:28:34 2001
@@ -133,14 +133,19 @@
 {
 	unsigned long offset = reloc_offset();
 	boot_infos_t* bi = PTRRELOC(RELOC(disp_bi));
-	unsigned long addr = (unsigned long)bi->dispDeviceBase;
 	unsigned long vaddr = KERNELBASE + 0x10000000;
+	unsigned long addr;
 	unsigned long lowbits;
 
 	if (!RELOC(disp_bi)) {
 		RELOC(boot_text_mapped) = 0;
 		return;
 	}
+	addr = (unsigned long)bi->dispDeviceBase;
+	if (!addr) {
+		RELOC(boot_text_mapped) = 0;
+		return;
+	}
 	if (PVR_VER(mfspr(PVR)) != 1) {
 		/* 603, 604, G3, G4, ... */
 		lowbits = addr & ~0xFF000000UL;
@@ -231,10 +236,10 @@
 {
 	if (disp_bi == 0)
 		return;
-	/* check it's the same frame buffer (within 64MB) */
-	if ((phys ^ (unsigned long)disp_bi->dispDeviceBase) & 0xfc000000) {
+
+	/* check it's the same frame buffer (within 256MB) */
+	if ((phys ^ (unsigned long)disp_bi->dispDeviceBase) & 0xf0000000)
 		return;
-	}
 
 	disp_bi->dispDeviceBase = (__u8 *) phys;
 	disp_bi->dispDeviceRect[0] = 0;
@@ -423,9 +428,11 @@
 	int rb			= bi->dispDeviceRowBytes;
 	
 	switch(bi->dispDeviceDepth) {
+	case 24:
 	case 32:
 		draw_byte_32(font, (unsigned long *)base, rb);
 		break;
+	case 15:
 	case 16:
 		draw_byte_16(font, (unsigned long *)base, rb);
 		break;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/kernel/chrp_setup.c linux/arch/ppc/kernel/chrp_setup.c
--- linux.orig/arch/ppc/kernel/chrp_setup.c	Mon Jan  7 23:41:08 2002
+++ linux/arch/ppc/kernel/chrp_setup.c	Wed Dec 26 16:28:34 2001
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.chrp_setup.c 1.38 11/13/01 21:26:07 paulus
+ * BK Id: SCCS/s.chrp_setup.c 1.40 12/19/01 09:45:54 trini
  */
 /*
  *  linux/arch/ppc/kernel/setup.c
@@ -374,7 +374,7 @@
 	openpic_init(1, NUM_8259_INTERRUPTS, chrp_int_ack_special, nmi_irq);
 	for ( i = 0 ; i < NUM_8259_INTERRUPTS  ; i++ )
 		irq_desc[i].handler = &i8259_pic;
-	i8259_init();
+	i8259_init(NULL);
 #if defined(CONFIG_VT) && defined(CONFIG_ADB_KEYBOARD) && defined(XMON)
 	/* see if there is a keyboard in the device tree
 	   with a parent of type "adb" */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/kernel/chrp_smp.c linux/arch/ppc/kernel/chrp_smp.c
--- linux.orig/arch/ppc/kernel/chrp_smp.c	Mon Jan  7 23:41:08 2002
+++ linux/arch/ppc/kernel/chrp_smp.c	Wed Dec 26 16:28:34 2001
@@ -36,7 +36,6 @@
 #include <asm/prom.h>
 #include <asm/smp.h>
 #include <asm/residual.h>
-#include <asm/feature.h>
 #include <asm/time.h>
 
 #include "open_pic.h"
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/kernel/cputable.c linux/arch/ppc/kernel/cputable.c
--- linux.orig/arch/ppc/kernel/cputable.c	Mon Jan  7 23:41:08 2002
+++ linux/arch/ppc/kernel/cputable.c	Wed Dec 26 16:28:34 2001
@@ -26,6 +26,7 @@
 extern void __setup_cpu_604(int cpu_nr);
 extern void __setup_cpu_750(int cpu_nr);
 extern void __setup_cpu_7400(int cpu_nr);
+extern void __setup_cpu_7410(int cpu_nr);
 extern void __setup_cpu_7450(int cpu_nr);
 extern void __setup_cpu_power3(int cpu_nr);
 extern void __setup_cpu_power4(int cpu_nr);
@@ -113,14 +114,22 @@
 	32, 32,
 	__setup_cpu_604
     },
-    {	/* 750 (0x4202, don't support TAU ?) */
-    	0xffffffff, 0x00084202, "750",
+    {	/* 740/750 (0x4202, don't support TAU ?) */
+    	0xffffffff, 0x00084202, "740/750",
     	CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_CAN_DOZE | CPU_FTR_USE_TB |
 	CPU_FTR_L2CR | CPU_FTR_HPTE_TABLE,
 	COMMON_PPC,
 	32, 32,
 	__setup_cpu_750
     },
+    {	/* 745/755 */
+    	0xfffff000, 0x00083000, "745/755",
+    	CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_CAN_DOZE | CPU_FTR_USE_TB |
+	CPU_FTR_L2CR | CPU_FTR_TAU | CPU_FTR_HPTE_TABLE,
+	COMMON_PPC,
+	32, 32,
+	__setup_cpu_750
+    },
     {	/* 750CX */
     	0xffffff00, 0x00082200, "750CX",
     	CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_CAN_DOZE | CPU_FTR_USE_TB |
@@ -159,9 +168,18 @@
 	CPU_FTR_L2CR | CPU_FTR_TAU | CPU_FTR_ALTIVEC_COMP | CPU_FTR_HPTE_TABLE,
 	COMMON_PPC | PPC_FEATURE_HAS_ALTIVEC,
 	32, 32,
-	__setup_cpu_7400
+	__setup_cpu_7410
+    },
+    {	/* 7450 2.0 - no doze/nap */
+    	0xffffffff, 0x80000200, "7450",
+    	CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB |
+	CPU_FTR_L2CR | CPU_FTR_TAU | CPU_FTR_ALTIVEC_COMP |
+	CPU_FTR_HPTE_TABLE | CPU_FTR_SPEC7450,
+	COMMON_PPC | PPC_FEATURE_HAS_ALTIVEC,
+	32, 32,
+	__setup_cpu_7450
     },
-    {	/* 7450 */
+    {	/* 7450 others */
     	0xffff0000, 0x80000000, "7450",
     	CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_CAN_DOZE | CPU_FTR_USE_TB |
 	CPU_FTR_L2CR | CPU_FTR_TAU | CPU_FTR_ALTIVEC_COMP |
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/kernel/feature.c linux/arch/ppc/kernel/feature.c
--- linux.orig/arch/ppc/kernel/feature.c	Mon Jan  7 23:41:08 2002
+++ linux/arch/ppc/kernel/feature.c	Thu Jan  1 00:00:00 1970
@@ -1,1300 +0,0 @@
-/*
- * BK Id: SCCS/s.feature.c 1.21 09/08/01 15:47:42 paulus
- */
-/*
- *  arch/ppc/kernel/feature.c
- *
- *  Copyright (C) 1996 Paul Mackerras (paulus@cs.anu.edu.au)
- *                     Ben. Herrenschmidt (benh@kernel.crashing.org)
- *
- *  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/init.h>
-#include <linux/delay.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/spinlock.h>
-#include <asm/sections.h>
-#include <asm/errno.h>
-#include <asm/ohare.h>
-#include <asm/heathrow.h>
-#include <asm/keylargo.h>
-#include <asm/uninorth.h>
-#include <asm/io.h>
-#include <asm/prom.h>
-#include <asm/feature.h>
-#include <asm/dbdma.h>
-#include <asm/machdep.h>
-
-#undef DEBUG_FEATURE
-
-#define MAX_FEATURE_CONTROLLERS		2
-#define FREG(c,r)			(&(((c)->reg)[(r)>>2]))
-
-/* Keylargo reg. access. */
-#define KL_FCR(r)		(keylargo_base + ((r) >> 2))
-#define KL_IN(r)		(in_le32(KL_FCR(r)))
-#define KL_OUT(r,v)		(out_le32(KL_FCR(r), (v)))
-#define KL_BIS(r,v)		(KL_OUT((r), KL_IN(r) | (v)))
-#define KL_BIC(r,v)		(KL_OUT((r), KL_IN(r) & ~(v)))
-#define KL_GPIO_IN(r)		(in_8(((volatile u8 *)keylargo_base)+(r)))
-#define KL_GPIO_OUT(r,v)	(out_8(((volatile u8 *)keylargo_base)+(r), (v)))
-#define KL_LOCK()		spin_lock_irqsave(&keylargo->lock, flags)
-#define KL_UNLOCK()		spin_unlock_irqrestore(&keylargo->lock, flags)
-
-/* Uni-N reg. access. Note that Uni-N regs are big endian */
-#define UN_REG(r)	(uninorth_base + ((r) >> 2))
-#define UN_IN(r)	(in_be32(UN_REG(r)))
-#define UN_OUT(r,v)	(out_be32(UN_REG(r), (v)))
-#define UN_BIS(r,v)	(UN_OUT((r), UN_IN(r) | (v)))
-#define UN_BIC(r,v)	(UN_OUT((r), UN_IN(r) & ~(v)))
-
-typedef struct feature_bit {
-	int		reg;		/* reg. offset from mac-io base */
-	unsigned int	polarity;	/* 0 = normal, 1 = inverse */
-	unsigned int	mask;		/* bit mask */
-} fbit;
-
-/* Those features concern for OHare-based PowerBooks (2400, 3400, 3500)
- */
-static fbit feature_bits_ohare_pbook[] __pmacdata = {
-	{0x38,0,0},			/* FEATURE_null */
-	{0x38,0,OH_SCC_RESET},		/* FEATURE_Serial_reset */
-	{0x38,0,OH_SCC_ENABLE},		/* FEATURE_Serial_enable */
-	{0x38,0,OH_SCCA_IO},		/* FEATURE_Serial_IO_A */
-	{0x38,0,OH_SCCB_IO},		/* FEATURE_Serial_IO_B */
-	{0x38,0,OH_FLOPPY_ENABLE},	/* FEATURE_SWIM3_enable */
-	{0x38,0,OH_MESH_ENABLE},	/* FEATURE_MESH_enable */
-	{0x38,0,OH_IDE0_ENABLE},	/* FEATURE_IDE0_enable */
-	{0x38,1,OH_IDE0_RESET_N},	/* FEATURE_IDE0_reset */
-	{0x38,0,OH_IOBUS_ENABLE},	/* FEATURE_IOBUS_enable */
-	{0x38,1,OH_BAY_RESET_N},	/* FEATURE_Mediabay_reset */
-	{0x38,1,OH_BAY_POWER_N},	/* FEATURE_Mediabay_power */
-	{0x38,0,OH_BAY_PCI_ENABLE},	/* FEATURE_Mediabay_PCI_enable */
-	{0x38,0,OH_BAY_IDE_ENABLE},	/* FEATURE_IDE1_enable */
-	{0x38,1,OH_IDE1_RESET_N},	/* FEATURE_IDE1_reset */
-	{0x38,0,OH_BAY_FLOPPY_ENABLE},	/* FEATURE_Mediabay_floppy_enable */
-	{0x38,0,0},			/* FEATURE_BMac_reset */
-	{0x38,0,0},			/* FEATURE_BMac_IO_enable */
-	{0x38,0,0},			/* FEATURE_Modem_power */
-	{0x38,0,0},			/* FEATURE_Slow_SCC_PCLK */
-	{0x38,0,0},			/* FEATURE_Sound_Power */
-	{0x38,0,0},			/* FEATURE_Sound_CLK_Enable */
-	{0x38,0,0},			/* FEATURE_IDE2_enable */
-	{0x38,0,0},			/* FEATURE_IDE2_reset */
-	{0x38,0,0},			/* FEATURE_Mediabay_IDE_switch */
-	{0x38,0,0},			/* FEATURE_Mediabay_content */
-	{0x38,0,0},			/* FEATURE_Airport_reset */
-};
-
-/* Those bits concern heathrow-based desktop machines (Beige G3s). We have removed
- * the SCC related bits and init them once. They have proven to occasionally cause
- * problems with the desktop units.
- */
-static fbit feature_bits_heathrow[] __pmacdata = {
-	{0x38,0,0},			/* FEATURE_null */
-	{0x38,0,0},			/* FEATURE_Serial_reset */
-	{0x38,0,0},			/* FEATURE_Serial_enable */
-	{0x38,0,0},			/* FEATURE_Serial_IO_A */
-	{0x38,0,0},			/* FEATURE_Serial_IO_B */
-	{0x38,0,HRW_SWIM_ENABLE},	/* FEATURE_SWIM3_enable */
-	{0x38,0,HRW_MESH_ENABLE},	/* FEATURE_MESH_enable */
-	{0x38,0,HRW_IDE0_ENABLE},	/* FEATURE_IDE0_enable */
-	{0x38,1,HRW_IDE0_RESET_N},	/* FEATURE_IDE0_reset */
-	{0x38,0,HRW_IOBUS_ENABLE},	/* FEATURE_IOBUS_enable */
-	{0x38,1,0},			/* FEATURE_Mediabay_reset */
-	{0x38,1,0},			/* FEATURE_Mediabay_power */
-	{0x38,0,0},			/* FEATURE_Mediabay_PCI_enable */
-	{0x38,0,HRW_BAY_IDE_ENABLE},	/* FEATURE_IDE1_enable */
-	{0x38,1,HRW_IDE1_RESET_N},	/* FEATURE_IDE1_reset */
-	{0x38,0,0},			/* FEATURE_Mediabay_floppy_enable */
-	{0x38,0,HRW_BMAC_RESET},	/* FEATURE_BMac_reset */
-	{0x38,0,HRW_BMAC_IO_ENABLE},	/* FEATURE_BMac_IO_enable */
-	{0x38,1,0},			/* FEATURE_Modem_power */
-	{0x38,0,HRW_SLOW_SCC_PCLK},	/* FEATURE_Slow_SCC_PCLK */
-	{0x38,1,0},			/* FEATURE_Sound_Power */
-	{0x38,0,0},			/* FEATURE_Sound_CLK_Enable */
-	{0x38,0,0},			/* FEATURE_IDE2_enable */
-	{0x38,0,0},			/* FEATURE_IDE2_reset */
-	{0x38,0,0},			/* FEATURE_Mediabay_IDE_switch */
-	{0x38,0,0},			/* FEATURE_Mediabay_content */
-	{0x38,0,0},			/* FEATURE_Airport_reset */
-};
-
-/* Those bits concern heathrow-based PowerBooks (wallstreet/mainstreet).
- * Heathrow-based desktop macs (Beige G3s) are _not_ handled here
- */
-static fbit feature_bits_wallstreet[] __pmacdata = {
-	{0x38,0,0},			/* FEATURE_null */
-	{0x38,0,HRW_RESET_SCC},		/* FEATURE_Serial_reset */
-	{0x38,0,HRW_SCC_ENABLE},	/* FEATURE_Serial_enable */
-	{0x38,0,HRW_SCCA_IO},		/* FEATURE_Serial_IO_A */
-	{0x38,0,HRW_SCCB_IO},		/* FEATURE_Serial_IO_B */
-	{0x38,0,HRW_SWIM_ENABLE},	/* FEATURE_SWIM3_enable */
-	{0x38,0,HRW_MESH_ENABLE},	/* FEATURE_MESH_enable */
-	{0x38,0,HRW_IDE0_ENABLE},	/* FEATURE_IDE0_enable */
-	{0x38,1,HRW_IDE0_RESET_N},	/* FEATURE_IDE0_reset */
-	{0x38,0,HRW_IOBUS_ENABLE},	/* FEATURE_IOBUS_enable */
-	{0x38,1,HRW_BAY_RESET_N},	/* FEATURE_Mediabay_reset */
-	{0x38,1,HRW_BAY_POWER_N},	/* FEATURE_Mediabay_power */
-	{0x38,0,HRW_BAY_PCI_ENABLE},	/* FEATURE_Mediabay_PCI_enable */
-	{0x38,0,HRW_BAY_IDE_ENABLE},	/* FEATURE_IDE1_enable */
-	{0x38,1,HRW_IDE1_RESET_N},	/* FEATURE_IDE1_reset */
-	{0x38,0,HRW_BAY_FLOPPY_ENABLE},	/* FEATURE_Mediabay_floppy_enable */
-	{0x38,0,HRW_BMAC_RESET},	/* FEATURE_BMac_reset */
-	{0x38,0,HRW_BMAC_IO_ENABLE},	/* FEATURE_BMac_IO_enable */
-	{0x38,1,HRW_MODEM_POWER_N},	/* FEATURE_Modem_power */
-	{0x38,0,HRW_SLOW_SCC_PCLK},	/* FEATURE_Slow_SCC_PCLK */
-	{0x38,1,HRW_SOUND_POWER_N},	/* FEATURE_Sound_Power */
-	{0x38,0,HRW_SOUND_CLK_ENABLE},	/* FEATURE_Sound_CLK_Enable */
-	{0x38,0,0},			/* FEATURE_IDE2_enable */
-	{0x38,0,0},			/* FEATURE_IDE2_reset */
-	{0x38,0,0},			/* FEATURE_Mediabay_IDE_switch */
-	{0x38,0,0},			/* FEATURE_Mediabay_content */
-	{0x38,0,0},			/* FEATURE_Airport_reset */
-};
-
-/*
- * Those bits are from a 1999 G3 PowerBook, with a paddington chip.
- * Mostly the same as the heathrow. They are used on both PowerBooks
- * and desktop machines using the paddington chip
- */
-static fbit feature_bits_paddington[] __pmacdata = {
-	{0x38,0,0},			/* FEATURE_null */
-	{0x38,0,PADD_RESET_SCC},	/* FEATURE_Serial_reset */
-	{0x38,0,HRW_SCC_ENABLE},	/* FEATURE_Serial_enable */
-	{0x38,0,HRW_SCCA_IO},		/* FEATURE_Serial_IO_A */
-	{0x38,0,HRW_SCCB_IO},		/* FEATURE_Serial_IO_B */
-	{0x38,0,HRW_SWIM_ENABLE},	/* FEATURE_SWIM3_enable */
-	{0x38,0,HRW_MESH_ENABLE},	/* FEATURE_MESH_enable */
-	{0x38,0,HRW_IDE0_ENABLE},	/* FEATURE_IDE0_enable */
-	{0x38,1,HRW_IDE0_RESET_N},	/* FEATURE_IDE0_reset */
-	{0x38,0,HRW_IOBUS_ENABLE},	/* FEATURE_IOBUS_enable */
-	{0x38,1,HRW_BAY_RESET_N},	/* FEATURE_Mediabay_reset */
-	{0x38,1,HRW_BAY_POWER_N},	/* FEATURE_Mediabay_power */
-	{0x38,0,HRW_BAY_PCI_ENABLE},	/* FEATURE_Mediabay_PCI_enable */
-	{0x38,0,HRW_BAY_IDE_ENABLE},	/* FEATURE_IDE1_enable */
-	{0x38,1,HRW_IDE1_RESET_N},	/* FEATURE_IDE1_reset */
-	{0x38,0,HRW_BAY_FLOPPY_ENABLE},	/* FEATURE_Mediabay_floppy_enable */
-	{0x38,0,HRW_BMAC_RESET},	/* FEATURE_BMac_reset */
-	{0x38,0,HRW_BMAC_IO_ENABLE},	/* FEATURE_BMac_IO_enable */
-	{0x38,1,PADD_MODEM_POWER_N},	/* FEATURE_Modem_power */
-	{0x38,0,HRW_SLOW_SCC_PCLK},	/* FEATURE_Slow_SCC_PCLK */
-	{0x38,1,HRW_SOUND_POWER_N},	/* FEATURE_Sound_Power */
-	{0x38,0,HRW_SOUND_CLK_ENABLE},	/* FEATURE_Sound_CLK_Enable */
-	{0x38,0,0},			/* FEATURE_IDE2_enable */
-	{0x38,0,0},			/* FEATURE_IDE2_reset */
-	{0x38,0,0},			/* FEATURE_Mediabay_IDE_switch */
-	{0x38,0,0},			/* FEATURE_Mediabay_content */
-	{0x38,0,0},			/* FEATURE_Airport_reset */
-};
-
-/* Those bits are for Core99 machines (iBook,G4,iMacSL/DV,Pismo,...).
- * Note: Different sets may be needed for iBook, especially for sound
- */
-static fbit feature_bits_keylargo[] __pmacdata = {
-	{0x38,0,0},			/* FEATURE_null */
-	{0x38,0,KL0_SCC_RESET},		/* FEATURE_Serial_reset */
-	{0x38,0,KL0_SERIAL_ENABLE},	/* FEATURE_Serial_enable */
-	{0x38,0,KL0_SCC_A_INTF_ENABLE},	/* FEATURE_Serial_IO_A */
-	{0x38,0,KL0_SCC_B_INTF_ENABLE},	/* FEATURE_Serial_IO_B */
-	{0x38,0,0},			/* FEATURE_SWIM3_enable */
-	{0x38,0,0},			/* FEATURE_MESH_enable */
-	{0x3c,0,KL1_EIDE0_ENABLE},	/* FEATURE_IDE0_enable */
- 	{0x3c,1,KL1_EIDE0_RESET_N},	/* FEATURE_IDE0_reset */
-	{0x38,0,0},			/* FEATURE_IOBUS_enable */
-	{0x34,1,KL_MBCR_MB0_DEV_RESET},	/* FEATURE_Mediabay_reset */
-	{0x34,1,KL_MBCR_MB0_DEV_POWER},	/* FEATURE_Mediabay_power */
-	{0x38,0,0},			/* FEATURE_Mediabay_PCI_enable */
-	{0x3c,0,KL1_EIDE1_ENABLE},	/* FEATURE_IDE1_enable */
-	{0x3c,1,KL1_EIDE1_RESET_N},	/* FEATURE_IDE1_reset */
-	{0x38,0,0},			/* FEATURE_Mediabay_floppy_enable */
-	{0x38,0,0},			/* FEATURE_BMac_reset */
-	{0x38,0,0},			/* FEATURE_BMac_IO_enable */
-	{0x40,1,KL2_MODEM_POWER_N},	/* FEATURE_Modem_power */
-	{0x38,0,0},			/* FEATURE_Slow_SCC_PCLK */
-	{0x38,0,0},			/* FEATURE_Sound_Power */
-	{0x38,0,0},			/* FEATURE_Sound_CLK_Enable */
-	{0x3c,0,KL1_UIDE_ENABLE},	/* FEATURE_IDE2_enable */
-	{0x3c,1,KL1_UIDE_RESET_N},	/* FEATURE_IDE2_reset */
-	{0x34,0,KL_MBCR_MB0_DEV_ENABLE},/* FEATURE_Mediabay_IDE_switch */
-	{0x34,0,KL_MBCR_MB0_ENABLE},	/* FEATURE_Mediabay_content */
-	{0x40,1,KL2_AIRPORT_RESET_N},	/* FEATURE_Airport_reset */
-};
-
-/* definition of a feature controller object */
-struct feature_controller {
-	fbit*			bits;
-	volatile u32*		reg;
-	struct device_node*	device;
-	spinlock_t		lock;
-};
-
-/* static functions */
-static struct feature_controller*
-feature_add_controller(struct device_node *controller_device, fbit* bits);
-
-static struct feature_controller*
-feature_lookup_controller(struct device_node *device);
-
-static void uninorth_init(void);
-static void keylargo_init(void);
-#ifdef CONFIG_PMAC_PBOOK
-static void heathrow_prepare_for_sleep(struct feature_controller* ctrler);
-static void heathrow_wakeup(struct feature_controller* ctrler);
-static void core99_prepare_for_sleep(struct feature_controller* ctrler);
-static void core99_wake_up(struct feature_controller* ctrler);
-#endif /* CONFIG_PMAC_PBOOK */
-
-/* static variables */
-static struct feature_controller	controllers[MAX_FEATURE_CONTROLLERS];
-static int				controller_count = 0;
-
-/* Core99 stuffs */
-/*static*/ volatile u32*		uninorth_base;
-static volatile u32*			keylargo_base;
-static struct feature_controller*	keylargo;
-static int				uninorth_rev;
-static int				keylargo_rev;
-static u32				board_features;
-static int				airport_pwr_state;
-static struct device_node*		airport_dev;
-static struct device_node*		uninorth_fw;
-
-/* Feature bits for Apple motherboards */
-#define FTR_NEED_OPENPIC_TWEAK		0x00000001
-#define FTR_CAN_NAP			0x00000002
-#define FTR_HAS_FW_POWER		0x00000004
-#define FTR_CAN_SLEEP			0x00000008
-
-/* This table currently lacks most oldworld machines, but
- * they currently don't need it so...
- * 
- * Todo: The whole feature_xxx mecanism need to be redone
- * some way to be able to handle the new kind of features
- * exposed by core99. At this point, the main "tables" will
- * probably be in this table, which will have to be filled with
- * all known machines
- */
-/* Warning: Don't change ordering of entries as some machines
- * adverstise beeing compatible with several models. In those
- * case, the "highest" has to be first
- */
-static struct board_features_t {
-	char*	compatible;
-	u32	features;
-} board_features_datas[] __initdata = 
-{
-  {	"AAPL,PowerMac G3",	0				}, /* Beige G3 */
-  {	"iMac,1",		0				}, /* First iMac (gossamer) */
-  {	"PowerMac1,1",		0				}, /* B&W G3 / Yikes */
-  {	"PowerMac1,2",		0				}, /* B&W G3 / Yikes */
-  {	"PowerMac2,1",		FTR_CAN_SLEEP			}, /* r128 based iMac */
-  {	"PowerMac2,2",		FTR_HAS_FW_POWER|FTR_CAN_SLEEP	}, /* Summer 2000 iMac */
-  {	"PowerMac4,1",		FTR_CAN_SLEEP			}, /* iMac "Flower Power" */
-  {	"PowerMac3,1",		FTR_NEED_OPENPIC_TWEAK		}, /* Sawtooth (G4) */
-  {	"PowerMac3,2",		0				}, /* G4/Dual G4 */
-  {	"PowerMac3,3",		FTR_NEED_OPENPIC_TWEAK		}, /* G4/Dual G4 */
-  {	"PowerMac3,4",		0				}, /* QuickSilver G4 */
-  {	"PowerMac3,5",		0				}, /* QuickSilver G4 */
-  {	"PowerMac5,1",		FTR_CAN_NAP			}, /* Cube */
-  {	"AAPL,3400/2400",	FTR_CAN_SLEEP			}, /* 2400/3400 PowerBook */
-  {	"AAPL,3500",		FTR_CAN_SLEEP			}, /* 3500 PowerBook (G3) */
-  {	"AAPL,PowerBook1998",	FTR_CAN_SLEEP			}, /* Wallstreet PowerBook */
-  {	"PowerBook1,1",		FTR_CAN_SLEEP			}, /* 101 (Lombard) PowerBook */
-  {	"PowerBook4,1",		FTR_CAN_NAP|FTR_CAN_SLEEP|	   /* New polycarbonate iBook */
-  				0/*FTR_HAS_FW_POWER*/		}, 
-  {	"PowerBook2,1",		FTR_CAN_SLEEP 			}, /* iBook */
-  {	"PowerBook2,2",		FTR_CAN_SLEEP /*| FTR_CAN_NAP*/	}, /* iBook FireWire */
-  {	"PowerBook3,1",		FTR_CAN_SLEEP|FTR_CAN_NAP|	   /* PowerBook 2000 (Pismo) */
-  				FTR_HAS_FW_POWER		}, 
-  {	"PowerBook3,2",		FTR_CAN_NAP|FTR_CAN_SLEEP|	   /* PowerBook Titanium */
-  				0/*FTR_HAS_FW_POWER*/		},
-  {	NULL, 0 }
-};
-
-extern unsigned long powersave_nap;
-
-void __init
-feature_init(void)
-{
-	struct device_node *np;
-	u32 *rev;
-	int i;
-	char* model;
-	
-	if (_machine != _MACH_Pmac)
-		return;
-
-	np = find_path_device("/");
-	if (!np) {
-		printk(KERN_ERR "feature.c: Can't find device-tree root !\n");
-		return;
-	}
-	model = (char*)get_property(np, "model", NULL);
-	if (model) {
-		printk("PowerMac model: %s\n", model);
-		/* Figure out motherboard type & options */
-		for(i=0;board_features_datas[i].compatible;i++) {
-			if (!strcmp(board_features_datas[i].compatible, model)) {
-				board_features = board_features_datas[i].features;
-				break;
-			}
-		}
-	}
-
-	/* Set default value of powersave_nap on machines that support it */
-	if (board_features & FTR_CAN_NAP)
-		powersave_nap = 1;
-		
-	/* Track those poor mac-io's */
-	np = find_devices("mac-io");
-	while (np != NULL) {
-		/* KeyLargo contains several (5 ?) FCR registers in mac-io,
-		 * plus some gpio's which could eventually be handled here.
-		 */
-		if (device_is_compatible(np, "Keylargo")) {
-			struct feature_controller* ctrler =
-				feature_add_controller(np, feature_bits_keylargo);
-			if (ctrler) {
-				keylargo = ctrler;
-				keylargo_base = ctrler->reg;
-				rev = (u32 *)get_property(ctrler->device, "revision-id", NULL);
-				if (rev)
-					keylargo_rev = *rev;
-				
-				rev = (u32 *)get_property(ctrler->device, "device-id", NULL);
-				if (rev && (*rev) == 0x0025)
-					keylargo_rev |= KL_PANGEA_REV;
-			}
-		} else if (device_is_compatible(np, "paddington")) {
-			feature_add_controller(np, feature_bits_paddington);
-			/* We disable the modem power bit on Yikes as it can
-			 * do bad things (it's the nvram power)
-			 */
-			if (machine_is_compatible("PowerMac1,1") ||
-				machine_is_compatible("PowerMac1,2")) {
-				feature_bits_paddington[FEATURE_Modem_power].mask = 0;
-			}
-		} else if (machine_is_compatible("AAPL,PowerBook1998")) {
-			feature_add_controller(np, feature_bits_wallstreet);
-		} else {
-			struct feature_controller* ctrler =
-				feature_add_controller(np, feature_bits_heathrow);
-			if (ctrler)
-				out_le32(FREG(ctrler,HEATHROW_FEATURE_REG),
-					in_le32(FREG(ctrler,HEATHROW_FEATURE_REG)) | HRW_DEFAULTS);
-			
-		}
-		np = np->next;
-	}
-	if (controller_count == 0)
-	{
-		np = find_devices("ohare");
-		if (np) {
-			if (find_devices("via-pmu") != NULL)
-				feature_add_controller(np, feature_bits_ohare_pbook);
-			else
-				/* else not sure; maybe this is a Starmax? */
-				feature_add_controller(np, NULL);
-		}
-	}
-
-	/* Locate core99 Uni-N */
-	np = find_devices("uni-n");
-	if (np && np->n_addrs > 0) {
-		uninorth_base = ioremap(np->addrs[0].address, 0x1000);
-		uninorth_rev = in_be32(UN_REG(UNI_N_VERSION));
-		printk("Uninorth at 0x%08x\n", np->addrs[0].address);
-	}
-	if (uninorth_base && keylargo_base)
-		printk("Uni-N revision: %d, KeyLargo revision: %d %s\n",
-			uninorth_rev, keylargo_rev,
-			(keylargo_rev & KL_PANGEA_REV) ? "(Pangea chipset)" : "");
-
-	if (uninorth_base)
-		uninorth_init();
-	if (keylargo_base)
-		keylargo_init();
-
-	if (controller_count)
-		printk(KERN_INFO "Registered %d feature controller(s)\n", controller_count);
-
-#if defined(CONFIG_PMAC_PBOOK) && !defined(CONFIG_DMASOUND_AWACS)
-	/* On PowerBooks, we disable the sound chip when dmasound is a module
-	 * or not used at all
-	 */
-	if (controller_count && find_devices("via-pmu") != NULL) {
-		feature_clear(controllers[0].device, FEATURE_Sound_power);
-		feature_clear(controllers[0].device, FEATURE_Sound_CLK_enable);
-	}
-#endif
-
-#ifdef CONFIG_PMAC_PBOOK
-	/* On PowerBooks, we disable the serial ports by default, they
-	 * will be re-enabled by the driver
-	 */
-#ifndef CONFIG_XMON
-	if (controller_count && find_devices("via-pmu") != NULL) {
-		feature_set_modem_power(NULL, 0);
-		feature_clear(controllers[0].device, FEATURE_Serial_IO_A);
-		feature_clear(controllers[0].device, FEATURE_Serial_IO_B);
-		feature_clear(controllers[0].device, FEATURE_Serial_enable);
-		if (controller_count > 1) {
-			feature_clear(controllers[1].device, FEATURE_Serial_IO_A);
-			feature_clear(controllers[1].device, FEATURE_Serial_IO_B);
-			feature_clear(controllers[1].device, FEATURE_Serial_enable);
-		}
-	}
-#endif
-#endif
-}
-
-static struct feature_controller __init *
-feature_add_controller(struct device_node *controller_device, fbit* bits)
-{
-	struct feature_controller*	controller;
-	
-	if (controller_count >= MAX_FEATURE_CONTROLLERS) {
-		printk(KERN_INFO "Feature controller %s skipped(MAX:%d)\n",
-			controller_device->full_name, MAX_FEATURE_CONTROLLERS);
-		return NULL;
-	}
-	controller = &controllers[controller_count];
-
-	controller->bits	= bits;
-	controller->device	= controller_device;
-	if (controller_device->n_addrs == 0) {
-		printk(KERN_ERR "No addresses for %s\n",
-			controller_device->full_name);
-		return NULL;
-	}
-
-	/* We remap the entire mac-io here. Normally, this will just
-	 * give us back our already existing BAT mapping
-	 */
-	controller->reg		= (volatile u32 *)ioremap(
-		controller_device->addrs[0].address,
-		controller_device->addrs[0].size);
-
-	if (bits == NULL) {
-		printk(KERN_INFO "Twiddling the magic ohare bits\n");
-		out_le32(FREG(controller,OHARE_FEATURE_REG), STARMAX_FEATURES);
-		return NULL;
-	}
-
-	spin_lock_init(&controller->lock);
-
-	controller_count++;
-
-	return controller;
-}
-
-static struct feature_controller __pmac *
-feature_lookup_controller(struct device_node *device)
-{
-	int	i;
-	
-	if (device == NULL)
-		return NULL;
-		
-	while(device)
-	{
-		for (i=0; i<controller_count; i++)
-			if (device == controllers[i].device)
-				return &controllers[i];
-		device = device->parent;
-	}
-
-#ifdef DEBUG_FEATURE
-	printk("feature: <%s> not found on any controller\n",
-		device->name);
-#endif
-	
-	return NULL;
-}
-
-int __pmac
-feature_set(struct device_node* device, enum system_feature f)
-{
-	struct feature_controller*	controller;
-	unsigned long			flags;
-	unsigned long			value;
-	fbit*				bit;
-
-	if (f >= FEATURE_last)
-		return -EINVAL;	
-
-	controller = feature_lookup_controller(device);
-	if (!controller)
-		return -ENODEV;
-	bit = &controller->bits[f];
-	if (!bit->mask)
-		return -EINVAL;
-	
-#ifdef DEBUG_FEATURE
-	printk("feature: <%s> setting feature %d in controller @0x%x\n",
-		device->name, (int)f, (unsigned int)controller->reg);
-#endif
-
-	spin_lock_irqsave(&controller->lock, flags);
-	value = in_le32(FREG(controller, bit->reg));
-	value = bit->polarity ? (value & ~bit->mask) : (value | bit->mask);
-	out_le32(FREG(controller, bit->reg), value);
-	(void)in_le32(FREG(controller, bit->reg));
-	spin_unlock_irqrestore(&controller->lock, flags);
-	
-	return 0;
-}
-
-int __pmac
-feature_clear(struct device_node* device, enum system_feature f)
-{
-	struct feature_controller*	controller;
-	unsigned long			flags;
-	unsigned long			value;
-	fbit*				bit;
-
-	if (f >= FEATURE_last)
-		return -EINVAL;	
-
-	controller = feature_lookup_controller(device);
-	if (!controller)
-		return -ENODEV;
-	bit = &controller->bits[f];
-	if (!bit->mask)
-		return -EINVAL;
-	
-#ifdef DEBUG_FEATURE
-	printk("feature: <%s> clearing feature %d in controller @0x%x\n",
-		device->name, (int)f, (unsigned int)controller->reg);
-#endif
-
-	spin_lock_irqsave(&controller->lock, flags);
-	value = in_le32(FREG(controller, bit->reg));
-	value = bit->polarity ? (value | bit->mask) : (value & ~bit->mask);
-	out_le32(FREG(controller, bit->reg), value);
-	(void)in_le32(FREG(controller, bit->reg));
-	spin_unlock_irqrestore(&controller->lock, flags);
-	
-	return 0;
-}
-
-int __pmac
-feature_test(struct device_node* device, enum system_feature f)
-{
-	struct feature_controller*	controller;
-	unsigned long			value;
-	fbit*				bit;
-
-	if (f >= FEATURE_last)
-		return -EINVAL;	
-
-	controller = feature_lookup_controller(device);
-	if (!controller)
-		return -ENODEV;
-	bit = &controller->bits[f];
-	if (!bit->mask)
-		return -EINVAL;
-	
-#ifdef DEBUG_FEATURE
-	printk("feature: <%s> clearing feature %d in controller @0x%x\n",
-		device->name, (int)f, (unsigned int)controller->reg);
-#endif
-	/* If one feature contains several bits, all of them must be set
-	 * for value to be true, or all of them must be 0 if polarity is
-	 * inverse
-	 */
-	value = (in_le32(FREG(controller, bit->reg)) & bit->mask);
-	return bit->polarity ? (value == 0) : (value == bit->mask);
-}
-
-int __pmac
-feature_can_sleep(void)
-{
-	return ((board_features & FTR_CAN_SLEEP) != 0);	
-}
-
-/*
- * Core99 functions
- * 
- * Note: We currently assume there is _one_ UniN chip and _one_ KeyLargo
- *       chip, which is the case on all Core99 machines so far
- */
-
-/* Only one GMAC is assumed */
-void __pmac
-feature_set_gmac_power(struct device_node* device, int power)
-{
-	unsigned long flags;
-	
-	if (!uninorth_base || !keylargo)
-		return;
-		
-	/* TODO: Handle save/restore of PCI config space here
-	 */
-
-	/* XXX We use the keylargo spinlock, but we never
-	 * have uninorth without keylargo, so...
-	 */
-	KL_LOCK();
-	if (power)
-		UN_BIS(UNI_N_CLOCK_CNTL, UNI_N_CLOCK_CNTL_GMAC);
-	else
-		UN_BIC(UNI_N_CLOCK_CNTL, UNI_N_CLOCK_CNTL_GMAC);
-	(void)UN_IN(UNI_N_CLOCK_CNTL);
-	KL_UNLOCK();
-	udelay(20);
-}
-
-void __pmac
-feature_gmac_phy_reset(struct device_node* device)
-{
-	unsigned long flags;
-	
-	if (!keylargo_base || !keylargo)
-		return;
-		
-	KL_LOCK();
-	KL_GPIO_OUT(KL_GPIO_ETH_PHY_RESET, KEYLARGO_GPIO_OUTPUT_ENABLE);
-	(void)KL_GPIO_IN(KL_GPIO_ETH_PHY_RESET);
-	KL_UNLOCK();
-	mdelay(10);
-	KL_LOCK();
-	KL_GPIO_OUT(KL_GPIO_ETH_PHY_RESET,
-		KEYLARGO_GPIO_OUTPUT_ENABLE | KEYLARGO_GPIO_OUTOUT_DATA);
-	(void)KL_GPIO_IN(KL_GPIO_ETH_PHY_RESET);
-	KL_UNLOCK();
-	mdelay(10);
-}
-
-/* Pass the node of the correct controller, please */
-void __pmac
-feature_set_usb_power(struct device_node* device, int power)
-{
-	char* prop;
-	int number;
-	u32 reg;
-	
-	unsigned long flags;
-	
-	if (!keylargo_base || !keylargo)
-		return;
-		
-	prop = (char *)get_property(device, "AAPL,clock-id", NULL);
-	if (!prop)
-		return;
-	if (strncmp(prop, "usb0u048", strlen("usb0u048")) == 0)
-		number = 0;
-	else if (strncmp(prop, "usb1u148", strlen("usb1u148")) == 0)
-		number = 2;
-	else
-		return;
-
-	KL_LOCK();
-	if (power) {
-		/* Turn ON */
-			
-		if (number == 0) {
-			KL_BIC(KEYLARGO_FCR0, (KL0_USB0_PAD_SUSPEND0 | KL0_USB0_PAD_SUSPEND1));
-			(void)KL_IN(KEYLARGO_FCR0);
-			KL_UNLOCK();
-			mdelay(1);
-			KL_LOCK();
-			KL_BIS(KEYLARGO_FCR0, KL0_USB0_CELL_ENABLE);
-		} else {
-			KL_BIC(KEYLARGO_FCR0, (KL0_USB1_PAD_SUSPEND0 | KL0_USB1_PAD_SUSPEND1));
-			KL_UNLOCK();
-			(void)KL_IN(KEYLARGO_FCR0);
-			mdelay(1);
-			KL_LOCK();
-			KL_BIS(KEYLARGO_FCR0, KL0_USB1_CELL_ENABLE);
-		}
-		reg = KL_IN(KEYLARGO_FCR4);
-		reg &=	~(KL4_SET_PORT_ENABLE(number) | KL4_SET_PORT_RESUME(number) |
-			KL4_SET_PORT_CONNECT(number) | KL4_SET_PORT_DISCONNECT(number));
-		reg &=	~(KL4_SET_PORT_ENABLE(number+1) | KL4_SET_PORT_RESUME(number+1) |
-			KL4_SET_PORT_CONNECT(number+1) | KL4_SET_PORT_DISCONNECT(number+1));
-		KL_OUT(KEYLARGO_FCR4, reg);
-		(void)KL_IN(KEYLARGO_FCR4);
-		udelay(10);
-	} else {
-		/* Turn OFF */
-		
-		reg = KL_IN(KEYLARGO_FCR4);
-		reg |=	KL4_SET_PORT_ENABLE(number) | KL4_SET_PORT_RESUME(number) |
-			KL4_SET_PORT_CONNECT(number) | KL4_SET_PORT_DISCONNECT(number);
-		reg |=	KL4_SET_PORT_ENABLE(number+1) | KL4_SET_PORT_RESUME(number+1) |
-			KL4_SET_PORT_CONNECT(number+1) | KL4_SET_PORT_DISCONNECT(number+1);
-		KL_OUT(KEYLARGO_FCR4, reg);
-		(void)KL_IN(KEYLARGO_FCR4);
-		udelay(1);
-		if (number == 0) {
-			KL_BIC(KEYLARGO_FCR0, KL0_USB0_CELL_ENABLE);
-			(void)KL_IN(KEYLARGO_FCR0);
-			udelay(1);
-			KL_BIS(KEYLARGO_FCR0, (KL0_USB0_PAD_SUSPEND0 | KL0_USB0_PAD_SUSPEND1));
-			(void)KL_IN(KEYLARGO_FCR0);
-		} else {
-			KL_BIC(KEYLARGO_FCR0, KL0_USB1_CELL_ENABLE);
-			(void)KL_IN(KEYLARGO_FCR0);
-			udelay(1);
-			KL_BIS(KEYLARGO_FCR0, (KL0_USB1_PAD_SUSPEND0 | KL0_USB1_PAD_SUSPEND1));
-			(void)KL_IN(KEYLARGO_FCR0);
-		}
-		udelay(1);
-	}
-	KL_UNLOCK();
-}
-
-void  __pmac
-feature_set_firewire_power(struct device_node* device, int power)
-{
-	unsigned long flags;
-
-	/* TODO: should probably handle save/restore of PCI config space here
-	 */
-		
-	if (!uninorth_fw || (device && uninorth_fw != device))
-		return;
-	if (!uninorth_base)
-		return;
-
-	/* XXX We use the keylargo spinlock, but we never
-	 * have uninorth without keylargo, so...
-	 */
-	KL_LOCK();
-	if (power)
-		UN_BIS(UNI_N_CLOCK_CNTL, UNI_N_CLOCK_CNTL_FW);
-	else
-		UN_BIC(UNI_N_CLOCK_CNTL, UNI_N_CLOCK_CNTL_FW);
-	(void)UN_IN(UNI_N_CLOCK_CNTL);
-	KL_UNLOCK();
-	udelay(20);
-}
-
-/* Warning: will kill the PHY.. */
-void  __pmac
-feature_set_firewire_cable_power(struct device_node* device, int power)
-{
-	unsigned long flags;
-	u8 gpioValue = power ? 0 : 4;
-
-	if (!uninorth_fw || (device && uninorth_fw != device))
-		return;
-	if (!keylargo_base || !(board_features & FTR_HAS_FW_POWER))
-		return;
-	KL_LOCK();
-	KL_GPIO_OUT(KL_GPIO_FW_CABLE_POWER, gpioValue);
-	(void)KL_GPIO_IN(KL_GPIO_FW_CABLE_POWER);
-	KL_UNLOCK();		
-}
-
-void
-feature_set_modem_power(struct device_node* device, int power)
-{
-	unsigned long flags;
-
-	if (!device) {
-		device = find_devices("ch-a");
-		while(device && !device_is_compatible(device, "cobalt"))
-			device = device->next;
-		if (!device)
-			return;
-	}
-	if (keylargo && (keylargo_rev & KL_PANGEA_REV)) {
-		KL_LOCK();
-		if (power) {
-			/* Assert modem reset */
-			KL_GPIO_OUT(KL_GPIO_MODEM_RESET, KEYLARGO_GPIO_OUTPUT_ENABLE);
-			(void)KL_GPIO_IN(KL_GPIO_MODEM_RESET);
-			udelay(10);
-			/* Power up modem */
-			KL_GPIO_OUT(KL_GPIO_MODEM_POWER, KEYLARGO_GPIO_OUTPUT_ENABLE);
-			(void)KL_GPIO_IN(KL_GPIO_MODEM_POWER);
-			udelay(10);
-			/* Release modem reset */
-			KL_GPIO_OUT(KL_GPIO_MODEM_RESET,
-				KEYLARGO_GPIO_OUTPUT_ENABLE | KEYLARGO_GPIO_OUTOUT_DATA);
-			(void)KL_GPIO_IN(KL_GPIO_MODEM_RESET);
-		} else {
-			/* Power down modem */
-			KL_GPIO_OUT(KL_GPIO_MODEM_POWER,
-				KEYLARGO_GPIO_OUTPUT_ENABLE | KEYLARGO_GPIO_OUTOUT_DATA);
-			(void)KL_GPIO_IN(KL_GPIO_MODEM_POWER);
-		}
-		KL_UNLOCK();
-	} else {
-		if (power) {
-			mdelay(300);
-			feature_set(device, FEATURE_Modem_power);
-	   		mdelay(5);
-			feature_clear(device, FEATURE_Modem_power);
-	   		mdelay(10);
-			feature_set(device, FEATURE_Modem_power);
-		} else {
-			feature_clear(device, FEATURE_Modem_power);
-			mdelay(10);
-		}
-	}
-}
-
-#ifdef CONFIG_SMP
-void __pmac
-feature_core99_kick_cpu(int cpu_nr)
-{
-	const int reset_lines[] = {	KL_GPIO_RESET_CPU0,
-					KL_GPIO_RESET_CPU1,
-					KL_GPIO_RESET_CPU2,
-					KL_GPIO_RESET_CPU3 };
-	int reset_io;
-	unsigned long flags;
-	
-	if (!keylargo_base || cpu_nr > 3)
-		return;
-	reset_io = reset_lines[cpu_nr];
-	
-	KL_LOCK();
-	KL_GPIO_OUT(reset_io, KEYLARGO_GPIO_OUTPUT_ENABLE);
-	(void)KL_GPIO_IN(reset_io);
-	udelay(1);
-	KL_GPIO_OUT(reset_io, KEYLARGO_GPIO_OUTPUT_ENABLE | KEYLARGO_GPIO_OUTOUT_DATA);
-	(void)KL_GPIO_IN(reset_io);
-	KL_UNLOCK();
-}
-#endif /* CONFIG_SMP */
-
-void __pmac
-feature_set_airport_power(struct device_node* device, int power)
-{
-	unsigned long flags;
-	
-	if (!keylargo_base || !airport_dev || airport_dev != device)
-		return;
-	if (airport_pwr_state == power)
-		return;	
-	if (power) {
-		/* This code is a reproduction of OF enable-cardslot
-		 * and init-wireless methods, slightly hacked until
-		 * I got it working.
-		 */
-		KL_LOCK();
-		KL_GPIO_OUT(KEYLARGO_GPIO_0+0xf, 5);
-		(void)KL_GPIO_IN(KEYLARGO_GPIO_0+0xf);
-		KL_UNLOCK();
-		mdelay(10);
-		KL_LOCK();
-		KL_GPIO_OUT(KEYLARGO_GPIO_0+0xf, 4);
-		(void)KL_GPIO_IN(KEYLARGO_GPIO_0+0xf);
-		KL_UNLOCK();
-
-		mdelay(10);
-
-		KL_LOCK();
-		KL_BIC(KEYLARGO_FCR2, KL2_AIRPORT_RESET_N);
-		(void)KL_IN(KEYLARGO_FCR2);
-		udelay(10);
-		KL_GPIO_OUT(KEYLARGO_GPIO_EXTINT_0+0xb, 0);
-		(void)KL_GPIO_IN(KEYLARGO_GPIO_EXTINT_0+0xb);
-		udelay(10);
-		KL_GPIO_OUT(KEYLARGO_GPIO_EXTINT_0+0xa, 0x28);
-		(void)KL_GPIO_IN(KEYLARGO_GPIO_EXTINT_0+0xa);
-		udelay(10);
-		KL_GPIO_OUT(KEYLARGO_GPIO_EXTINT_0+0xd, 0x28);
-		(void)KL_GPIO_IN(KEYLARGO_GPIO_EXTINT_0+0xd);
-		udelay(10);
-		KL_GPIO_OUT(KEYLARGO_GPIO_0+0xd, 0x28);
-		(void)KL_GPIO_IN(KEYLARGO_GPIO_0+0xd);
-		udelay(10);
-		KL_GPIO_OUT(KEYLARGO_GPIO_0+0xe, 0x28);
-		(void)KL_GPIO_IN(KEYLARGO_GPIO_0+0xe);
-		KL_UNLOCK();
-		udelay(10);
-		KL_OUT(0x1c000, 0);
-		
-		mdelay(1);
-		out_8((volatile u8*)KL_FCR(0x1a3e0), 0x41);
-		(void)in_8((volatile u8*)KL_FCR(0x1a3e0));
-		udelay(10);
-		KL_LOCK();
-		KL_BIS(KEYLARGO_FCR2, KL2_AIRPORT_RESET_N);
-		(void)KL_IN(KEYLARGO_FCR2);
-		KL_UNLOCK();
-		mdelay(100);
-	} else {
-		KL_LOCK();
-		KL_BIC(KEYLARGO_FCR2, KL2_AIRPORT_RESET_N);
-		(void)KL_IN(KEYLARGO_FCR2);
-		KL_GPIO_OUT(KL_GPIO_AIRPORT_0, 0);
-		KL_GPIO_OUT(KL_GPIO_AIRPORT_1, 0);
-		KL_GPIO_OUT(KL_GPIO_AIRPORT_2, 0);
-		KL_GPIO_OUT(KL_GPIO_AIRPORT_3, 0);
-		KL_GPIO_OUT(KL_GPIO_AIRPORT_4, 0);
-		(void)KL_GPIO_IN(KL_GPIO_AIRPORT_4);
-		KL_UNLOCK();
-	}
-	airport_pwr_state = power;	
-}
-
-/* Initialize the Core99 UniNorth host bridge and memory controller
- */
-static void __init
-uninorth_init(void)
-{
-	struct device_node* gmac, *fw;
-	unsigned long actrl;
-	
-	/* Set the arbitrer QAck delay according to what Apple does
-	 */
-	if (uninorth_rev < 0x10) {
-		actrl = UN_IN(UNI_N_ARB_CTRL) & ~UNI_N_ARB_CTRL_QACK_DELAY_MASK;
-		actrl |= ((uninorth_rev < 3) ? UNI_N_ARB_CTRL_QACK_DELAY105 :
-			UNI_N_ARB_CTRL_QACK_DELAY) << UNI_N_ARB_CTRL_QACK_DELAY_SHIFT;
-		UN_OUT(UNI_N_ARB_CTRL, actrl);
-	}
-	
-	/* Enable GMAC for now for PCI probing. It will be disabled
-	 * later on after PCI probe
-	 */
-	gmac = find_devices("ethernet");
-	while(gmac) {
-		if (device_is_compatible(gmac, "gmac"))
-			break;
-		gmac = gmac->next;
-	}
-	if (gmac)
-		feature_set_gmac_power(gmac, 1);
-
-	/* Enable FW before PCI probe. Will be disabled later on
-	 */
-	fw = find_devices("firewire");
-	if (fw && (device_is_compatible(fw, "pci106b,18") || 
-			device_is_compatible(fw, "pci106b,30"))) {
-		uninorth_fw = fw;
-		feature_set_firewire_power(fw, 1);
-	}
-}
-
-/* Initialize the Core99 KeyLargo ASIC. 
- */
-static void __init
-keylargo_init(void)
-{
-	struct device_node* np;
-	
-	KL_BIS(KEYLARGO_FCR2, KL2_MPIC_ENABLE);
-
-	/* Lookup for an airport card, and disable it if found
-	 * to save power (will be re-enabled by driver if used)
-	 */
-	np = find_devices("radio");
-	if (np && np->parent == keylargo->device)
-		airport_dev = np;
-
-	if (airport_dev) {
-		airport_pwr_state = 1;
-		feature_set_airport_power(airport_dev, 0);
-	}
-	
-}
-
-#ifdef CONFIG_PMAC_PBOOK
-void __pmac
-feature_prepare_for_sleep(void)
-{
-	/* We assume gatwick is second */
-	struct feature_controller* ctrler = &controllers[0];
-
-	if (controller_count > 1 &&
-		device_is_compatible(ctrler->device, "gatwick"))
-		ctrler = &controllers[1];
-
-	if (ctrler->bits == feature_bits_wallstreet ||
-		ctrler->bits == feature_bits_paddington) {
-		heathrow_prepare_for_sleep(ctrler);
-		return;
-	}
-	if (ctrler->bits == feature_bits_keylargo) {
-		core99_prepare_for_sleep(ctrler);
-		return;
-	}
-}
-
-void __pmac
-feature_wake_up(void)
-{
-	struct feature_controller* ctrler = &controllers[0];
-
-	if (controller_count > 1 &&
-		device_is_compatible(ctrler->device, "gatwick"))
-		ctrler = &controllers[1];
-	
-	if (ctrler->bits == feature_bits_wallstreet ||
-		ctrler->bits == feature_bits_paddington) {
-		heathrow_wakeup(ctrler);
-		return;
-	}
-	if (ctrler->bits == feature_bits_keylargo) {
-		core99_wake_up(ctrler);
-		return;
-	}
-}
-
-static u32 save_fcr[5];
-static u32 save_mbcr;
-static u32 save_gpio_levels[2];
-static u8 save_gpio_extint[KEYLARGO_GPIO_EXTINT_CNT];
-static u8 save_gpio_normal[KEYLARGO_GPIO_CNT];
-static u32 save_unin_clock_ctl;
-static struct dbdma_regs save_dbdma[13];
-
-static void __pmac
-heathrow_prepare_for_sleep(struct feature_controller* ctrler)
-{
-	save_mbcr = in_le32(FREG(ctrler, 0x34));
-	save_fcr[0] = in_le32(FREG(ctrler, 0x38));
-	save_fcr[1] = in_le32(FREG(ctrler, 0x3c));
-
-	out_le32(FREG(ctrler, 0x38), save_fcr[0] & ~HRW_IOBUS_ENABLE);
-}
-
-static void __pmac
-heathrow_wakeup(struct feature_controller* ctrler)
-{
-	out_le32(FREG(ctrler, 0x38), save_fcr[0]);
-	out_le32(FREG(ctrler, 0x3c), save_fcr[1]);
-	out_le32(FREG(ctrler, 0x34), save_mbcr);
-	mdelay(1);
-	out_le32(FREG(ctrler, 0x38), save_fcr[0] | HRW_IOBUS_ENABLE);
-	mdelay(1);
-}
-
-static void __pmac
-turn_off_keylargo(void)
-{
-	u32 temp;
-
-	mdelay(1);
-	KL_BIS(KEYLARGO_FCR0, KL0_USB_REF_SUSPEND);
-	(void)KL_IN(KEYLARGO_FCR0);
-	mdelay(100);
-
-	KL_BIC(KEYLARGO_FCR0,	KL0_SCCA_ENABLE | KL0_SCCB_ENABLE |
-				KL0_SCC_CELL_ENABLE |
-		      		KL0_IRDA_ENABLE | KL0_IRDA_CLK32_ENABLE |
-		      		KL0_IRDA_CLK19_ENABLE);
-
-	(void)KL_IN(KEYLARGO_FCR0); udelay(10);
-	KL_BIS(KEYLARGO_MBCR, KL_MBCR_MB0_DEV_ENABLE);
-	(void)KL_IN(KEYLARGO_MBCR); udelay(10);
-
-	KL_BIC(KEYLARGO_FCR1,
-		KL1_AUDIO_SEL_22MCLK | KL1_AUDIO_CLK_ENABLE_BIT |
-		KL1_AUDIO_CLK_OUT_ENABLE | KL1_AUDIO_CELL_ENABLE |
-		KL1_I2S0_CELL_ENABLE | KL1_I2S0_CLK_ENABLE_BIT |
-		KL1_I2S0_ENABLE | KL1_I2S1_CELL_ENABLE |
-		KL1_I2S1_CLK_ENABLE_BIT | KL1_I2S1_ENABLE |
-		KL1_EIDE0_ENABLE | KL1_EIDE0_RESET_N |
-		KL1_EIDE1_ENABLE | KL1_EIDE1_RESET_N |
-		KL1_UIDE_ENABLE);
-	(void)KL_IN(KEYLARGO_FCR1); udelay(10);
-
-	KL_BIS(KEYLARGO_FCR2, KL2_MODEM_POWER_N);
- 	udelay(10);
- 	KL_BIC(KEYLARGO_FCR2, KL2_IOBUS_ENABLE);
- 	udelay(10);
-	temp = KL_IN(KEYLARGO_FCR3);
-	if (keylargo_rev >= 2)
-		temp |= (KL3_SHUTDOWN_PLL2X | KL3_SHUTDOWN_PLL_TOTAL);
-		
-	temp |= KL3_SHUTDOWN_PLLKW6 | KL3_SHUTDOWN_PLLKW4 |
-		KL3_SHUTDOWN_PLLKW35 | KL3_SHUTDOWN_PLLKW12;
-	temp &= ~(KL3_CLK66_ENABLE | KL3_CLK49_ENABLE | KL3_CLK45_ENABLE
-		| KL3_CLK31_ENABLE | KL3_TIMER_CLK18_ENABLE | KL3_I2S1_CLK18_ENABLE
-		| KL3_I2S0_CLK18_ENABLE | KL3_VIA_CLK16_ENABLE);
-	KL_OUT(KEYLARGO_FCR3, temp);
-	(void)KL_IN(KEYLARGO_FCR3); udelay(10);
-}
-
-static void __pmac
-turn_off_pangea(void)
-{
-	u32 temp;
-
-	KL_BIC(KEYLARGO_FCR0,	KL0_SCCA_ENABLE | KL0_SCCB_ENABLE |
-				KL0_SCC_CELL_ENABLE |
-				KL0_USB0_CELL_ENABLE | KL0_USB1_CELL_ENABLE);
-
-	(void)KL_IN(KEYLARGO_FCR0); udelay(10);
-	KL_BIS(KEYLARGO_MBCR, KL_MBCR_MB0_DEV_ENABLE);
-	(void)KL_IN(KEYLARGO_MBCR); udelay(10);
-
-	KL_BIC(KEYLARGO_FCR1,
-		KL1_AUDIO_SEL_22MCLK | KL1_AUDIO_CLK_ENABLE_BIT |
-		KL1_AUDIO_CLK_OUT_ENABLE | KL1_AUDIO_CELL_ENABLE |
-		KL1_I2S0_CELL_ENABLE | KL1_I2S0_CLK_ENABLE_BIT |
-		KL1_I2S0_ENABLE | KL1_I2S1_CELL_ENABLE |
-		KL1_I2S1_CLK_ENABLE_BIT | KL1_I2S1_ENABLE |
-		KL1_UIDE_ENABLE);
-	(void)KL_IN(KEYLARGO_FCR1); udelay(10);
-
-	KL_BIS(KEYLARGO_FCR2, KL2_MODEM_POWER_N);
- 	udelay(10);
-	temp = KL_IN(KEYLARGO_FCR3);
-	temp |= KL3_SHUTDOWN_PLLKW6 | KL3_SHUTDOWN_PLLKW4 |
-		KL3_SHUTDOWN_PLLKW35;
-	temp &= ~(KL3_CLK49_ENABLE | KL3_CLK45_ENABLE
-		| KL3_CLK31_ENABLE | KL3_TIMER_CLK18_ENABLE | KL3_I2S1_CLK18_ENABLE
-		| KL3_I2S0_CLK18_ENABLE | KL3_VIA_CLK16_ENABLE);
-	KL_OUT(KEYLARGO_FCR3, temp);
-	(void)KL_IN(KEYLARGO_FCR3); udelay(10);
-}
-
-static void __pmac
-core99_prepare_for_sleep(struct feature_controller* ctrler)
-{
-	int i;
-	volatile u8* base8;
-	
-	/*
-	 * Save various bits of KeyLargo
-	 */
-
-	/* We power off the wireless slot in case it was not done
-	 * by the driver. We don't power it on automatically however
-	 */
-	feature_set_airport_power(airport_dev, 0);
-
-	/* We power off the FW cable. Should be done by the driver... */
-	feature_set_firewire_power(NULL, 0);
-	feature_set_firewire_cable_power(NULL, 0);
-
-	/* We make sure int. modem is off (in case driver lost it) */
-	feature_set_modem_power(NULL, 0);
-	 
-	/* Save the state of the various GPIOs */
-	save_gpio_levels[0] = KL_IN(KEYLARGO_GPIO_LEVELS0);
-	save_gpio_levels[1] = KL_IN(KEYLARGO_GPIO_LEVELS1);
-	base8 = ((volatile u8 *)keylargo_base) + KEYLARGO_GPIO_EXTINT_0;
-	for (i=0; i<KEYLARGO_GPIO_EXTINT_CNT; i++)
-		save_gpio_extint[i] = in_8(base8+i);
-	base8 = ((volatile u8 *)keylargo_base) + KEYLARGO_GPIO_0;
-	for (i=0; i<KEYLARGO_GPIO_CNT; i++)
-		save_gpio_normal[i] = in_8(base8+i);
-
-	/* Save the FCRs */
-	save_mbcr = KL_IN(KEYLARGO_MBCR);
-	save_fcr[0] = KL_IN(KEYLARGO_FCR0);
-	save_fcr[1] = KL_IN(KEYLARGO_FCR1);
-	save_fcr[2] = KL_IN(KEYLARGO_FCR2);
-	save_fcr[3] = KL_IN(KEYLARGO_FCR3);
-	save_fcr[4] = KL_IN(KEYLARGO_FCR4);
-
-	/* Save state & config of DBDMA channels */
-	for (i=0; i<13; i++) {
-		volatile struct dbdma_regs* chan = (volatile struct dbdma_regs*)
-			(keylargo_base + ((0x8000+i*0x100)>>2));
-		save_dbdma[i].cmdptr_hi = in_le32(&chan->cmdptr_hi);
-		save_dbdma[i].cmdptr = in_le32(&chan->cmdptr);
-		save_dbdma[i].intr_sel = in_le32(&chan->intr_sel);
-		save_dbdma[i].br_sel = in_le32(&chan->br_sel);
-		save_dbdma[i].wait_sel = in_le32(&chan->wait_sel);
-	}
-
-	/*
-	 * Turn off as much as we can
-	 */
-	if (keylargo_rev & KL_PANGEA_REV)
-		turn_off_pangea();
-	else
-		turn_off_keylargo();
-	
-	/* 
-	 * Put the host bridge to sleep
-	 */
-
-	save_unin_clock_ctl = UN_IN(UNI_N_CLOCK_CNTL);
-	UN_OUT(UNI_N_CLOCK_CNTL, save_unin_clock_ctl &
-		~(UNI_N_CLOCK_CNTL_GMAC|UNI_N_CLOCK_CNTL_FW/*|UNI_N_CLOCK_CNTL_PCI*/));
-	udelay(100);
-	UN_OUT(UNI_N_HWINIT_STATE, UNI_N_HWINIT_STATE_SLEEPING);
-	UN_OUT(UNI_N_POWER_MGT, UNI_N_POWER_MGT_SLEEP);
-
-	/*
-	 * FIXME: A bit of black magic with OpenPIC (don't ask me why)
-	 */
-	if (board_features & FTR_NEED_OPENPIC_TWEAK) {
-		KL_BIS(0x506e0, 0x00400000);
-		KL_BIS(0x506e0, 0x80000000);
-	}
-}
-
-static void __pmac
-core99_wake_up(struct feature_controller* ctrler)
-{
-	int i;
-	volatile u8* base8;
-
-	/*
-	 * Wakeup the host bridge
-	 */
-	UN_OUT(UNI_N_POWER_MGT, UNI_N_POWER_MGT_NORMAL);
-	udelay(10);
-	UN_OUT(UNI_N_HWINIT_STATE, UNI_N_HWINIT_STATE_RUNNING);
-	udelay(10);
-	
-	/*
-	 * Restore KeyLargo
-	 */
-	 
-	KL_OUT(KEYLARGO_MBCR, save_mbcr);
-	(void)KL_IN(KEYLARGO_MBCR); udelay(10);
-	KL_OUT(KEYLARGO_FCR0, save_fcr[0]);
-	(void)KL_IN(KEYLARGO_FCR0); udelay(10);
-	KL_OUT(KEYLARGO_FCR1, save_fcr[1]);
-	(void)KL_IN(KEYLARGO_FCR1); udelay(10);
-	KL_OUT(KEYLARGO_FCR2, save_fcr[2]);
-	(void)KL_IN(KEYLARGO_FCR2); udelay(10);
-	KL_OUT(KEYLARGO_FCR3, save_fcr[3]);
-	(void)KL_IN(KEYLARGO_FCR3); udelay(10);
-	KL_OUT(KEYLARGO_FCR4, save_fcr[4]);
-	(void)KL_IN(KEYLARGO_FCR4); udelay(10);
-
-	for (i=0; i<13; i++) {
-		volatile struct dbdma_regs* chan = (volatile struct dbdma_regs*)
-			(keylargo_base + ((0x8000+i*0x100)>>2));
-		out_le32(&chan->control, (ACTIVE|DEAD|WAKE|FLUSH|PAUSE|RUN)<<16);
-		while (in_le32(&chan->status) & ACTIVE)
-			mb();
-		out_le32(&chan->cmdptr_hi, save_dbdma[i].cmdptr_hi);
-		out_le32(&chan->cmdptr, save_dbdma[i].cmdptr);
-		out_le32(&chan->intr_sel, save_dbdma[i].intr_sel);
-		out_le32(&chan->br_sel, save_dbdma[i].br_sel);
-		out_le32(&chan->wait_sel, save_dbdma[i].wait_sel);
-	}
-
-	KL_OUT(KEYLARGO_GPIO_LEVELS0, save_gpio_levels[0]);
-	KL_OUT(KEYLARGO_GPIO_LEVELS1, save_gpio_levels[1]);
-	base8 = ((volatile u8 *)keylargo_base) + KEYLARGO_GPIO_EXTINT_0;
-	for (i=0; i<KEYLARGO_GPIO_EXTINT_CNT; i++)
-		out_8(base8+i, save_gpio_extint[i]);
-	base8 = ((volatile u8 *)keylargo_base) + KEYLARGO_GPIO_0;
-	for (i=0; i<KEYLARGO_GPIO_CNT; i++)
-		out_8(base8+i, save_gpio_normal[i]);
-
-	/* FIXME more black magic with OpenPIC ... */
-	if (board_features & FTR_NEED_OPENPIC_TWEAK) {
-		KL_BIC(0x506e0, 0x00400000);
-		KL_BIC(0x506e0, 0x80000000);
-	}
-
-	UN_OUT(UNI_N_CLOCK_CNTL, save_unin_clock_ctl);
-	udelay(100);
-}
-#endif /* CONFIG_PMAC_PBOOK */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/kernel/head.S linux/arch/ppc/kernel/head.S
--- linux.orig/arch/ppc/kernel/head.S	Mon Jan  7 23:41:08 2002
+++ linux/arch/ppc/kernel/head.S	Wed Dec 26 16:28:34 2001
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.head.S 1.31 10/18/01 15:02:09 trini
+ * BK Id: SCCS/s.head.S 1.34 12/02/01 11:35:27 benh
  */
 /*
  *  PowerPC version 
@@ -1347,7 +1347,19 @@
 	bl	setup_750_7400_hid0
 	mtlr	r4
 	blr
+_GLOBAL(__setup_cpu_7410)
+	mflr	r4
+	bl	setup_common_caches
+	bl	setup_750_7400_hid0
+	li	r3,0
+	mtspr	SPRN_L2CR2,r3
+	mtlr	r4
+	blr
 _GLOBAL(__setup_cpu_7450)
+	mflr	r4
+	bl	setup_common_caches
+	bl	setup_7450_hid0
+	mtlr	r4
 	blr
 _GLOBAL(__setup_cpu_power3)
 	blr
@@ -1400,6 +1412,47 @@
 	li	r3,HID0_SPD
 	andc	r11,r11,r3		/* clear SPD: enable speculative */
  	li	r3,0
+ 	mtspr	ICTC,r3			/* Instruction Cache Throttling off */
+	isync
+	mtspr	HID0,r11
+	sync
+	isync
+	blr
+
+/* 7450
+ * Enable Store Gathering (SGE), Branch Folding (FOLD)
+ * Branch History Table (BHTE), Branch Target ICache (BTIC)
+ * Dynamic Power Management (DPM), Speculative (SPD)
+ * Ensure our data cache instructions really operate.
+ * Timebase has to be running or we wouldn't have made it here,
+ * just ensure we don't disable it.
+ * Clear Instruction cache throttling (ICTC)
+ */
+setup_7450_hid0:
+	/* We check for the presence of an L3 cache setup by
+	 * the firmware. If any, we disable DOZE capability
+	 */
+	mfspr	r11,SPRN_L3CR
+	andis.	r11,r11,L3CR_L3E@h
+	beq	1f
+	li	r7,CPU_FTR_CAN_DOZE
+	lwz	r6,CPU_SPEC_FEATURES(r5)
+	andc	r6,r6,r7
+	stw	r6,CPU_SPEC_FEATURES(r5)
+1:	
+	mfspr	r11,HID0
+
+	/* All of the bits we have to set.....
+	*/
+	ori	r11,r11,HID0_SGE | HID0_FOLD | HID0_BHTE | HID0_BTIC
+	oris	r11,r11,HID0_DPM@h	/* enable dynamic power mgmt */
+
+	/* All of the bits we have to clear....
+	*/
+	li	r3,HID0_SPD | HID0_NOPDST | HID0_NOPTI
+	andc	r11,r11,r3		/* clear SPD: enable speculative */
+ 	li	r3,0
+
  	mtspr	ICTC,r3			/* Instruction Cache Throttling off */
 	isync
 	mtspr	HID0,r11
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/kernel/i8259.c linux/arch/ppc/kernel/i8259.c
--- linux.orig/arch/ppc/kernel/i8259.c	Mon Jan  7 23:41:08 2002
+++ linux/arch/ppc/kernel/i8259.c	Wed Dec 26 16:28:34 2001
@@ -1,14 +1,18 @@
 /*
- * BK Id: SCCS/s.i8259.c 1.7 05/17/01 18:14:21 cort
+ * BK Id: SCCS/s.i8259.c 1.11 12/19/01 09:45:54 trini
  */
 
 #include <linux/stddef.h>
 #include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/ioport.h>
 #include <linux/sched.h>
 #include <linux/signal.h>
 #include <asm/io.h>
 #include "i8259.h"
 
+static volatile char *pci_intack; /* RO, gives us the irq vector */
+
 unsigned char cached_8259[2] = { 0xff, 0xff };
 #define cached_A1 (cached_8259[0])
 #define cached_21 (cached_8259[1])
@@ -17,32 +21,56 @@
 
 int i8259_pic_irq_offset;
 
-int i8259_irq(int cpu)
+/* Acknowledge the irq using the PCI host bridge's interrupt acknowledge
+ * feature. (Polling is somehow broken on some IBM and Motorola PReP boxes.)
+ */
+int i8259_irq(void)
+{
+	int irq;
+
+	spin_lock/*_irqsave*/(&i8259_lock/*, flags*/);
+
+	irq = *pci_intack & 0xff;
+	if (irq==7) {
+		/*
+		 * This may be a spurious interrupt.
+		 *
+		 * Read the interrupt status register (ISR). If the most
+		 * significant bit is not set then there is no valid
+		 * interrupt.
+		 */
+		if(~inb(0x20)&0x80) {
+			irq = -1;
+		}
+	}
+	spin_unlock/*_irqrestore*/(&i8259_lock/*, flags*/);
+	return irq;
+}
+
+/* Poke the 8259's directly using poll commands. */
+int i8259_poll(void)
 {
 	int irq;
-	
+
 	spin_lock/*_irqsave*/(&i8259_lock/*, flags*/);
-        /*
-         * Perform an interrupt acknowledge cycle on controller 1
-         */                                                             
-        outb(0x0C, 0x20);
-        irq = inb(0x20) & 7;                                   
-        if (irq == 2)                                                     
-        {                                                                   
-                /*                                     
-                 * Interrupt is cascaded so perform interrupt
-                 * acknowledge on controller 2
-                 */
-                outb(0x0C, 0xA0);                      
-                irq = (inb(0xA0) & 7) + 8;
-        }
-        else if (irq==7)                                
-        {
-                /*                               
-                 * This may be a spurious interrupt
-                 *                         
-                 * Read the interrupt status register. If the most
-                 * significant bit is not set then there is no valid
+	/*
+	 * Perform an interrupt acknowledge cycle on controller 1
+	 */
+	outb(0x0C, 0x20); /* prepare for poll */
+	irq = inb(0x20) & 7;
+	if (irq == 2) {
+		/*
+		 * Interrupt is cascaded so perform interrupt
+		 * acknowledge on controller 2
+		 */
+		outb(0x0C, 0xA0); /* prepare for poll */
+		irq = (inb(0xA0) & 7) + 8;
+	} else if (irq==7) {
+		/*
+		 * This may be a spurious interrupt
+		 *
+		 * Read the interrupt status register. If the most
+		 * significant bit is not set then there is no valid
 		 * interrupt
 		 */
 		outb(0x0b, 0x20);
@@ -58,44 +86,44 @@
 static void i8259_mask_and_ack_irq(unsigned int irq_nr)
 {
 	unsigned long flags;
-	
+
 	spin_lock_irqsave(&i8259_lock, flags);
-        if ( irq_nr >= i8259_pic_irq_offset )
-                irq_nr -= i8259_pic_irq_offset;
+	if ( irq_nr >= i8259_pic_irq_offset )
+		irq_nr -= i8259_pic_irq_offset;
 
-        if (irq_nr > 7) {                                                   
-                cached_A1 |= 1 << (irq_nr-8);                                   
-                inb(0xA1);      /* DUMMY */                                     
-                outb(cached_A1,0xA1);                                           
-                outb(0x20,0xA0);        /* Non-specific EOI */             
-                outb(0x20,0x20);        /* Non-specific EOI to cascade */
-        } else {                                                            
-                cached_21 |= 1 << irq_nr;                                   
-                inb(0x21);      /* DUMMY */                                 
-                outb(cached_21,0x21);
-                outb(0x20,0x20);        /* Non-specific EOI */                 
-        }                                                                
+	if (irq_nr > 7) {
+		cached_A1 |= 1 << (irq_nr-8);
+		inb(0xA1); /* DUMMY */
+		outb(cached_A1,0xA1);
+		outb(0x20,0xA0); /* Non-specific EOI */
+		outb(0x20,0x20); /* Non-specific EOI to cascade */
+	} else {
+		cached_21 |= 1 << irq_nr;
+		inb(0x21); /* DUMMY */
+		outb(cached_21,0x21);
+		outb(0x20,0x20); /* Non-specific EOI */
+	}
 	spin_unlock_irqrestore(&i8259_lock, flags);
 }
 
 static void i8259_set_irq_mask(int irq_nr)
 {
-        outb(cached_A1,0xA1);
-        outb(cached_21,0x21);
+	outb(cached_A1,0xA1);
+	outb(cached_21,0x21);
 }
-
+ 
 static void i8259_mask_irq(unsigned int irq_nr)
 {
 	unsigned long flags;
 
 	spin_lock_irqsave(&i8259_lock, flags);
-        if ( irq_nr >= i8259_pic_irq_offset )
-                irq_nr -= i8259_pic_irq_offset;
-        if ( irq_nr < 8 )
-                cached_21 |= 1 << irq_nr;
-        else
-                cached_A1 |= 1 << (irq_nr-8);
-        i8259_set_irq_mask(irq_nr);
+	if ( irq_nr >= i8259_pic_irq_offset )
+		irq_nr -= i8259_pic_irq_offset;
+	if ( irq_nr < 8 )
+		cached_21 |= 1 << irq_nr;
+	else
+		cached_A1 |= 1 << (irq_nr-8);
+	i8259_set_irq_mask(irq_nr);
 	spin_unlock_irqrestore(&i8259_lock, flags);
 }
 
@@ -104,13 +132,13 @@
 	unsigned long flags;
 
 	spin_lock_irqsave(&i8259_lock, flags);
-        if ( irq_nr >= i8259_pic_irq_offset )
-                irq_nr -= i8259_pic_irq_offset;
-        if ( irq_nr < 8 )
-                cached_21 &= ~(1 << irq_nr);
-        else
-                cached_A1 &= ~(1 << (irq_nr-8));
-        i8259_set_irq_mask(irq_nr);
+	if ( irq_nr >= i8259_pic_irq_offset )
+		irq_nr -= i8259_pic_irq_offset;
+	if ( irq_nr < 8 )
+		cached_21 &= ~(1 << irq_nr);
+	else
+		cached_A1 &= ~(1 << (irq_nr-8));
+	i8259_set_irq_mask(irq_nr);
 	spin_unlock_irqrestore(&i8259_lock, flags);
 }
 
@@ -121,36 +149,62 @@
 }
 
 struct hw_interrupt_type i8259_pic = {
-        " i8259    ",
-        NULL,
-        NULL,
-        i8259_unmask_irq,
-        i8259_mask_irq,
-        i8259_mask_and_ack_irq,
-        i8259_end_irq,
-        NULL
+	" i8259    ",
+	NULL,
+	NULL,
+	i8259_unmask_irq,
+	i8259_mask_irq,
+	i8259_mask_and_ack_irq,
+	i8259_end_irq,
+	NULL
+};
+
+static struct resource pic1_iores = {
+	"8259 (master)", 0x20, 0x21, IORESOURCE_BUSY
 };
 
-void __init i8259_init(void)
+static struct resource pic2_iores = {
+	"8259 (slave)", 0xa0, 0xa1, IORESOURCE_BUSY
+};
+
+static struct resource pic_edgectrl_iores = {
+	"8259 edge control", 0x4d0, 0x4d1, IORESOURCE_BUSY
+};
+
+void __init i8259_init(long intack_addr)
 {
 	unsigned long flags;
-	
+
 	spin_lock_irqsave(&i8259_lock, flags);
-        /* init master interrupt controller */
-        outb(0x11, 0x20); /* Start init sequence */
-        outb(0x00, 0x21); /* Vector base */
-        outb(0x04, 0x21); /* edge tiggered, Cascade (slave) on IRQ2 */
-        outb(0x01, 0x21); /* Select 8086 mode */
-        outb(0xFF, 0x21); /* Mask all */
-        /* init slave interrupt controller */
-        outb(0x11, 0xA0); /* Start init sequence */
-        outb(0x08, 0xA1); /* Vector base */
-        outb(0x02, 0xA1); /* edge triggered, Cascade (slave) on IRQ2 */
-        outb(0x01, 0xA1); /* Select 8086 mode */
-        outb(0xFF, 0xA1); /* Mask all */
-        outb(cached_A1, 0xA1);
-        outb(cached_21, 0x21);
+	/* init master interrupt controller */
+	outb(0x11, 0x20); /* Start init sequence */
+	outb(0x00, 0x21); /* Vector base */
+	outb(0x04, 0x21); /* edge tiggered, Cascade (slave) on IRQ2 */
+	outb(0x01, 0x21); /* Select 8086 mode */
+
+	/* init slave interrupt controller */
+	outb(0x11, 0xA0); /* Start init sequence */
+	outb(0x08, 0xA1); /* Vector base */
+	outb(0x02, 0xA1); /* edge triggered, Cascade (slave) on IRQ2 */
+	outb(0x01, 0xA1); /* Select 8086 mode */
+
+	/* always read ISR */
+	outb(0x0B, 0x20);
+	outb(0x0B, 0xA0);
+
+	/* Mask all interrupts */
+	outb(cached_A1, 0xA1);
+	outb(cached_21, 0x21);
+
 	spin_unlock_irqrestore(&i8259_lock, flags);
-        request_irq( i8259_pic_irq_offset + 2, no_action, SA_INTERRUPT,
-                     "82c59 secondary cascade", NULL );
+
+	/* reserve our resources */
+	request_irq( i8259_pic_irq_offset + 2, no_action, SA_INTERRUPT,
+				"82c59 secondary cascade", NULL );
+	request_resource(&ioport_resource, &pic1_iores);
+	request_resource(&ioport_resource, &pic2_iores);
+	request_resource(&ioport_resource, &pic_edgectrl_iores);
+
+	if (intack_addr)
+		pci_intack = ioremap(intack_addr, 1);
 }
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/kernel/i8259.h linux/arch/ppc/kernel/i8259.h
--- linux.orig/arch/ppc/kernel/i8259.h	Mon Jan  7 23:41:08 2002
+++ linux/arch/ppc/kernel/i8259.h	Wed Dec 26 16:28:34 2001
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.i8259.h 1.5 05/17/01 18:14:21 cort
+ * BK Id: SCCS/s.i8259.h 1.8 12/19/01 09:45:54 trini
  */
 
 #ifndef _PPC_KERNEL_i8259_H
@@ -9,7 +9,8 @@
 
 extern struct hw_interrupt_type i8259_pic;
 
-void i8259_init(void);
-int i8259_irq(int);
+void i8259_init(long);
+int i8259_irq(void);
+int i8259_poll(void);
 
 #endif /* _PPC_KERNEL_i8259_H */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/kernel/idle.c linux/arch/ppc/kernel/idle.c
--- linux.orig/arch/ppc/kernel/idle.c	Mon Jan  7 23:41:08 2002
+++ linux/arch/ppc/kernel/idle.c	Wed Dec 26 16:28:34 2001
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.idle.c 1.16 10/16/01 15:58:42 trini
+ * BK Id: SCCS/s.idle.c 1.18 12/01/01 20:09:06 benh
  */
 /*
  * Idle daemon for PowerPC.  Idle daemon will handle any action
@@ -58,7 +58,6 @@
 	init_idle();
 	for (;;) {
 #ifdef CONFIG_SMP
-
 		if (!do_power_save) {
 			/*
 			 * Deal with another CPU just having chosen a thread to
@@ -231,6 +230,13 @@
 void power_save(void)
 {
 	unsigned long hid0;
+	int nap = powersave_nap;
+	
+	/* 7450 has no DOZE mode mode, we return if powersave_nap
+	 * isn't enabled
+	 */
+	if (!nap &&  cur_cpu_spec[smp_processor_id()]->cpu_features & CPU_FTR_SPEC7450)
+		return;
 	/*
 	 * Disable interrupts to prevent a lost wakeup
 	 * when going to sleep.  This is necessary even with
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/kernel/irq.c linux/arch/ppc/kernel/irq.c
--- linux.orig/arch/ppc/kernel/irq.c	Mon Jan  7 23:41:08 2002
+++ linux/arch/ppc/kernel/irq.c	Wed Dec 26 16:28:34 2001
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.irq.c 1.32 08/24/01 20:07:37 paulus
+ * BK Id: SCCS/s.irq.c 1.34 12/01/01 20:09:06 benh
  */
 /*
  *  arch/ppc/kernel/irq.c
@@ -537,21 +537,29 @@
 int do_IRQ(struct pt_regs *regs)
 {
 	int cpu = smp_processor_id();
-	int irq;
-        hardirq_enter(cpu);
+	int irq, first = 1;
+        hardirq_enter( cpu );
 
-	/* every arch is required to have a get_irq -- Cort */
-	irq = ppc_md.get_irq(regs);
+	for (;;) {
+		/*
+		 * Every arch is required to implement ppc_md.get_irq.
+		 * This function will either return an irq number or -1 to
+		 * indicate there are no more pending.  But the first time
+		 * through the loop this means there wasn't and IRQ pending.
+		 * The value -2 is for buggy hardware and means that this IRQ
+		 * has already been handled. -- Tom
+		 */
+		irq = ppc_md.get_irq( regs );
 
-	if (irq >= 0) {
-		ppc_irq_dispatch_handler( regs, irq );
-	} else if (irq != -2) {
-		/* -2 means ignore, already handled */
-		if (ppc_spurious_interrupts < 10)
-			printk(KERN_DEBUG "Bogus interrupt %d from PC = %lx\n",
-			       irq, regs->nip);
-		/* That's not SMP safe ... but who cares ? */
-		ppc_spurious_interrupts++;
+		if (irq >= 0)
+			ppc_irq_dispatch_handler( regs, irq );
+		else {
+			if (irq != -2 && first)
+				/* That's not SMP safe ... but who cares ? */
+				ppc_spurious_interrupts++;
+			break;
+		}
+		first = 0;
 	}
         hardirq_exit( cpu );
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/kernel/l2cr.S linux/arch/ppc/kernel/l2cr.S
--- linux.orig/arch/ppc/kernel/l2cr.S	Mon Jan  7 23:41:08 2002
+++ linux/arch/ppc/kernel/l2cr.S	Wed Dec 26 16:28:34 2001
@@ -30,6 +30,12 @@
 	***********
 	Thu, July 13, 2000.
 	- Terry: Added isync to correct for an errata.
+
+	22 August 2001.
+	- DanM: Finally added the 7450 patch I've had for the past
+		several months.  The L2CR is similar, but I'm going
+		to assume the user of this functions knows what they
+		are doing.
 	
 	Author:	Terry Greeniaus (tgree@phys.ualberta.ca)
 	Please e-mail updates to this file to me, thanks!
@@ -71,6 +77,15 @@
 	features, such as L2DO which caches only data, or L2TS which
 	causes cache pushes from the L1 cache to go to the L2 cache
 	instead of to main memory.
+
+IMPORTANT:
+	Starting with the 7450, the bits in this register have moved
+	or behave differently.  The Enable, Parity Enable, Size,
+	and L2 Invalidate are the only bits that have not moved.
+	The size is read-only for these processors with internal L2
+	cache, and the invalidate is a control as well as status.
+		-- Dan
+
 */
 /*
  * Summary: this procedure ignores the L2I bit in the value passed in,
@@ -115,6 +130,8 @@
 	 /**** Might be a good idea to set L2DO here - to prevent instructions
 	       from getting into the cache.  But since we invalidate
 	       the next time we enable the cache it doesn't really matter.
+	       Don't do this unless you accomodate all processor variations.
+	       The bit moved on the 7450.....
 	  ****/
 
 	lis	r4,0x0002
@@ -159,12 +176,21 @@
 	sync
 	isync				/* For errata */
 
+BEGIN_FTR_SECTION
+	/* On the 7450, we wait for the L2I bit to clear......
+	*/
+10:	mfspr	r3,L2CR
+	andis.	r4,r3,0x0020
+	bne	10b
+	b	11f
+END_FTR_SECTION_IFSET(CPU_FTR_SPEC7450)
+
 	/* Wait for the invalidation to complete */
 3:	mfspr	r3,L2CR
 	rlwinm.	r4,r3,0,31,31
 	bne	3b
 	
-	rlwinm	r3,r3,0,11,9		/* Turn off the L2I bit */
+11:	rlwinm	r3,r3,0,11,9		/* Turn off the L2I bit */
 	sync
 	mtspr	L2CR,r3
 	sync
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/kernel/misc.S linux/arch/ppc/kernel/misc.S
--- linux.orig/arch/ppc/kernel/misc.S	Mon Jan  7 23:41:08 2002
+++ linux/arch/ppc/kernel/misc.S	Wed Dec 26 16:28:34 2001
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.misc.S 1.34 11/23/01 16:38:29 paulus
+ * BK Id: SCCS/s.misc.S 1.36 12/01/01 20:09:06 benh
  */
 /*
  * This file contains miscellaneous low-level functions.
@@ -128,6 +128,9 @@
 /*
  * call_setup_cpu - call the setup_cpu function for this cpu
  * r3 = data offset, r24 = cpu number
+ *
+ * Don't change register layout, the setup function may rely
+ * on r5 containing a relocated pointer to the current cpu spec.
  */
 _GLOBAL(call_setup_cpu)
 	addis	r5,r3,cur_cpu_spec@ha
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/kernel/open_pic.c linux/arch/ppc/kernel/open_pic.c
--- linux.orig/arch/ppc/kernel/open_pic.c	Mon Jan  7 23:41:08 2002
+++ linux/arch/ppc/kernel/open_pic.c	Wed Dec 26 16:28:34 2001
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.open_pic.c 1.31 10/11/01 12:09:11 trini
+ * BK Id: SCCS/s.open_pic.c 1.33 12/19/01 09:45:54 trini
  */
 /*
  *  arch/ppc/kernel/open_pic.c -- OpenPIC Interrupt Handling
@@ -28,6 +28,7 @@
 #include "local_irq.h"
 #include "open_pic.h"
 #include "open_pic_defs.h"
+#include "i8259.h"
 
 void* OpenPIC_Addr;
 static volatile struct OpenPIC *OpenPIC = NULL;
@@ -784,9 +785,6 @@
 /*
  * Clean up needed. -VAL
  */
-#ifndef CONFIG_GEMINI
-	extern int i8259_irq(int cpu);
-#endif
 	int irq = openpic_irq();
 
 	/* Management of the cascade should be moved out of here */
@@ -801,7 +799,7 @@
 			irq = *chrp_int_ack_special;
 #ifndef CONFIG_GEMINI
 		else
-			irq = i8259_irq( smp_processor_id() );
+			irq = i8259_poll();
 #endif
 		openpic_eoi();
         }
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/kernel/pci.c linux/arch/ppc/kernel/pci.c
--- linux.orig/arch/ppc/kernel/pci.c	Mon Jan  7 23:41:08 2002
+++ linux/arch/ppc/kernel/pci.c	Wed Dec 26 16:28:34 2001
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.pci.c 1.35 11/13/01 08:19:57 trini
+ * BK Id: SCCS/s.pci.c 1.38 12/11/01 14:57:05 benh
  */
 /*
  * Common pmac/prep/chrp pci routines. -- Cort
@@ -19,6 +19,7 @@
 #include <asm/processor.h>
 #include <asm/io.h>
 #include <asm/prom.h>
+#include <asm/sections.h>
 #include <asm/pci-bridge.h>
 #include <asm/residual.h>
 #include <asm/byteorder.h>
@@ -66,7 +67,7 @@
 	{ PCI_FIXUP_HEADER,	PCI_ANY_ID,		PCI_ANY_ID,			pcibios_fixup_resources },
 #ifdef CONFIG_ALL_PPC
 	/* We should add per-machine fixup support in xxx_setup.c or xxx_pci.c */
-	{ PCI_FIXUP_FINAL,	PCI_VENDOR_ID_TI,	PCI_DEVICE_ID_TI_1211,		pcibios_fixup_cardbus }, 
+	{ PCI_FIXUP_FINAL,	PCI_VENDOR_ID_TI,	PCI_ANY_ID,			pcibios_fixup_cardbus }, 
 #endif /* CONFIG_ALL_PPC */
  	{ 0 }
 };
@@ -175,18 +176,25 @@
 static void
 pcibios_fixup_cardbus(struct pci_dev* dev)
 {
+	if (_machine != _MACH_Pmac)
+		return;
 	/*
 	 * Fix the interrupt routing on the TI1211 chip on the 1999
 	 * G3 powerbook, which doesn't get initialized properly by OF.
+	 * Same problem with the 1410 of the new titanium pbook which
+	 * has the same register.
 	 */
 	if (dev->vendor == PCI_VENDOR_ID_TI
-	    && dev->device == PCI_DEVICE_ID_TI_1211) {
-		u32 val;
+	    && (dev->device == PCI_DEVICE_ID_TI_1211 ||
+	        dev->device == PCI_DEVICE_ID_TI_1410)) {
+		u8 val;
 		/* 0x8c == TI122X_IRQMUX, 2 says to route the INTA
 		   signal out the MFUNC0 pin */
-		if (pci_read_config_dword(dev, 0x8c, &val) == 0
-		    && val == 0)
-			pci_write_config_dword(dev, 0x8c, 2);
+		if (pci_read_config_byte(dev, 0x8c, &val) == 0)
+			pci_write_config_byte(dev, 0x8c, (val & ~0x0f) | 2);
+		/* Disable ISA interrupt mode */	
+		if (pci_read_config_byte(dev, 0x92, &val) == 0)
+			pci_write_config_byte(dev, 0x92, val & ~0x06);
 	}
 }
 #endif /* CONFIG_ALL_PPC */
@@ -438,7 +446,7 @@
 /*
  * Functions below are used on OpenFirmware machines.
  */
-static void
+static void __openfirmware
 make_one_node_map(struct device_node* node, u8 pci_bus)
 {
 	int *bus_range;
@@ -472,7 +480,7 @@
 	}
 }
 		
-void
+void __openfirmware
 pcibios_make_OF_bus_map(void)
 {
 	int i;
@@ -512,17 +520,17 @@
 #endif	
 }
 
-static struct device_node*
-scan_OF_childs_for_device(struct device_node* node, u8 bus, u8 dev_fn)
+typedef int (*pci_OF_scan_iterator)(struct device_node* node, void* data);
+
+static struct device_node* __openfirmware
+scan_OF_pci_childs(struct device_node* node, pci_OF_scan_iterator filter, void* data)
 {
 	struct device_node* sub_node;
 	
 	for (; node != 0;node = node->sibling) {
-		unsigned int *class_code, *reg;
+		unsigned int *class_code;
 		
-		reg = (unsigned int *) get_property(node, "reg", 0);
-		if (reg && ((reg[0] >> 8) & 0xff) == dev_fn
-			&& ((reg[0] >> 16) & 0xff) == bus)
+		if (filter(node, data))
 			return node;
 
 		/* For PCI<->PCI bridges or CardBus bridges, we go down
@@ -535,13 +543,34 @@
 			(*class_code >> 8) != PCI_CLASS_BRIDGE_CARDBUS)) &&
 			strcmp(node->name, "multifunc-device"))
 			continue;
-		sub_node = scan_OF_childs_for_device(node->child, bus, dev_fn);
+		sub_node = scan_OF_pci_childs(node->child, filter, data);
 		if (sub_node)
 			return sub_node;
 	}
 	return NULL;
 }
 
+static int
+scan_OF_pci_childs_iterator(struct device_node* node, void* data)
+{
+	unsigned int *reg;
+	u8* fdata = (u8*)data;
+		
+	reg = (unsigned int *) get_property(node, "reg", 0);
+	if (reg && ((reg[0] >> 8) & 0xff) == fdata[1]
+		&& ((reg[0] >> 16) & 0xff) == fdata[0])
+		return 1;
+	return 0;
+}
+
+static struct device_node* __openfirmware
+scan_OF_childs_for_device(struct device_node* node, u8 bus, u8 dev_fn)
+{
+	u8 filter_data[2] = {bus, dev_fn};
+
+	return scan_OF_pci_childs(node, scan_OF_pci_childs_iterator, filter_data);
+}
+
 /* 
  * Scans the OF tree for a device node matching a PCI device
  */
@@ -598,6 +627,12 @@
 	return NULL;
 }
 
+static int __openfirmware
+find_OF_pci_device_filter(struct device_node* node, void* data)
+{
+	return ((void *)node == data);
+}
+
 /* 
  * Returns the PCI device matching a given OF node
  */
@@ -605,21 +640,40 @@
 pci_device_from_OF_node(struct device_node* node, u8* bus, u8* devfn)
 {
 	unsigned int *reg;
-	int i;
+	struct pci_controller* hose;
+	struct pci_dev* dev;
 		
 	if (!have_of)
 		return -ENODEV;
+	/* Make sure it's really a PCI device */
+	hose = pci_find_hose_for_OF_device(node);
+	if (!hose || !hose->arch_data)
+		return -ENODEV;
+	if (!scan_OF_pci_childs(((struct device_node*)hose->arch_data)->child,
+			find_OF_pci_device_filter, (void *)node))
+		return -ENODEV;
 	reg = (unsigned int *) get_property(node, "reg", 0);
 	if (!reg)
 		return -ENODEV;
 	*bus = (reg[0] >> 16) & 0xff;
-	for (i=0; pci_to_OF_bus_map && i<pci_bus_count; i++)
-		if (pci_to_OF_bus_map[i] == *bus) {
-			*bus = i;
-			break;
-		}
 	*devfn = ((reg[0] >> 8) & 0xff);
-	return 0;
+
+	/* Ok, here we need some tweak. If we have already renumbered
+	 * all busses, we can't rely on the OF bus number any more.
+	 * the pci_to_OF_bus_map is not enough as several PCI busses
+	 * may match the same OF bus number.
+	 */
+	if (!pci_to_OF_bus_map)
+		return 0;
+	pci_for_each_dev(dev) {
+		if (pci_to_OF_bus_map[dev->bus->number] != *bus)
+			continue;
+		if (dev->devfn != *devfn)
+			continue;
+		*bus = dev->bus->number;
+		return 0;
+	}
+	return -ENODEV;
 }
 
 void __init
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/kernel/pmac_backlight.c linux/arch/ppc/kernel/pmac_backlight.c
--- linux.orig/arch/ppc/kernel/pmac_backlight.c	Mon Jan  7 23:41:08 2002
+++ linux/arch/ppc/kernel/pmac_backlight.c	Wed Dec 26 16:28:34 2001
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.pmac_backlight.c 1.8 09/08/01 15:47:42 paulus
+ * BK Id: SCCS/s.pmac_backlight.c 1.10 12/01/01 20:09:06 benh
  */
 /*
  * Miscellaneous procedures for dealing with the PowerMac hardware.
@@ -78,7 +78,7 @@
 		pmu_request(&req, NULL, 2, 0xd9, 0);
 		while (!req.complete)
 			pmu_poll();
-		backlight_level = req.reply[1] >> 4;
+		backlight_level = req.reply[0] >> 4;
 	}
 #endif
 	if (!backlighter->set_enable(1, backlight_level, data))
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/kernel/pmac_feature.c linux/arch/ppc/kernel/pmac_feature.c
--- linux.orig/arch/ppc/kernel/pmac_feature.c	Thu Jan  1 00:00:00 1970
+++ linux/arch/ppc/kernel/pmac_feature.c	Wed Dec 26 16:28:34 2001
@@ -0,0 +1,2075 @@
+/*
+ * BK Id: %F% %I% %G% %U% %#%
+ */
+/*
+ *  arch/ppc/kernel/pmac_feature.c
+ *
+ *  Copyright (C) 1996-2001 Paul Mackerras (paulus@cs.anu.edu.au)
+ *                          Ben. Herrenschmidt (benh@kernel.crashing.org)
+ *
+ *  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.
+ *  
+ *  TODO: 
+ *  
+ *   - Replace mdelay with some schedule loop if possible
+ *   - Shorten some obfuscated delays on some routines (like modem
+ *     power)
+ *
+ */
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/adb.h>
+#include <linux/pmu.h>
+#include <asm/sections.h>
+#include <asm/errno.h>
+#include <asm/ohare.h>
+#include <asm/heathrow.h>
+#include <asm/keylargo.h>
+#include <asm/uninorth.h>
+#include <asm/io.h>
+#include <asm/prom.h>
+#include <asm/machdep.h>
+#include <asm/pmac_feature.h>
+#include <asm/dbdma.h>
+
+#undef DEBUG_FEATURE
+
+#ifdef DEBUG_FEATURE
+#define DBG(fmt,...) printk(KERN_DEBUG fmt)
+#else
+#define DBG(fmt,...)
+#endif
+
+/* Exported from arch/ppc/kernel/idle.c */
+extern unsigned long powersave_nap;
+
+/*
+ * We use a single global lock to protect accesses. Each driver has
+ * to take care of it's own locking
+ */
+static spinlock_t feature_lock  __pmacdata = SPIN_LOCK_UNLOCKED;
+
+#define LOCK(flags)	spin_lock_irqsave(&feature_lock, flags);
+#define UNLOCK(flags)	spin_unlock_irqrestore(&feature_lock, flags);
+
+/*
+ * Helper functions regarding the various flavors of mac-io
+ */
+ 
+#define MAX_MACIO_CHIPS		2
+
+enum {
+	macio_unknown = 0,
+	macio_grand_central,
+	macio_ohare,
+	macio_ohareII,
+	macio_heathrow,
+	macio_gatwick,
+	macio_paddington,
+	macio_keylargo,
+	macio_pangea
+};
+
+static const char* macio_names[] __pmacdata = 
+{
+	"Unknown",
+	"Grand Central",
+	"OHare",
+	"OHareII",
+	"Heathrow",
+	"Gatwick",
+	"Paddington",
+	"Keylargo",
+	"Pangea"
+};
+
+static struct macio_chip
+{
+	struct device_node*	of_node;
+	int			type;
+	int			rev;
+	volatile u32*		base;
+	unsigned long		flags;
+} macio_chips[MAX_MACIO_CHIPS]  __pmacdata;
+
+#define MACIO_FLAG_SCCA_ON	0x00000001
+#define MACIO_FLAG_SCCB_ON	0x00000002
+#define MACIO_FLAG_SCC_LOCKED	0x00000004
+#define MACIO_FLAG_AIRPORT_ON	0x00000010
+#define MACIO_FLAG_FW_SUPPORTED	0x00000020
+
+static struct macio_chip* __pmac
+macio_find(struct device_node* child, int type)
+{
+	while(child) {
+		int	i;
+	
+		for (i=0; i < MAX_MACIO_CHIPS && macio_chips[i].of_node; i++)
+			if (child == macio_chips[i].of_node &&
+			    (!type || macio_chips[i].type == type))
+				return &macio_chips[i];
+		child = child->parent;
+	}
+	return NULL;
+}
+
+#define MACIO_FCR32(macio, r)	((macio)->base + ((r) >> 2))
+#define MACIO_FCR8(macio, r)	(((volatile u8*)((macio)->base)) + (r))
+
+#define MACIO_IN32(r)		(in_le32(MACIO_FCR32(macio,r)))
+#define MACIO_OUT32(r,v)	(out_le32(MACIO_FCR32(macio,r), (v)))
+#define MACIO_BIS(r,v)		(MACIO_OUT32((r), MACIO_IN32(r) | (v)))
+#define MACIO_BIC(r,v)		(MACIO_OUT32((r), MACIO_IN32(r) & ~(v)))
+#define MACIO_IN8(r)		(in_8(MACIO_FCR8(macio,r)))
+#define MACIO_OUT8(r,v)		(out_8(MACIO_FCR8(macio,r), (v)))
+
+/*
+ * Uninorth reg. access. Note that Uni-N regs are big endian
+ */
+ 
+#define UN_REG(r)	(uninorth_base + ((r) >> 2))
+#define UN_IN(r)	(in_be32(UN_REG(r)))
+#define UN_OUT(r,v)	(out_be32(UN_REG(r), (v)))
+#define UN_BIS(r,v)	(UN_OUT((r), UN_IN(r) | (v)))
+#define UN_BIC(r,v)	(UN_OUT((r), UN_IN(r) & ~(v)))
+
+static struct device_node* uninorth_node __pmacdata;
+static u32* uninorth_base __pmacdata;
+static u32 uninorth_rev __pmacdata;
+
+
+/*
+ * For each motherboard family, we have a table of functions pointers
+ * that handle the various features.
+ */
+
+typedef int (*feature_call)(struct device_node* node, int param, int value);
+
+struct feature_table_entry {
+	unsigned int	selector;	
+	feature_call	function;
+};
+
+struct pmac_mb_def
+{
+	const char*			model_string;
+	const char*			model_name;
+	int				model_id;
+	struct feature_table_entry* 	features;
+	unsigned long			board_flags;
+};
+static struct pmac_mb_def pmac_mb __pmacdata;
+
+/*
+ * Here are the chip specific feature functions
+ */
+
+static inline int __pmac
+simple_feature_tweak(struct device_node* node, int type, int reg, u32 mask, int value)
+{
+	struct macio_chip*	macio;
+	unsigned long		flags;
+	
+	macio = macio_find(node, type);
+	if (!macio)
+		return -ENODEV;
+	LOCK(flags);
+	if (value)
+		MACIO_BIS(reg, mask);
+	else
+		MACIO_BIC(reg, mask);
+	(void)MACIO_IN32(reg);
+	UNLOCK(flags);
+
+	return 0;
+}
+
+static int __pmac
+generic_scc_enable(struct device_node* node, u32 enable_mask, u32 reset_mask,
+	int param, int value)
+{
+	struct macio_chip*	macio;
+	unsigned long		chan_mask;
+	unsigned long		fcr;
+	unsigned long		flags;
+	
+	macio = macio_find(node, 0);
+	if (!macio)
+		return -ENODEV;
+	if (!strcmp(node->name, "ch-a"))
+		chan_mask = MACIO_FLAG_SCCA_ON;
+	else if (!strcmp(node->name, "ch-b"))
+		chan_mask = MACIO_FLAG_SCCB_ON;
+	else
+		return -ENODEV;
+
+	if (value) {
+		LOCK(flags);
+		fcr = MACIO_IN32(OHARE_FCR);
+		/* Check if scc cell need enabling */
+		if (!(fcr & OH_SCC_ENABLE)) {
+			fcr |= enable_mask;
+			MACIO_OUT32(OHARE_FCR, fcr);
+			fcr |= reset_mask;
+			MACIO_OUT32(OHARE_FCR, fcr);
+			UNLOCK(flags);
+			(void)MACIO_IN32(OHARE_FCR);
+			mdelay(15);
+			LOCK(flags);
+			fcr &= ~reset_mask;
+			MACIO_OUT32(OHARE_FCR, fcr);
+		}
+		if (chan_mask & MACIO_FLAG_SCCA_ON)
+			fcr |= OH_SCCA_IO;
+		if (chan_mask & MACIO_FLAG_SCCB_ON)
+			fcr |= OH_SCCB_IO;
+		MACIO_OUT32(OHARE_FCR, fcr);
+		macio->flags |= chan_mask;
+		UNLOCK(flags);
+		if (param & PMAC_SCC_FLAG_XMON)
+			macio->flags |= MACIO_FLAG_SCC_LOCKED;
+	} else {
+		if (macio->flags & MACIO_FLAG_SCC_LOCKED)
+			return -EPERM;
+		LOCK(flags);
+		fcr = MACIO_IN32(OHARE_FCR);
+		if (chan_mask & MACIO_FLAG_SCCA_ON)
+			fcr &= ~OH_SCCA_IO;
+		if (chan_mask & MACIO_FLAG_SCCB_ON)
+			fcr &= ~OH_SCCB_IO;
+		MACIO_OUT32(OHARE_FCR, fcr);
+		if ((fcr & (OH_SCCA_IO | OH_SCCB_IO)) == 0) {
+			fcr &= ~enable_mask;
+			MACIO_OUT32(OHARE_FCR, fcr);
+		}
+		macio->flags &= ~(chan_mask);
+		UNLOCK(flags);
+		mdelay(10);
+	}
+	return 0;
+}
+
+static int __pmac
+ohare_scc_enable(struct device_node* node, int param, int value)
+{
+	int rc;
+
+#ifdef CONFIG_ADB_PMU
+	if (value && (param & 0xfff) == PMAC_SCC_IRDA)
+		pmu_enable_irled(1);
+#endif /* CONFIG_ADB_PMU */		
+	rc = generic_scc_enable(node, OH_SCC_ENABLE, OH_SCC_RESET, param, value);
+#ifdef CONFIG_ADB_PMU
+	if ((param & 0xfff) == PMAC_SCC_IRDA && (rc || !value))
+		pmu_enable_irled(0);
+#endif /* CONFIG_ADB_PMU */		
+	return rc;
+}
+
+static int __pmac
+ohare_floppy_enable(struct device_node* node, int param, int value)
+{
+	return simple_feature_tweak(node, macio_ohare,
+		OHARE_FCR, OH_FLOPPY_ENABLE, value);
+}
+
+static int __pmac
+ohare_mesh_enable(struct device_node* node, int param, int value)
+{
+	return simple_feature_tweak(node, macio_ohare,
+		OHARE_FCR, OH_MESH_ENABLE, value);
+}
+
+static int __pmac
+ohare_ide_enable(struct device_node* node, int param, int value)
+{
+	switch(param) {
+	    case 0:
+	    	/* For some reason, setting the bit in set_initial_features()
+	    	 * doesn't stick. I'm still investigating... --BenH.
+	    	 */
+	    	if (value)
+	    		simple_feature_tweak(node, macio_ohare,
+				OHARE_FCR, OH_IOBUS_ENABLE, 1);
+		return simple_feature_tweak(node, macio_ohare,
+			OHARE_FCR, OH_IDE0_ENABLE, value);
+	    case 1:
+		return simple_feature_tweak(node, macio_ohare,
+			OHARE_FCR, OH_BAY_IDE_ENABLE, value);
+	    default:
+	    	return -ENODEV;
+	}
+}
+
+static int __pmac
+ohare_ide_reset(struct device_node* node, int param, int value)
+{
+	switch(param) {
+	    case 0:
+		return simple_feature_tweak(node, macio_ohare,
+			OHARE_FCR, OH_IDE0_RESET_N, !value);
+	    case 1:
+		return simple_feature_tweak(node, macio_ohare,
+			OHARE_FCR, OH_IDE1_RESET_N, !value);
+	    default:
+	    	return -ENODEV;
+	}
+}
+
+static int __pmac
+ohare_sleep_state(struct device_node* node, int param, int value)
+{
+	struct macio_chip*	macio = &macio_chips[0];
+
+	if ((pmac_mb.board_flags & PMAC_MB_CAN_SLEEP) == 0)
+		return -EPERM;
+	if (value) {
+		MACIO_BIC(OHARE_FCR, OH_IOBUS_ENABLE);
+	} else {
+		MACIO_BIS(OHARE_FCR, OH_IOBUS_ENABLE);
+	}
+	
+	return 0;
+}
+
+static int __pmac
+heathrow_scc_enable(struct device_node* node, int param, int value)
+{
+	int rc;
+	
+#ifdef CONFIG_ADB_PMU
+	if (value && param == PMAC_SCC_IRDA)
+		pmu_enable_irled(1);
+#endif /* CONFIG_ADB_PMU */		
+	/* Fixme: It's possible that wallstreet (heathrow) is different
+	 * than other paddington machines. I still have to figure that
+	 * out exactly, for now, the paddington values are used
+	 */
+	rc = generic_scc_enable(node, HRW_SCC_ENABLE, PADD_RESET_SCC, param, value);
+#ifdef CONFIG_ADB_PMU
+	if (param == PMAC_SCC_IRDA && (rc || !value))
+		pmu_enable_irled(0);
+#endif /* CONFIG_ADB_PMU */		
+	return rc;
+}
+
+static int __pmac
+heathrow_modem_enable(struct device_node* node, int param, int value)
+{
+	struct macio_chip*	macio;
+	u8			gpio;
+	unsigned long		flags;
+	
+	macio = macio_find(node, macio_unknown);
+	if (!macio)
+		return -ENODEV;
+	gpio = MACIO_IN8(HRW_GPIO_MODEM_RESET) & ~1;
+	if (!value) {
+		LOCK(flags);
+		MACIO_OUT8(HRW_GPIO_MODEM_RESET, gpio);
+		UNLOCK(flags);
+		(void)MACIO_IN8(HRW_GPIO_MODEM_RESET);
+		mdelay(250);
+	}
+	if (pmac_mb.model_id != PMAC_TYPE_YOSEMITE &&
+	    pmac_mb.model_id != PMAC_TYPE_YIKES) {
+	    	LOCK(flags);
+	    	/* We use the paddington values as they seem to work properly
+	    	 * on the wallstreet (heathrow) as well. I can't tell why we
+	    	 * had to flip them on older feature.c, the fact is that new
+	    	 * code uses the paddington values which are also the ones used
+	    	 * in Darwin, and that works on wallstreet !
+	    	 */
+	    	if (value)
+	    		MACIO_BIC(HEATHROW_FCR, PADD_MODEM_POWER_N);
+	    	else
+	    		MACIO_BIS(HEATHROW_FCR, PADD_MODEM_POWER_N);
+	    	UNLOCK(flags);
+	    	(void)MACIO_IN32(HEATHROW_FCR);
+		mdelay(250);
+	}
+	if (value) {
+		LOCK(flags);
+		MACIO_OUT8(HRW_GPIO_MODEM_RESET, gpio | 1);
+		(void)MACIO_IN8(HRW_GPIO_MODEM_RESET);
+	    	UNLOCK(flags); mdelay(250); LOCK(flags);
+		MACIO_OUT8(HRW_GPIO_MODEM_RESET, gpio);
+		(void)MACIO_IN8(HRW_GPIO_MODEM_RESET);
+	    	UNLOCK(flags); mdelay(250); LOCK(flags);
+		MACIO_OUT8(HRW_GPIO_MODEM_RESET, gpio | 1);
+		(void)MACIO_IN8(HRW_GPIO_MODEM_RESET);
+	    	UNLOCK(flags); mdelay(250); LOCK(flags);
+	}
+	return 0;
+}
+
+static int __pmac
+heathrow_floppy_enable(struct device_node* node, int param, int value)
+{
+	return simple_feature_tweak(node, macio_unknown,
+		HEATHROW_FCR,
+		HRW_SWIM_ENABLE|HRW_BAY_FLOPPY_ENABLE,
+		value);
+}
+
+static int __pmac
+heathrow_mesh_enable(struct device_node* node, int param, int value)
+{
+	struct macio_chip*	macio;
+	unsigned long		flags;
+	
+	macio = macio_find(node, macio_unknown);
+	if (!macio)
+		return -ENODEV;
+	LOCK(flags);
+	/* Set clear mesh cell enable */
+	if (value)
+		MACIO_BIS(HEATHROW_FCR, HRW_MESH_ENABLE);
+	else
+		MACIO_BIC(HEATHROW_FCR, HRW_MESH_ENABLE);
+	(void)MACIO_IN32(HEATHROW_FCR);
+	udelay(10);
+	/* Set/Clear termination power (todo: test ! the bit value
+	 * used by Darwin doesn't seem to match what we used so
+	 * far. If you experience problems, turn #if 1 into #if 0
+	 * and tell me about it --BenH.
+	 */
+#if 1
+	if (value)
+		MACIO_BIC(HEATHROW_MBCR, 0x00000004);
+	else
+		MACIO_BIS(HEATHROW_MBCR, 0x00000004);
+#else
+	if (value)
+		MACIO_BIC(HEATHROW_MBCR, 0x00040000);
+	else
+		MACIO_BIS(HEATHROW_MBCR, 0x00040000);
+#endif		
+	(void)MACIO_IN32(HEATHROW_MBCR);
+	udelay(10);
+	UNLOCK(flags);
+
+	return 0;
+}
+
+static int __pmac
+heathrow_ide_enable(struct device_node* node, int param, int value)
+{
+	switch(param) {
+	    case 0:
+		return simple_feature_tweak(node, macio_unknown,
+			HEATHROW_FCR, HRW_IDE0_ENABLE, value);
+	    case 1:
+		return simple_feature_tweak(node, macio_unknown,
+			HEATHROW_FCR, HRW_BAY_IDE_ENABLE, value);
+	    default:
+	    	return -ENODEV;
+	}
+}
+
+static int __pmac
+heathrow_ide_reset(struct device_node* node, int param, int value)
+{
+	switch(param) {
+	    case 0:
+		return simple_feature_tweak(node, macio_unknown,
+			HEATHROW_FCR, HRW_IDE0_RESET_N, !value);
+	    case 1:
+		return simple_feature_tweak(node, macio_unknown,
+			HEATHROW_FCR, HRW_IDE1_RESET_N, !value);
+	    default:
+	    	return -ENODEV;
+	}
+}
+
+static int __pmac
+heathrow_bmac_enable(struct device_node* node, int param, int value)
+{
+	struct macio_chip*	macio;
+	unsigned long		flags;
+	
+	macio = macio_find(node, 0);
+	if (!macio)
+		return -ENODEV;
+	if (value) {
+		LOCK(flags);
+		MACIO_BIS(HEATHROW_FCR, HRW_BMAC_IO_ENABLE);
+		MACIO_BIS(HEATHROW_FCR, HRW_BMAC_RESET);
+		UNLOCK(flags);
+		(void)MACIO_IN32(HEATHROW_FCR);
+		mdelay(10);
+		LOCK(flags);
+		MACIO_BIC(HEATHROW_FCR, HRW_BMAC_RESET);
+		UNLOCK(flags);
+		(void)MACIO_IN32(HEATHROW_FCR);
+		mdelay(10);
+	} else {
+		LOCK(flags);
+		MACIO_BIC(HEATHROW_FCR, HRW_BMAC_IO_ENABLE);
+		UNLOCK(flags);
+	}
+	return 0;
+}
+
+static int __pmac
+heathrow_sound_enable(struct device_node* node, int param, int value)
+{
+	struct macio_chip*	macio;
+	unsigned long		flags;
+
+	/* B&W G3 and Yikes don't support that properly (the
+	 * sound appear to never come back after beeing shut down).
+	 */
+	if (pmac_mb.model_id == PMAC_TYPE_YOSEMITE ||
+	    pmac_mb.model_id == PMAC_TYPE_YIKES)
+		return 0;
+	
+	macio = macio_find(node, 0);
+	if (!macio)
+		return -ENODEV;
+	if (value) {
+		LOCK(flags);
+		MACIO_BIS(HEATHROW_FCR, HRW_SOUND_CLK_ENABLE);
+		MACIO_BIC(HEATHROW_FCR, HRW_SOUND_POWER_N);
+		UNLOCK(flags);
+		(void)MACIO_IN32(HEATHROW_FCR);
+	} else {
+		LOCK(flags);
+		MACIO_BIS(HEATHROW_FCR, HRW_SOUND_POWER_N);
+		MACIO_BIC(HEATHROW_FCR, HRW_SOUND_CLK_ENABLE);
+		UNLOCK(flags);
+	}
+	return 0;
+}
+
+static u32 save_fcr[5] __pmacdata;
+static u32 save_mbcr __pmacdata;
+static u32 save_gpio_levels[2] __pmacdata;
+static u8 save_gpio_extint[KEYLARGO_GPIO_EXTINT_CNT] __pmacdata;
+static u8 save_gpio_normal[KEYLARGO_GPIO_CNT] __pmacdata;
+static u32 save_unin_clock_ctl __pmacdata;
+static struct dbdma_regs save_dbdma[13] __pmacdata;
+static struct dbdma_regs save_alt_dbdma[13] __pmacdata;
+
+static void __pmac
+dbdma_save(struct macio_chip* macio, struct dbdma_regs* save)
+{
+	int i;
+	
+	/* Save state & config of DBDMA channels */
+	for (i=0; i<13; i++) {
+		volatile struct dbdma_regs* chan = (volatile struct dbdma_regs*)
+			(macio->base + ((0x8000+i*0x100)>>2));
+		save[i].cmdptr_hi = in_le32(&chan->cmdptr_hi);
+		save[i].cmdptr = in_le32(&chan->cmdptr);
+		save[i].intr_sel = in_le32(&chan->intr_sel);
+		save[i].br_sel = in_le32(&chan->br_sel);
+		save[i].wait_sel = in_le32(&chan->wait_sel);
+	}
+}
+
+static void __pmac
+dbdma_restore(struct macio_chip* macio, struct dbdma_regs* save)
+{
+	int i;
+	
+	/* Save state & config of DBDMA channels */
+	for (i=0; i<13; i++) {
+		volatile struct dbdma_regs* chan = (volatile struct dbdma_regs*)
+			(macio->base + ((0x8000+i*0x100)>>2));
+		out_le32(&chan->control, (ACTIVE|DEAD|WAKE|FLUSH|PAUSE|RUN)<<16);
+		while (in_le32(&chan->status) & ACTIVE)
+			mb();
+		out_le32(&chan->cmdptr_hi, save[i].cmdptr_hi);
+		out_le32(&chan->cmdptr, save[i].cmdptr);
+		out_le32(&chan->intr_sel, save[i].intr_sel);
+		out_le32(&chan->br_sel, save[i].br_sel);
+		out_le32(&chan->wait_sel, save[i].wait_sel);
+	}
+}
+
+static void __pmac
+heathrow_sleep(struct macio_chip* macio, int secondary)
+{
+	if (secondary) {
+		dbdma_save(macio, save_alt_dbdma);
+		save_fcr[2] = MACIO_IN32(0x38);
+		save_fcr[3] = MACIO_IN32(0x3c);
+	} else {
+		dbdma_save(macio, save_dbdma);
+		save_fcr[0] = MACIO_IN32(0x38);
+		save_fcr[1] = MACIO_IN32(0x3c);
+		save_mbcr = MACIO_IN32(0x34);
+		/* Make sure sound is shut down */
+		MACIO_BIS(HEATHROW_FCR, HRW_SOUND_POWER_N);
+		MACIO_BIC(HEATHROW_FCR, HRW_SOUND_CLK_ENABLE);
+		/* This seems to be necessary as well or the fan
+		 * keeps coming up and battery drains fast */
+		MACIO_BIC(HEATHROW_FCR, HRW_IOBUS_ENABLE);
+	}
+	/* Make sure modem is shut down */
+	MACIO_OUT8(HRW_GPIO_MODEM_RESET,
+		MACIO_IN8(HRW_GPIO_MODEM_RESET) & ~1);
+	MACIO_BIS(HEATHROW_FCR, PADD_MODEM_POWER_N);
+	MACIO_BIC(HEATHROW_FCR, OH_SCCA_IO|OH_SCCB_IO|HRW_SCC_ENABLE);
+
+	/* Let things settle */
+	(void)MACIO_IN32(HEATHROW_FCR);
+	mdelay(1);
+}
+
+static void __pmac
+heathrow_wakeup(struct macio_chip* macio, int secondary)
+{
+	if (secondary) {
+		MACIO_OUT32(0x38, save_fcr[2]);
+		(void)MACIO_IN32(0x38);
+		mdelay(1);
+		MACIO_OUT32(0x3c, save_fcr[3]);
+		(void)MACIO_IN32(0x38);
+		mdelay(10);
+		dbdma_restore(macio, save_alt_dbdma);
+	} else {
+		MACIO_OUT32(0x38, save_fcr[0] | HRW_IOBUS_ENABLE);
+		(void)MACIO_IN32(0x38);
+		mdelay(1);
+		MACIO_OUT32(0x3c, save_fcr[1]);
+		(void)MACIO_IN32(0x38);
+		mdelay(1);
+		MACIO_OUT32(0x34, save_mbcr);
+		(void)MACIO_IN32(0x38);
+		mdelay(10);
+		dbdma_restore(macio, save_dbdma);
+	}
+}
+
+static int __pmac
+heathrow_sleep_state(struct device_node* node, int param, int value)
+{
+	if ((pmac_mb.board_flags & PMAC_MB_CAN_SLEEP) == 0)
+		return -EPERM;
+	if (value == 1) {
+		if (macio_chips[1].type == macio_gatwick)
+			heathrow_sleep(&macio_chips[0], 1);
+		heathrow_sleep(&macio_chips[0], 0);
+	} else if (value == 0) {
+		heathrow_wakeup(&macio_chips[0], 0);
+		if (macio_chips[1].type == macio_gatwick)
+			heathrow_wakeup(&macio_chips[0], 1);
+	}
+	return 0;
+}
+
+static int __pmac
+core99_scc_enable(struct device_node* node, int param, int value)
+{
+	struct macio_chip*	macio;
+	unsigned long		flags;
+	unsigned long		chan_mask;
+	u32			fcr;
+	
+	macio = macio_find(node, 0);
+	if (!macio)
+		return -ENODEV;
+	if (!strcmp(node->name, "ch-a"))
+		chan_mask = MACIO_FLAG_SCCA_ON;
+	else if (!strcmp(node->name, "ch-b"))
+		chan_mask = MACIO_FLAG_SCCB_ON;
+	else
+		return -ENODEV;
+
+	if (value) {
+		int need_reset_scc = 0;
+		int need_reset_irda = 0;
+		
+		LOCK(flags);
+		fcr = MACIO_IN32(KEYLARGO_FCR0);
+		/* Check if scc cell need enabling */
+		if (!(fcr & KL0_SCC_CELL_ENABLE)) {
+			fcr |= KL0_SCC_CELL_ENABLE;
+			need_reset_scc = 1;
+		}
+		if (chan_mask & MACIO_FLAG_SCCA_ON) {
+			fcr |= KL0_SCCA_ENABLE;
+			/* Don't enable line drivers for I2S modem */
+			if ((param & 0xfff) == PMAC_SCC_I2S1)
+				fcr &= ~KL0_SCC_A_INTF_ENABLE;
+			else
+				fcr |= KL0_SCC_A_INTF_ENABLE;
+		}
+		if (chan_mask & MACIO_FLAG_SCCB_ON) {
+			fcr |= KL0_SCCB_ENABLE;
+			/* Perform irda specific inits */
+			if ((param & 0xfff) == PMAC_SCC_IRDA) {
+				fcr &= ~KL0_SCC_B_INTF_ENABLE;
+				fcr |= KL0_IRDA_ENABLE;
+				fcr |= KL0_IRDA_CLK32_ENABLE | KL0_IRDA_CLK19_ENABLE;
+				fcr |= KL0_IRDA_SOURCE1_SEL;
+				fcr &= ~(KL0_IRDA_FAST_CONNECT|KL0_IRDA_DEFAULT1|KL0_IRDA_DEFAULT0);
+				fcr &= ~(KL0_IRDA_SOURCE2_SEL|KL0_IRDA_HIGH_BAND);
+				need_reset_irda = 1;
+			} else
+				fcr |= KL0_SCC_B_INTF_ENABLE;
+		}
+		MACIO_OUT32(KEYLARGO_FCR0, fcr);
+		macio->flags |= chan_mask;
+		if (need_reset_scc)  {
+			MACIO_BIS(KEYLARGO_FCR0, KL0_SCC_RESET);
+			(void)MACIO_IN32(KEYLARGO_FCR0);
+			UNLOCK(flags);
+			mdelay(15);
+			LOCK(flags);
+			MACIO_BIC(KEYLARGO_FCR0, KL0_SCC_RESET);
+		}
+		if (need_reset_irda)  {
+			MACIO_BIS(KEYLARGO_FCR0, KL0_IRDA_RESET);
+			(void)MACIO_IN32(KEYLARGO_FCR0);
+			UNLOCK(flags);
+			mdelay(15);
+			LOCK(flags);
+			MACIO_BIC(KEYLARGO_FCR0, KL0_IRDA_RESET);
+		}
+		UNLOCK(flags);
+		if (param & PMAC_SCC_FLAG_XMON)
+			macio->flags |= MACIO_FLAG_SCC_LOCKED;
+	} else {
+		if (macio->flags & MACIO_FLAG_SCC_LOCKED)
+			return -EPERM;
+		LOCK(flags);
+		fcr = MACIO_IN32(KEYLARGO_FCR0);
+		if (chan_mask & MACIO_FLAG_SCCA_ON)
+			fcr &= ~KL0_SCCA_ENABLE;
+		if (chan_mask & MACIO_FLAG_SCCB_ON) {
+			fcr &= ~KL0_SCCB_ENABLE;
+			/* Perform irda specific clears */
+			if ((param & 0xfff) == PMAC_SCC_IRDA) {
+				fcr &= ~KL0_IRDA_ENABLE;
+				fcr &= ~(KL0_IRDA_CLK32_ENABLE | KL0_IRDA_CLK19_ENABLE);
+				fcr &= ~(KL0_IRDA_FAST_CONNECT|KL0_IRDA_DEFAULT1|KL0_IRDA_DEFAULT0);
+				fcr &= ~(KL0_IRDA_SOURCE1_SEL|KL0_IRDA_SOURCE2_SEL|KL0_IRDA_HIGH_BAND);
+			}
+		}
+		MACIO_OUT32(KEYLARGO_FCR0, fcr);
+		if ((fcr & (KL0_SCCA_ENABLE | KL0_SCCB_ENABLE)) == 0) {
+			fcr &= ~KL0_SCC_CELL_ENABLE;
+			MACIO_OUT32(KEYLARGO_FCR0, fcr);
+		}
+		macio->flags &= ~(chan_mask);
+		UNLOCK(flags);
+		mdelay(10);
+	}
+	return 0;
+}
+
+static int __pmac
+core99_modem_enable(struct device_node* node, int param, int value)
+{
+	struct macio_chip*	macio;
+	u8			gpio;
+	unsigned long		flags;
+	
+	macio = macio_find(node, 0);
+	if (!macio)
+		return -ENODEV;
+	gpio = MACIO_IN8(KL_GPIO_MODEM_RESET);
+	gpio |= KEYLARGO_GPIO_OUTPUT_ENABLE;
+	gpio &= ~KEYLARGO_GPIO_OUTOUT_DATA;
+	
+	if (!value) {
+		LOCK(flags);
+		MACIO_OUT8(KL_GPIO_MODEM_RESET, gpio);
+		UNLOCK(flags);
+		(void)MACIO_IN8(KL_GPIO_MODEM_RESET);
+		mdelay(250);
+	}
+    	LOCK(flags);
+    	if (value) {
+    		MACIO_BIC(KEYLARGO_FCR2, KL2_MODEM_POWER_N);
+	    	UNLOCK(flags);
+	    	(void)MACIO_IN32(KEYLARGO_FCR2);
+		mdelay(250);
+    	} else {
+    		MACIO_BIS(KEYLARGO_FCR2, KL2_MODEM_POWER_N);
+	    	UNLOCK(flags);
+    	}
+	if (value) {
+		LOCK(flags);
+		MACIO_OUT8(KL_GPIO_MODEM_RESET, gpio | KEYLARGO_GPIO_OUTOUT_DATA);
+		(void)MACIO_IN8(KL_GPIO_MODEM_RESET);
+	    	UNLOCK(flags); mdelay(250); LOCK(flags);
+		MACIO_OUT8(KL_GPIO_MODEM_RESET, gpio);
+		(void)MACIO_IN8(KL_GPIO_MODEM_RESET);
+	    	UNLOCK(flags); mdelay(250); LOCK(flags);
+		MACIO_OUT8(KL_GPIO_MODEM_RESET, gpio | KEYLARGO_GPIO_OUTOUT_DATA);
+		(void)MACIO_IN8(KL_GPIO_MODEM_RESET);
+	    	UNLOCK(flags); mdelay(250); LOCK(flags);
+	}
+	return 0;
+}
+
+static int __pmac
+core99_ide_enable(struct device_node* node, int param, int value)
+{
+	switch(param) {
+	    case 0:
+		return simple_feature_tweak(node, macio_unknown,
+			KEYLARGO_FCR1, KL1_EIDE0_ENABLE, value);
+	    case 1:
+		return simple_feature_tweak(node, macio_unknown,
+			KEYLARGO_FCR1, KL1_EIDE1_ENABLE, value);
+	    case 2:
+		return simple_feature_tweak(node, macio_unknown,
+			KEYLARGO_FCR1, KL1_UIDE_ENABLE, value);
+	    default:
+	    	return -ENODEV;
+	}
+}
+
+static int __pmac
+core99_ide_reset(struct device_node* node, int param, int value)
+{
+	switch(param) {
+	    case 0:
+		return simple_feature_tweak(node, macio_unknown,
+			KEYLARGO_FCR1, KL1_EIDE0_RESET_N, !value);
+	    case 1:
+		return simple_feature_tweak(node, macio_unknown,
+			KEYLARGO_FCR1, KL1_EIDE1_RESET_N, !value);
+	    case 2:
+		return simple_feature_tweak(node, macio_unknown,
+			KEYLARGO_FCR1, KL1_UIDE_RESET_N, !value);
+	    default:
+	    	return -ENODEV;
+	}
+}
+
+static int __pmac
+core99_gmac_enable(struct device_node* node, int param, int value)
+{
+	unsigned long flags;
+
+	LOCK(flags);
+	if (value)
+		UN_BIS(UNI_N_CLOCK_CNTL, UNI_N_CLOCK_CNTL_GMAC);
+	else
+		UN_BIC(UNI_N_CLOCK_CNTL, UNI_N_CLOCK_CNTL_GMAC);
+	(void)UN_IN(UNI_N_CLOCK_CNTL);
+	UNLOCK(flags);
+	udelay(20);
+
+	return 0;
+}
+
+static int __pmac
+core99_gmac_phy_reset(struct device_node* node, int param, int value)
+{
+	unsigned long flags;
+	struct macio_chip* macio;
+	
+	macio = &macio_chips[0];
+	if (macio->type != macio_keylargo && macio->type != macio_pangea)
+		return -ENODEV;
+
+	LOCK(flags);
+	MACIO_OUT8(KL_GPIO_ETH_PHY_RESET, KEYLARGO_GPIO_OUTPUT_ENABLE);
+	(void)MACIO_IN8(KL_GPIO_ETH_PHY_RESET);
+	UNLOCK(flags);
+	mdelay(10);
+	LOCK(flags);
+	MACIO_OUT8(KL_GPIO_ETH_PHY_RESET, KEYLARGO_GPIO_OUTPUT_ENABLE
+		| KEYLARGO_GPIO_OUTOUT_DATA);
+	UNLOCK(flags);
+	mdelay(10);
+
+	return 0;
+}
+
+static int __pmac
+core99_sound_chip_enable(struct device_node* node, int param, int value)
+{
+	struct macio_chip*	macio;
+	unsigned long		flags;
+	
+	macio = macio_find(node, 0);
+	if (!macio)
+		return -ENODEV;
+
+	/* Do a better probe code, screamer G4 desktops &
+	 * iMacs can do that too, add a recalibrate  in
+	 * the driver as well
+	 */
+	if (pmac_mb.model_id == PMAC_TYPE_PISMO ||
+	    pmac_mb.model_id == PMAC_TYPE_TITANIUM) {
+		LOCK(flags);
+		if (value)
+	    		MACIO_OUT8(KL_GPIO_SOUND_POWER,
+	    			KEYLARGO_GPIO_OUTPUT_ENABLE |
+	    			KEYLARGO_GPIO_OUTOUT_DATA);
+	    	else
+	    		MACIO_OUT8(KL_GPIO_SOUND_POWER,
+	    			KEYLARGO_GPIO_OUTPUT_ENABLE);
+	    	(void)MACIO_IN8(KL_GPIO_SOUND_POWER);
+	    	UNLOCK(flags);
+	}
+	return 0;
+}
+
+static int __pmac
+core99_airport_enable(struct device_node* node, int param, int value)
+{
+	struct macio_chip*	macio;
+	unsigned long		flags;
+	int			state;
+	
+	macio = macio_find(node, 0);
+	if (!macio)
+		return -ENODEV;
+	
+	/* Hint: we allow passing of macio itself for the sake of the
+	 * sleep code
+	 */
+	if (node != macio->of_node &&
+	    (!node->parent || node->parent != macio->of_node))
+		return -ENODEV;
+	state = (macio->flags & MACIO_FLAG_AIRPORT_ON) != 0;
+	if (value == state)
+		return 0;
+	if (value) {
+		/* This code is a reproduction of OF enable-cardslot
+		 * and init-wireless methods, slightly hacked until
+		 * I got it working.
+		 */
+		LOCK(flags);
+		MACIO_OUT8(KEYLARGO_GPIO_0+0xf, 5);
+		(void)MACIO_IN8(KEYLARGO_GPIO_0+0xf);
+		UNLOCK(flags);
+		mdelay(10);
+		LOCK(flags);
+		MACIO_OUT8(KEYLARGO_GPIO_0+0xf, 4);
+		(void)MACIO_IN8(KEYLARGO_GPIO_0+0xf);
+		UNLOCK(flags);
+
+		mdelay(10);
+
+		LOCK(flags);
+		MACIO_BIC(KEYLARGO_FCR2, KL2_AIRPORT_RESET_N);
+		(void)MACIO_IN32(KEYLARGO_FCR2);
+		udelay(10);
+		MACIO_OUT8(KEYLARGO_GPIO_EXTINT_0+0xb, 0);
+		(void)MACIO_IN8(KEYLARGO_GPIO_EXTINT_0+0xb);
+		udelay(10);
+		MACIO_OUT8(KEYLARGO_GPIO_EXTINT_0+0xa, 0x28);
+		(void)MACIO_IN8(KEYLARGO_GPIO_EXTINT_0+0xa);
+		udelay(10);
+		MACIO_OUT8(KEYLARGO_GPIO_EXTINT_0+0xd, 0x28);
+		(void)MACIO_IN8(KEYLARGO_GPIO_EXTINT_0+0xd);
+		udelay(10);
+		MACIO_OUT8(KEYLARGO_GPIO_0+0xd, 0x28);
+		(void)MACIO_IN8(KEYLARGO_GPIO_0+0xd);
+		udelay(10);
+		MACIO_OUT8(KEYLARGO_GPIO_0+0xe, 0x28);
+		(void)MACIO_IN8(KEYLARGO_GPIO_0+0xe);
+		UNLOCK(flags);
+		udelay(10);
+		MACIO_OUT32(0x1c000, 0);
+		mdelay(1);
+		MACIO_OUT8(0x1a3e0, 0x41);
+		(void)MACIO_IN8(0x1a3e0);
+		udelay(10);
+		LOCK(flags);
+		MACIO_BIS(KEYLARGO_FCR2, KL2_AIRPORT_RESET_N);
+		(void)MACIO_IN32(KEYLARGO_FCR2);
+		UNLOCK(flags);
+		mdelay(100);
+
+		macio->flags |= MACIO_FLAG_AIRPORT_ON;
+	} else {
+		LOCK(flags);
+		MACIO_BIC(KEYLARGO_FCR2, KL2_AIRPORT_RESET_N);
+		(void)MACIO_IN32(KEYLARGO_FCR2);
+		MACIO_OUT8(KL_GPIO_AIRPORT_0, 0);
+		MACIO_OUT8(KL_GPIO_AIRPORT_1, 0);
+		MACIO_OUT8(KL_GPIO_AIRPORT_2, 0);
+		MACIO_OUT8(KL_GPIO_AIRPORT_3, 0);
+		MACIO_OUT8(KL_GPIO_AIRPORT_4, 0);
+		(void)MACIO_IN8(KL_GPIO_AIRPORT_4);
+		UNLOCK(flags);
+
+		macio->flags &= ~MACIO_FLAG_AIRPORT_ON;
+	}
+	return 0;
+}
+
+#ifdef CONFIG_SMP
+static int __pmac
+core99_reset_cpu(struct device_node* node, int param, int value)
+{
+	const int reset_lines[] = {	KL_GPIO_RESET_CPU0,
+					KL_GPIO_RESET_CPU1,
+					KL_GPIO_RESET_CPU2,
+					KL_GPIO_RESET_CPU3 };
+	int reset_io;
+	unsigned long flags;
+	struct macio_chip* macio;
+	
+	macio = &macio_chips[0];
+	if (macio->type != macio_keylargo && macio->type != macio_pangea)
+		return -ENODEV;
+	if (param > 3 || param < 0)
+		return -ENODEV;
+
+	reset_io = reset_lines[param];
+	
+	LOCK(flags);
+	MACIO_OUT8(reset_io, KEYLARGO_GPIO_OUTPUT_ENABLE);
+	(void)MACIO_IN8(reset_io);
+	udelay(1);
+	MACIO_OUT8(reset_io, KEYLARGO_GPIO_OUTPUT_ENABLE | KEYLARGO_GPIO_OUTOUT_DATA);
+	(void)MACIO_IN8(reset_io);
+	UNLOCK(flags);
+
+	return 0;
+}
+#endif /* CONFIG_SMP */
+
+static int __pmac
+core99_usb_enable(struct device_node* node, int param, int value)
+{
+	struct macio_chip* macio;
+	unsigned long flags;
+	char* prop;
+	int number;
+	u32 reg;
+	
+	macio = &macio_chips[0];
+	if (macio->type != macio_keylargo && macio->type != macio_pangea)
+		return -ENODEV;
+	
+	prop = (char *)get_property(node, "AAPL,clock-id", NULL);
+	if (!prop)
+		return -ENODEV;
+	if (strncmp(prop, "usb0u048", strlen("usb0u048")) == 0)
+		number = 0;
+	else if (strncmp(prop, "usb1u148", strlen("usb1u148")) == 0)
+		number = 2;
+	else
+		return -ENODEV;
+
+	/* Sorry for the brute-force locking, but this is only used during
+	 * sleep and the timing seem to be critical
+	 */
+	LOCK(flags);
+	if (value) {
+		/* Turn ON */
+		if (number == 0) {
+			MACIO_BIC(KEYLARGO_FCR0, (KL0_USB0_PAD_SUSPEND0 | KL0_USB0_PAD_SUSPEND1));
+			(void)MACIO_IN32(KEYLARGO_FCR0);
+			UNLOCK(flags);
+			mdelay(1);
+			LOCK(flags);
+			MACIO_BIS(KEYLARGO_FCR0, KL0_USB0_CELL_ENABLE);
+		} else {
+			MACIO_BIC(KEYLARGO_FCR0, (KL0_USB1_PAD_SUSPEND0 | KL0_USB1_PAD_SUSPEND1));
+			UNLOCK(flags);
+			(void)MACIO_IN32(KEYLARGO_FCR0);
+			mdelay(1);
+			LOCK(flags);
+			MACIO_BIS(KEYLARGO_FCR0, KL0_USB1_CELL_ENABLE);
+		}
+		reg = MACIO_IN32(KEYLARGO_FCR4);
+		reg &=	~(KL4_SET_PORT_ENABLE(number) | KL4_SET_PORT_RESUME(number) |
+			KL4_SET_PORT_CONNECT(number) | KL4_SET_PORT_DISCONNECT(number));
+		reg &=	~(KL4_SET_PORT_ENABLE(number+1) | KL4_SET_PORT_RESUME(number+1) |
+			KL4_SET_PORT_CONNECT(number+1) | KL4_SET_PORT_DISCONNECT(number+1));
+		MACIO_OUT32(KEYLARGO_FCR4, reg);
+		(void)MACIO_IN32(KEYLARGO_FCR4);
+		udelay(10);
+	} else {
+		/* Turn OFF */
+		reg = MACIO_IN32(KEYLARGO_FCR4);
+		reg |=	KL4_SET_PORT_ENABLE(number) | KL4_SET_PORT_RESUME(number) |
+			KL4_SET_PORT_CONNECT(number) | KL4_SET_PORT_DISCONNECT(number);
+		reg |=	KL4_SET_PORT_ENABLE(number+1) | KL4_SET_PORT_RESUME(number+1) |
+			KL4_SET_PORT_CONNECT(number+1) | KL4_SET_PORT_DISCONNECT(number+1);
+		MACIO_OUT32(KEYLARGO_FCR4, reg);
+		(void)MACIO_IN32(KEYLARGO_FCR4);
+		udelay(1);
+		if (number == 0) {
+			MACIO_BIC(KEYLARGO_FCR0, KL0_USB0_CELL_ENABLE);
+			(void)MACIO_IN32(KEYLARGO_FCR0);
+			udelay(1);
+			MACIO_BIS(KEYLARGO_FCR0, (KL0_USB0_PAD_SUSPEND0 | KL0_USB0_PAD_SUSPEND1));
+			(void)MACIO_IN32(KEYLARGO_FCR0);
+		} else {
+			MACIO_BIC(KEYLARGO_FCR0, KL0_USB1_CELL_ENABLE);
+			(void)MACIO_IN32(KEYLARGO_FCR0);
+			udelay(1);
+			MACIO_BIS(KEYLARGO_FCR0, (KL0_USB1_PAD_SUSPEND0 | KL0_USB1_PAD_SUSPEND1));
+			(void)MACIO_IN32(KEYLARGO_FCR0);
+		}
+		udelay(1);
+	}
+	UNLOCK(flags);
+
+	return 0;
+}
+
+static int __pmac
+core99_firewire_enable(struct device_node* node, int param, int value)
+{
+	unsigned long flags;
+	struct macio_chip* macio;
+
+	macio = &macio_chips[0];
+	if (macio->type != macio_keylargo && macio->type != macio_pangea)
+		return -ENODEV;
+	if (!(macio->flags & MACIO_FLAG_FW_SUPPORTED))
+		return -ENODEV;
+	
+	LOCK(flags);
+	if (value) {
+		UN_BIS(UNI_N_CLOCK_CNTL, UNI_N_CLOCK_CNTL_FW);
+		(void)UN_IN(UNI_N_CLOCK_CNTL);
+	} else {
+		UN_BIC(UNI_N_CLOCK_CNTL, UNI_N_CLOCK_CNTL_FW);
+		(void)UN_IN(UNI_N_CLOCK_CNTL);
+	}
+	UNLOCK(flags);
+	mdelay(1);
+
+	return 0;
+}
+
+static int __pmac
+core99_firewire_cable_power(struct device_node* node, int param, int value)
+{
+	unsigned long flags;
+	struct macio_chip* macio;
+
+	/* Trick: we allow NULL node */
+	if (pmac_mb.model_id != PMAC_TYPE_FW_IBOOK &&
+	    pmac_mb.model_id != PMAC_TYPE_PISMO &&
+	    pmac_mb.model_id != PMAC_TYPE_IBOOK2)
+	    	return -ENODEV;
+	macio = &macio_chips[0];
+	if (macio->type != macio_keylargo && macio->type != macio_pangea)
+		return -ENODEV;
+	if (!(macio->flags & MACIO_FLAG_FW_SUPPORTED))
+		return -ENODEV;
+	
+	LOCK(flags);
+	if (value) {
+		MACIO_OUT8(KL_GPIO_FW_CABLE_POWER , 0);
+		MACIO_IN8(KL_GPIO_FW_CABLE_POWER);
+		udelay(10);
+	} else {
+		MACIO_OUT8(KL_GPIO_FW_CABLE_POWER , 4);
+		MACIO_IN8(KL_GPIO_FW_CABLE_POWER); udelay(10);
+	}
+	UNLOCK(flags);
+	mdelay(1);
+
+	return 0;
+}
+
+static int __pmac
+core99_read_gpio(struct device_node* node, int param, int value)
+{
+	struct macio_chip* macio = &macio_chips[0];
+	
+	return MACIO_IN8(param);
+}
+
+
+static int __pmac
+core99_write_gpio(struct device_node* node, int param, int value)
+{
+	struct macio_chip* macio = &macio_chips[0];
+
+	MACIO_OUT8(param, (u8)(value & 0xff));
+	return 0;
+}
+
+static void __pmac
+keylargo_shutdown(struct macio_chip* macio, int restart)
+{
+	u32 temp;
+
+	mdelay(1);
+	MACIO_BIS(KEYLARGO_FCR0, KL0_USB_REF_SUSPEND);
+	(void)MACIO_IN32(KEYLARGO_FCR0);
+	mdelay(100);
+
+	MACIO_BIC(KEYLARGO_FCR0,KL0_SCCA_ENABLE | KL0_SCCB_ENABLE |
+				KL0_SCC_CELL_ENABLE |
+		      		KL0_IRDA_ENABLE | KL0_IRDA_CLK32_ENABLE |
+		      		KL0_IRDA_CLK19_ENABLE);
+
+	(void)MACIO_IN32(KEYLARGO_FCR0); udelay(10);
+	MACIO_BIC(KEYLARGO_MBCR, KL_MBCR_MB0_DEV_MASK);
+	(void)MACIO_IN32(KEYLARGO_MBCR); udelay(10);
+
+	MACIO_BIC(KEYLARGO_FCR1,
+		KL1_AUDIO_SEL_22MCLK | KL1_AUDIO_CLK_ENABLE_BIT |
+		KL1_AUDIO_CLK_OUT_ENABLE | KL1_AUDIO_CELL_ENABLE |
+		KL1_I2S0_CELL_ENABLE | KL1_I2S0_CLK_ENABLE_BIT |
+		KL1_I2S0_ENABLE | KL1_I2S1_CELL_ENABLE |
+		KL1_I2S1_CLK_ENABLE_BIT | KL1_I2S1_ENABLE |
+		KL1_EIDE0_ENABLE | KL1_EIDE0_RESET_N |
+		KL1_EIDE1_ENABLE | KL1_EIDE1_RESET_N |
+		KL1_UIDE_ENABLE);
+	(void)MACIO_IN32(KEYLARGO_FCR1); udelay(10);
+
+	MACIO_BIS(KEYLARGO_FCR2, KL2_MODEM_POWER_N);
+ 	udelay(10);
+ 	MACIO_BIC(KEYLARGO_FCR2, KL2_IOBUS_ENABLE);
+ 	udelay(10);
+	temp = MACIO_IN32(KEYLARGO_FCR3);
+	if (macio->rev >= 2)
+		temp |= (KL3_SHUTDOWN_PLL2X | KL3_SHUTDOWN_PLL_TOTAL);
+		
+	temp |= KL3_SHUTDOWN_PLLKW6 | KL3_SHUTDOWN_PLLKW4 |
+		KL3_SHUTDOWN_PLLKW35 | KL3_SHUTDOWN_PLLKW12;
+	temp &= ~(KL3_CLK66_ENABLE | KL3_CLK49_ENABLE | KL3_CLK45_ENABLE
+		| KL3_CLK31_ENABLE | KL3_TIMER_CLK18_ENABLE | KL3_I2S1_CLK18_ENABLE
+		| KL3_I2S0_CLK18_ENABLE | KL3_VIA_CLK16_ENABLE);
+	MACIO_OUT32(KEYLARGO_FCR3, temp);
+	(void)MACIO_IN32(KEYLARGO_FCR3); udelay(10);
+}
+
+static void __pmac
+pangea_shutdown(struct macio_chip* macio, int restart)
+{
+	u32 temp;
+
+	MACIO_BIC(KEYLARGO_FCR0,KL0_SCCA_ENABLE | KL0_SCCB_ENABLE |
+				KL0_SCC_CELL_ENABLE |
+				KL0_USB0_CELL_ENABLE | KL0_USB1_CELL_ENABLE);
+
+	(void)MACIO_IN32(KEYLARGO_FCR0); udelay(10);
+	MACIO_BIC(KEYLARGO_MBCR, KL_MBCR_MB0_DEV_MASK);
+	(void)MACIO_IN32(KEYLARGO_MBCR); udelay(10);
+
+	MACIO_BIC(KEYLARGO_FCR1,
+		KL1_AUDIO_SEL_22MCLK | KL1_AUDIO_CLK_ENABLE_BIT |
+		KL1_AUDIO_CLK_OUT_ENABLE | KL1_AUDIO_CELL_ENABLE |
+		KL1_I2S0_CELL_ENABLE | KL1_I2S0_CLK_ENABLE_BIT |
+		KL1_I2S0_ENABLE | KL1_I2S1_CELL_ENABLE |
+		KL1_I2S1_CLK_ENABLE_BIT | KL1_I2S1_ENABLE |
+		KL1_UIDE_ENABLE);
+	(void)MACIO_IN32(KEYLARGO_FCR1); udelay(10);
+
+	MACIO_BIS(KEYLARGO_FCR2, KL2_MODEM_POWER_N);
+ 	udelay(10);
+	temp = MACIO_IN32(KEYLARGO_FCR3);
+	temp |= KL3_SHUTDOWN_PLLKW6 | KL3_SHUTDOWN_PLLKW4 |
+		KL3_SHUTDOWN_PLLKW35;
+	temp &= ~(KL3_CLK49_ENABLE | KL3_CLK45_ENABLE
+		| KL3_CLK31_ENABLE | KL3_TIMER_CLK18_ENABLE | KL3_I2S1_CLK18_ENABLE
+		| KL3_I2S0_CLK18_ENABLE | KL3_VIA_CLK16_ENABLE);
+	MACIO_OUT32(KEYLARGO_FCR3, temp);
+	(void)MACIO_IN32(KEYLARGO_FCR3); udelay(10);
+}
+
+static int __pmac
+core99_sleep(void)
+{
+	struct macio_chip* macio;
+	int i;
+
+	macio = &macio_chips[0];
+	if (macio->type != macio_keylargo && macio->type != macio_pangea)
+		return -ENODEV;
+	
+	/* We power off the wireless slot in case it was not done
+	 * by the driver. We don't power it on automatically however
+	 */
+	if (macio->flags & MACIO_FLAG_AIRPORT_ON)
+		core99_airport_enable(macio->of_node, 0, 0);
+
+	/* We power off the FW cable. Should be done by the driver... */
+	if (macio->flags & MACIO_FLAG_FW_SUPPORTED) {
+		core99_firewire_enable(NULL, 0, 0);
+		core99_firewire_cable_power(NULL, 0, 0);
+	}
+
+	/* We make sure int. modem is off (in case driver lost it) */
+	core99_modem_enable(macio->of_node, 0, 0);
+	/* We make sure the sound is off as well */
+	core99_sound_chip_enable(macio->of_node, 0, 0);
+	 
+	/*
+	 * Save various bits of KeyLargo
+	 */
+
+	/* Save the state of the various GPIOs */
+	save_gpio_levels[0] = MACIO_IN32(KEYLARGO_GPIO_LEVELS0);
+	save_gpio_levels[1] = MACIO_IN32(KEYLARGO_GPIO_LEVELS1);
+	for (i=0; i<KEYLARGO_GPIO_EXTINT_CNT; i++)
+		save_gpio_extint[i] = MACIO_IN8(KEYLARGO_GPIO_EXTINT_0+i);
+	for (i=0; i<KEYLARGO_GPIO_CNT; i++)
+		save_gpio_normal[i] = MACIO_IN8(KEYLARGO_GPIO_0+i);
+
+	/* Save the FCRs */
+	save_mbcr = MACIO_IN32(KEYLARGO_MBCR);
+	save_fcr[0] = MACIO_IN32(KEYLARGO_FCR0);
+	save_fcr[1] = MACIO_IN32(KEYLARGO_FCR1);
+	save_fcr[2] = MACIO_IN32(KEYLARGO_FCR2);
+	save_fcr[3] = MACIO_IN32(KEYLARGO_FCR3);
+	save_fcr[4] = MACIO_IN32(KEYLARGO_FCR4);
+
+	/* Save state & config of DBDMA channels */
+	dbdma_save(macio, save_dbdma);
+	
+	/*
+	 * Turn off as much as we can
+	 */
+	if (macio->type == macio_pangea)
+		pangea_shutdown(macio, 0);
+	else if (macio->type == macio_keylargo)
+		keylargo_shutdown(macio, 0);
+	
+	/* 
+	 * Put the host bridge to sleep
+	 */
+
+	save_unin_clock_ctl = UN_IN(UNI_N_CLOCK_CNTL);
+	UN_OUT(UNI_N_CLOCK_CNTL, save_unin_clock_ctl &
+		~(UNI_N_CLOCK_CNTL_GMAC|UNI_N_CLOCK_CNTL_FW/*|UNI_N_CLOCK_CNTL_PCI*/));
+	udelay(100);
+	UN_OUT(UNI_N_HWINIT_STATE, UNI_N_HWINIT_STATE_SLEEPING);
+	UN_OUT(UNI_N_POWER_MGT, UNI_N_POWER_MGT_SLEEP);
+
+	/*
+	 * FIXME: A bit of black magic with OpenPIC (don't ask me why)
+	 */
+	if (pmac_mb.model_id == PMAC_TYPE_SAWTOOTH) {
+		MACIO_BIS(0x506e0, 0x00400000);
+		MACIO_BIS(0x506e0, 0x80000000);
+	}
+	return 0;
+}
+
+static int __pmac
+core99_wake_up(void)
+{
+	struct macio_chip* macio;
+	int i;
+
+	macio = &macio_chips[0];
+	if (macio->type != macio_keylargo && macio->type != macio_pangea)
+		return -ENODEV;
+
+	/*
+	 * Wakeup the host bridge
+	 */
+	UN_OUT(UNI_N_POWER_MGT, UNI_N_POWER_MGT_NORMAL);
+	udelay(10);
+	UN_OUT(UNI_N_HWINIT_STATE, UNI_N_HWINIT_STATE_RUNNING);
+	udelay(10);
+	
+	/*
+	 * Restore KeyLargo
+	 */
+	 
+	MACIO_OUT32(KEYLARGO_MBCR, save_mbcr);
+	(void)MACIO_IN32(KEYLARGO_MBCR); udelay(10);
+	MACIO_OUT32(KEYLARGO_FCR0, save_fcr[0]);
+	(void)MACIO_IN32(KEYLARGO_FCR0); udelay(10);
+	MACIO_OUT32(KEYLARGO_FCR1, save_fcr[1]);
+	(void)MACIO_IN32(KEYLARGO_FCR1); udelay(10);
+	MACIO_OUT32(KEYLARGO_FCR2, save_fcr[2]);
+	(void)MACIO_IN32(KEYLARGO_FCR2); udelay(10);
+	MACIO_OUT32(KEYLARGO_FCR3, save_fcr[3]);
+	(void)MACIO_IN32(KEYLARGO_FCR3); udelay(10);
+	MACIO_OUT32(KEYLARGO_FCR4, save_fcr[4]);
+	(void)MACIO_IN32(KEYLARGO_FCR4); udelay(10);
+
+	dbdma_restore(macio, save_dbdma);
+
+	MACIO_OUT32(KEYLARGO_GPIO_LEVELS0, save_gpio_levels[0]);
+	MACIO_OUT32(KEYLARGO_GPIO_LEVELS1, save_gpio_levels[1]);
+	for (i=0; i<KEYLARGO_GPIO_EXTINT_CNT; i++)
+		MACIO_OUT8(KEYLARGO_GPIO_EXTINT_0+i, save_gpio_extint[i]);
+	for (i=0; i<KEYLARGO_GPIO_CNT; i++)
+		MACIO_OUT8(KEYLARGO_GPIO_0+i, save_gpio_normal[i]);
+
+	/* FIXME more black magic with OpenPIC ... */
+	if (pmac_mb.model_id == PMAC_TYPE_SAWTOOTH) {
+		MACIO_BIC(0x506e0, 0x00400000);
+		MACIO_BIC(0x506e0, 0x80000000);
+	}
+
+	UN_OUT(UNI_N_CLOCK_CNTL, save_unin_clock_ctl);
+	udelay(100);
+
+	return 0;
+}
+
+static int __pmac
+core99_sleep_state(struct device_node* node, int param, int value)
+{
+	if ((pmac_mb.board_flags & PMAC_MB_CAN_SLEEP) == 0)
+		return -EPERM;
+	if (value == 1)
+		return core99_sleep();
+	else if (value == 0)
+		return core99_wake_up();
+	return 0;
+}
+
+static int __pmac
+pangea_modem_enable(struct device_node* node, int param, int value)
+{
+	struct macio_chip*	macio;
+	u8			gpio;
+	unsigned long		flags;
+	
+	macio = macio_find(node, 0);
+	if (!macio)
+		return -ENODEV;
+	gpio = MACIO_IN8(KL_GPIO_MODEM_RESET);
+	gpio |= KEYLARGO_GPIO_OUTPUT_ENABLE;
+	gpio &= ~KEYLARGO_GPIO_OUTOUT_DATA;
+	
+	if (!value) {
+		LOCK(flags);
+		MACIO_OUT8(KL_GPIO_MODEM_RESET, gpio);
+		UNLOCK(flags);
+		(void)MACIO_IN8(KL_GPIO_MODEM_RESET);
+		mdelay(250);
+	}
+    	LOCK(flags);
+	if (value) {
+		MACIO_OUT8(KL_GPIO_MODEM_POWER,
+			KEYLARGO_GPIO_OUTPUT_ENABLE);
+    		UNLOCK(flags);
+	    	(void)MACIO_IN32(KEYLARGO_FCR2);
+		mdelay(250);
+	} else {
+		MACIO_OUT8(KL_GPIO_MODEM_POWER,
+			KEYLARGO_GPIO_OUTPUT_ENABLE | KEYLARGO_GPIO_OUTOUT_DATA);
+    		UNLOCK(flags);
+	}
+	if (value) {
+		LOCK(flags);
+		MACIO_OUT8(KL_GPIO_MODEM_RESET, gpio | KEYLARGO_GPIO_OUTOUT_DATA);
+		(void)MACIO_IN8(KL_GPIO_MODEM_RESET);
+	    	UNLOCK(flags); mdelay(250); LOCK(flags);
+		MACIO_OUT8(KL_GPIO_MODEM_RESET, gpio);
+		(void)MACIO_IN8(KL_GPIO_MODEM_RESET);
+	    	UNLOCK(flags); mdelay(250); LOCK(flags);
+		MACIO_OUT8(KL_GPIO_MODEM_RESET, gpio | KEYLARGO_GPIO_OUTOUT_DATA);
+		(void)MACIO_IN8(KL_GPIO_MODEM_RESET);
+	    	UNLOCK(flags); mdelay(250); LOCK(flags);
+	}
+	return 0;
+}
+
+
+static int __pmac
+generic_get_mb_info(struct device_node* node, int param, int value)
+{
+	switch(param) {
+		case PMAC_MB_INFO_MODEL:
+			return pmac_mb.model_id;
+		case PMAC_MB_INFO_FLAGS:
+			return pmac_mb.board_flags;
+		case PMAC_MB_INFO_NAME:	
+			/* hack hack hack... but should work */
+			return (int)pmac_mb.model_name;
+	}
+	return 0;
+}
+
+
+/* 
+ * Table definitions
+ */
+ 
+/* Used on any machine
+ */
+static struct feature_table_entry any_features[]  __pmacdata = {
+	{ PMAC_FTR_GET_MB_INFO,		generic_get_mb_info },
+	{ 0, NULL }
+};
+
+/* OHare based motherboards. Currently, we only use these on the
+ * 2400,3400 and 3500 series powerbooks. Some older desktops seem
+ * to have issues with turning on/off those asic cells
+ */
+static struct feature_table_entry ohare_features[]  __pmacdata = {
+	{ PMAC_FTR_SCC_ENABLE,		ohare_scc_enable },
+	{ PMAC_FTR_SWIM3_ENABLE,	ohare_floppy_enable },
+	{ PMAC_FTR_MESH_ENABLE,		ohare_mesh_enable },
+	{ PMAC_FTR_IDE_ENABLE,		ohare_ide_enable},
+	{ PMAC_FTR_IDE_RESET,		ohare_ide_reset},
+	{ PMAC_FTR_SLEEP_STATE,		ohare_sleep_state },
+	{ 0, NULL }
+};
+
+/* Heathrow desktop machines (Beige G3).
+ * Separated as some features couldn't be properly tested
+ * and the serial port control bits appear to confuse it.
+ */
+static struct feature_table_entry heathrow_desktop_features[]  __pmacdata = {
+	{ PMAC_FTR_SWIM3_ENABLE,	heathrow_floppy_enable },
+	{ PMAC_FTR_MESH_ENABLE,		heathrow_mesh_enable },
+	{ PMAC_FTR_IDE_ENABLE,		heathrow_ide_enable },
+	{ PMAC_FTR_IDE_RESET,		heathrow_ide_reset },
+	{ PMAC_FTR_BMAC_ENABLE,		heathrow_bmac_enable },
+	{ 0, NULL }
+};
+
+/* Heathrow based laptop, that is the Wallstreet and mainstreet
+ * powerbooks.
+ */
+static struct feature_table_entry heathrow_laptop_features[]  __pmacdata = {
+	{ PMAC_FTR_SCC_ENABLE,		heathrow_scc_enable },
+	{ PMAC_FTR_MODEM_ENABLE,	heathrow_modem_enable },
+	{ PMAC_FTR_SWIM3_ENABLE,	heathrow_floppy_enable },
+	{ PMAC_FTR_MESH_ENABLE,		heathrow_mesh_enable },
+	{ PMAC_FTR_IDE_ENABLE,		heathrow_ide_enable },
+	{ PMAC_FTR_IDE_RESET,		heathrow_ide_reset },
+	{ PMAC_FTR_BMAC_ENABLE,		heathrow_bmac_enable },
+	{ PMAC_FTR_SOUND_CHIP_ENABLE,	heathrow_sound_enable },
+	{ PMAC_FTR_SLEEP_STATE,		heathrow_sleep_state },
+	{ 0, NULL }
+};
+
+/* Paddington based machines
+ * The lombard (101) powerbook, first iMac models, B&W G3 and Yikes G4.
+ */
+static struct feature_table_entry paddington_features[]  __pmacdata = {
+	{ PMAC_FTR_SCC_ENABLE,		heathrow_scc_enable },
+	{ PMAC_FTR_MODEM_ENABLE,	heathrow_modem_enable },
+	{ PMAC_FTR_SWIM3_ENABLE,	heathrow_floppy_enable },
+	{ PMAC_FTR_MESH_ENABLE,		heathrow_mesh_enable },
+	{ PMAC_FTR_IDE_ENABLE,		heathrow_ide_enable },
+	{ PMAC_FTR_IDE_RESET,		heathrow_ide_reset },
+	{ PMAC_FTR_BMAC_ENABLE,		heathrow_bmac_enable },
+	{ PMAC_FTR_SOUND_CHIP_ENABLE,	heathrow_sound_enable },
+	{ PMAC_FTR_SLEEP_STATE,		heathrow_sleep_state },
+	{ 0, NULL }
+};
+
+/* Core99 & MacRISC 2 machines (all machines released since the
+ * iBook (included), that is all AGP machines, except pangea
+ * chipset. The pangea chipset is the "combo" UniNorth/KeyLargo
+ * used on iBook2 & iMac "flow power".
+ */
+static struct feature_table_entry core99_features[]  __pmacdata = {
+	{ PMAC_FTR_SCC_ENABLE,		core99_scc_enable },
+	{ PMAC_FTR_MODEM_ENABLE,	core99_modem_enable },
+	{ PMAC_FTR_IDE_ENABLE,		core99_ide_enable },
+	{ PMAC_FTR_IDE_RESET,		core99_ide_reset },
+	{ PMAC_FTR_GMAC_ENABLE,		core99_gmac_enable },
+	{ PMAC_FTR_GMAC_PHY_RESET,	core99_gmac_phy_reset },
+	{ PMAC_FTR_SOUND_CHIP_ENABLE,	core99_sound_chip_enable },
+	{ PMAC_FTR_AIRPORT_ENABLE,	core99_airport_enable },
+	{ PMAC_FTR_USB_ENABLE,		core99_usb_enable },
+	{ PMAC_FTR_1394_ENABLE,		core99_firewire_enable },
+	{ PMAC_FTR_1394_CABLE_POWER,	core99_firewire_cable_power },
+	{ PMAC_FTR_SLEEP_STATE,		core99_sleep_state },
+#ifdef CONFIG_SMP
+	{ PMAC_FTR_RESET_CPU,		core99_reset_cpu },
+#endif /* CONFIG_SMP */
+	{ PMAC_FTR_READ_GPIO,		core99_read_gpio },
+	{ PMAC_FTR_WRITE_GPIO,		core99_write_gpio },
+	{ 0, NULL }
+};
+
+/* Pangea features
+ */
+static struct feature_table_entry pangea_features[]  __pmacdata = {
+	{ PMAC_FTR_SCC_ENABLE,		core99_scc_enable },
+	{ PMAC_FTR_MODEM_ENABLE,	pangea_modem_enable },
+	{ PMAC_FTR_IDE_ENABLE,		core99_ide_enable },
+	{ PMAC_FTR_IDE_RESET,		core99_ide_reset },
+	{ PMAC_FTR_GMAC_ENABLE,		core99_gmac_enable },
+	{ PMAC_FTR_GMAC_PHY_RESET,	core99_gmac_phy_reset },
+	{ PMAC_FTR_SOUND_CHIP_ENABLE,	core99_sound_chip_enable },
+	{ PMAC_FTR_AIRPORT_ENABLE,	core99_airport_enable },
+	{ PMAC_FTR_USB_ENABLE,		core99_usb_enable },
+	{ PMAC_FTR_1394_ENABLE,		core99_firewire_enable },
+	{ PMAC_FTR_1394_CABLE_POWER,	core99_firewire_cable_power },
+	{ PMAC_FTR_SLEEP_STATE,		core99_sleep_state },
+	{ PMAC_FTR_READ_GPIO,		core99_read_gpio },
+	{ PMAC_FTR_WRITE_GPIO,		core99_write_gpio },
+	{ 0, NULL }
+};
+	
+static struct pmac_mb_def pmac_mb_defs[] __pmacdata = {
+	/* Warning: ordering is important as some models may claim
+	 * beeing compatible with several types
+	 */
+	{	"AAPL,8500",			"PowerMac 8500/8600",
+		PMAC_TYPE_PSURGE,		NULL,
+		0
+	},
+	{	"AAPL,9500",			"PowerMac 9500/9600",
+		PMAC_TYPE_PSURGE,		NULL,
+		0
+	},
+	{	"AAPL,7500",			"PowerMac 7500",
+		PMAC_TYPE_PSURGE,		NULL,
+		0
+	},
+	{	"AAPL,e407",			"Alchemy",
+		PMAC_TYPE_ALCHEMY,		NULL,
+		0
+	},
+	{	"AAPL,e411",			"Gazelle",
+		PMAC_TYPE_GAZELLE,		NULL,
+		0
+	},
+	{	"AAPL,3400/2400",		"PowerBook 3400",
+		PMAC_TYPE_HOOPER,		ohare_features,
+		PMAC_MB_CAN_SLEEP
+	},
+	{	"AAPL,3500",			"PowerBook 3500",
+		PMAC_TYPE_KANGA,		ohare_features,
+		PMAC_MB_CAN_SLEEP
+	},
+	{	"AAPL,Gossamer",		"PowerMac G3 (Gossamer)",
+		PMAC_TYPE_GOSSAMER,		heathrow_desktop_features,
+		0
+	},
+	{	"AAPL,PowerMac G3",		"PowerMac G3 (Silk)",
+		PMAC_TYPE_SILK,			heathrow_desktop_features,
+		0
+	},
+	{	"AAPL,PowerBook1998",		"PowerBook Wallstreet",
+		PMAC_TYPE_WALLSTREET,		heathrow_laptop_features,
+		PMAC_MB_CAN_SLEEP
+	},
+	{	"AAPL,PowerBook1,1",		"PowerBook 101 (Lombard)",
+		PMAC_TYPE_101_PBOOK,		paddington_features,
+		PMAC_MB_CAN_SLEEP
+	},
+	{	"iMac,1",			"iMac (first generation)",
+		PMAC_TYPE_ORIG_IMAC,		paddington_features,
+		0
+	},
+	{	"PowerMac4,1",			"iMac \"Flower Power\"",
+		PMAC_TYPE_PANGEA_IMAC,		pangea_features,
+		PMAC_MB_CAN_SLEEP
+	},
+	{	"PowerBook4,1",			"iBook 2",
+		PMAC_TYPE_IBOOK2,		pangea_features,
+		PMAC_MB_CAN_SLEEP
+	},
+	{	"PowerMac1,1",			"Blue&White G3",
+		PMAC_TYPE_YOSEMITE,		paddington_features,
+		0
+	},
+	{	"PowerMac1,2",			"PowerMac G4 PCI Graphics",
+		PMAC_TYPE_YIKES,		paddington_features,
+		0
+	},
+	{	"PowerBook2,1",			"iBook (first generation)",
+		PMAC_TYPE_ORIG_IBOOK,		core99_features,
+		PMAC_MB_CAN_SLEEP
+	},
+	{	"PowerMac3,1",			"PowerMac G4 AGP Graphics",
+		PMAC_TYPE_SAWTOOTH,		core99_features,
+		0
+	},
+	{	"PowerMac3,2",			"PowerMac G4 AGP Graphics",
+		PMAC_TYPE_SAWTOOTH,		core99_features,
+		0
+	},
+	{	"PowerMac3,3",			"PowerMac G4 AGP Graphics",
+		PMAC_TYPE_SAWTOOTH,		core99_features,
+		0
+	},
+	{	"PowerMac2,1",			"iMac FireWire",
+		PMAC_TYPE_FW_IMAC,		core99_features,
+		PMAC_MB_CAN_SLEEP
+	},
+	{	"PowerMac2,2",			"iMac FireWire",
+		PMAC_TYPE_FW_IMAC,		core99_features,
+		PMAC_MB_CAN_SLEEP
+	},
+	{	"PowerBook2,2",			"iBook FireWire",
+		PMAC_TYPE_FW_IBOOK,		core99_features,
+		PMAC_MB_CAN_SLEEP
+	},
+	{	"PowerMac5,1",			"PowerMac G4 Cube",
+		PMAC_TYPE_CUBE,			core99_features,
+	},
+	{	"PowerMac3,4",			"PowerMac G4 Silver",
+		PMAC_TYPE_QUICKSILVER,		core99_features,
+		0
+	},
+	{	"PowerMac3,5",			"PowerMac G4 Silver",
+		PMAC_TYPE_QUICKSILVER,		core99_features,
+		0
+	},
+	{	"PowerBook3,1",			"PowerBook Pismo",
+		PMAC_TYPE_PISMO,		core99_features,
+		PMAC_MB_CAN_SLEEP
+	},
+	{	"PowerBook3,2",			"PowerBook Titanium",
+		PMAC_TYPE_TITANIUM,		core99_features,
+		PMAC_MB_CAN_SLEEP
+	},
+	{	"PowerBook3,3",			"PowerBook Titanium II",
+		PMAC_TYPE_TITANIUM2,		core99_features,
+		PMAC_MB_CAN_SLEEP
+	},
+};
+
+/*
+ * The toplevel feature_call callback
+ */
+int __pmac
+pmac_do_feature_call(unsigned int selector, ...)
+{
+	struct device_node* node;
+	int param, value, i;
+	feature_call func = NULL;
+	va_list args;
+	
+	if (!pmac_mb.features)
+		return -ENODEV;
+	for (i=0; pmac_mb.features[i].function; i++)
+		if (pmac_mb.features[i].selector == selector) {
+			func = pmac_mb.features[i].function;
+			break;
+		}
+	if (!func)
+		for (i=0; any_features[i].function; i++)
+			if (any_features[i].selector == selector) {
+				func = any_features[i].function;
+				break;
+			}
+	if (!func)
+		return -ENODEV;
+
+	va_start(args, selector);
+	node = (struct device_node*)va_arg(args, void*);
+	param = va_arg(args, int);
+	value = va_arg(args, int);
+	va_end(args);
+
+	return func(node, param, value);
+}
+
+static int __init
+probe_motherboard(void)
+{
+	int i;
+	struct macio_chip* macio = &macio_chips[0];
+
+	/* Lookup known motherboard type in device-tree */
+	for(i=0; i<(sizeof(pmac_mb_defs)/sizeof(struct pmac_mb_def)); i++) {
+	    if (machine_is_compatible(pmac_mb_defs[i].model_string)) {
+		pmac_mb = pmac_mb_defs[i];
+		goto found;
+	    }
+	}
+
+	/* Fallback to selection depending on mac-io chip type */
+	switch(macio->type) {
+	    case macio_grand_central:
+		pmac_mb.model_id = PMAC_TYPE_PSURGE;
+		pmac_mb.model_name = "Unknown PowerSurge";
+		break;
+	    case macio_ohare:
+		pmac_mb.model_id = PMAC_TYPE_UNKNOWN_OHARE;
+		pmac_mb.model_name = "Unknown OHare-based";
+	    	break;
+	    case macio_heathrow:
+		pmac_mb.model_id = PMAC_TYPE_UNKNOWN_HEATHROW;
+		pmac_mb.model_name = "Unknown Heathrow-based";
+		pmac_mb.features = heathrow_desktop_features;
+		break;
+	    case macio_paddington:
+		pmac_mb.model_id = PMAC_TYPE_UNKNOWN_PADDINGTON;
+		pmac_mb.model_name = "Unknown Paddington-based";
+	    	pmac_mb.features = paddington_features;
+		break;
+	    case macio_keylargo:
+		pmac_mb.model_id = PMAC_TYPE_UNKNOWN_CORE99;
+		pmac_mb.model_name = "Unknown Keylargo-based";
+	    	pmac_mb.features = core99_features;
+		break;
+	    case macio_pangea:
+		pmac_mb.model_id = PMAC_TYPE_UNKNOWN_PANGEA;
+		pmac_mb.model_name = "Unknown Pangea-based";
+	    	pmac_mb.features = pangea_features;
+		break;
+	    default:
+	    	return -ENODEV;
+	}
+found:
+	/* Fixup Hooper vs. Comet */
+	if (pmac_mb.model_id == PMAC_TYPE_HOOPER) {
+		u32* mach_id_ptr = (u32*)ioremap(0xf3000034, 4);
+		if (!mach_id_ptr)
+			return -ENODEV;
+		/* Here, I used to disable the media-bay on comet. It
+		 * appears this is wrong, the floppy connector is actually
+		 * a kind of media-bay and works with the current driver.
+		 */
+		if ((*mach_id_ptr) & 0x20000000UL)
+			pmac_mb.model_id = PMAC_TYPE_COMET;
+		iounmap(mach_id_ptr);
+	}
+
+	/* Set default value of powersave_nap on machines that support it.
+	 * It appears that uninorth rev 3 has a problem with it, we don't
+	 * enable it on those. In theory, the flush-on-lock property is
+	 * supposed to be set when not supported, but I'm not very confident
+	 * that all Apple OF revs did it properly, I do it the paranoid way.
+	 */
+	while (uninorth_base && uninorth_rev > 3) {
+		struct device_node* np = find_path_device("/cpus");
+		u32 pvr = mfspr(PVR);
+		if (!np || !np->child) {
+			printk(KERN_WARNING "Can't find CPU(s) in device tree !\n");
+			break;
+		}
+		np = np->child;
+		/* Nap mode not supported on SMP */
+		if (np->sibling)
+			break;
+		/* Nap mode not supported if flush-on-lock property is present */
+		if (get_property(np, "flush-on-lock", NULL))
+			break;
+		/* Some 7450 may have problem with NAP mode too ... */
+		if (((pvr >> 16) == 0x8000) && ((pvr & 0xffff) < 0x0201))
+			break;
+		powersave_nap = 1;
+		printk(KERN_INFO "Processor NAP mode on idle enabled.\n");
+		break;
+	}
+
+	printk(KERN_INFO "PowerMac motherboard: %s\n", pmac_mb.model_name);
+	return 0;
+}
+
+/* Initialize the Core99 UniNorth host bridge and memory controller
+ */
+static void __init
+probe_uninorth(void)
+{
+	unsigned long actrl;
+	
+	/* Locate core99 Uni-N */
+	uninorth_node = find_devices("uni-n");
+	if (uninorth_node && uninorth_node->n_addrs > 0) {
+		uninorth_base = ioremap(uninorth_node->addrs[0].address, 0x1000);
+		uninorth_rev = in_be32(UN_REG(UNI_N_VERSION));
+	} else
+		uninorth_node = NULL;
+		
+	if (!uninorth_node)
+		return;
+	
+	printk(KERN_INFO "Found Uninorth memory controller & host bridge, revision: %d\n",
+			uninorth_rev);
+
+	/* Set the arbitrer QAck delay according to what Apple does
+	 */
+	if (uninorth_rev < 0x10) {
+		actrl = UN_IN(UNI_N_ARB_CTRL) & ~UNI_N_ARB_CTRL_QACK_DELAY_MASK;
+		actrl |= ((uninorth_rev < 3) ? UNI_N_ARB_CTRL_QACK_DELAY105 :
+			UNI_N_ARB_CTRL_QACK_DELAY) << UNI_N_ARB_CTRL_QACK_DELAY_SHIFT;
+		UN_OUT(UNI_N_ARB_CTRL, actrl);
+	}
+}	
+
+static void __init
+probe_one_macio(const char* name, const char* compat, int type)
+{
+	struct device_node*	node;
+	int			i;
+	volatile u32*		base;
+	u32*			revp;
+	
+	node = find_devices(name);
+	if (!node || !node->n_addrs)
+		return;
+	if (compat)
+		do {
+			if (device_is_compatible(node, compat))
+				break;
+			node = node->next;
+		} while (node);
+	if (!node)
+		return;
+	for(i=0; i<MAX_MACIO_CHIPS; i++) {
+		if (!macio_chips[i].of_node)
+			break;
+		if (macio_chips[i].of_node == node)
+			return;
+	}
+	if (i >= MAX_MACIO_CHIPS) {
+		printk(KERN_ERR "pmac_feature: Please increase MAX_MACIO_CHIPS !\n");
+		printk(KERN_ERR "pmac_feature: %s skipped\n", node->full_name);
+		return;
+	}
+	base = (volatile u32*)ioremap(node->addrs[0].address, node->addrs[0].size);
+	if (!base) {
+		printk(KERN_ERR "pmac_feature: Can't map mac-io chip !\n");
+		return;
+	}
+	if (type == macio_keylargo) {
+		u32* did = (u32 *)get_property(node, "device-id", NULL);
+		if (*did == 0x00000025)
+			type = macio_pangea;
+	}
+	macio_chips[i].of_node	= node;
+	macio_chips[i].type	= type;
+	macio_chips[i].base	= base;
+	macio_chips[i].flags	= MACIO_FLAG_SCCB_ON | MACIO_FLAG_SCCB_ON;
+	revp = (u32 *)get_property(node, "revision-id", NULL);
+	if (revp)
+		macio_chips[i].rev = *revp;
+	printk(KERN_INFO "Found a %s mac-io controller, rev: %d, mapped at 0x%p\n",
+		macio_names[type], macio_chips[i].rev, macio_chips[i].base);
+}
+
+static int __init
+probe_macios(void)
+{
+	/* Warning, ordering is important */
+	probe_one_macio("gc", NULL, macio_grand_central);
+	probe_one_macio("ohare", NULL, macio_ohare);
+	probe_one_macio("pci106b,7", NULL, macio_ohareII);
+	probe_one_macio("mac-io", "keylargo", macio_keylargo);
+	probe_one_macio("mac-io", "paddington", macio_paddington);
+	probe_one_macio("mac-io", "gatwick", macio_gatwick);
+	probe_one_macio("mac-io", "heathrow", macio_heathrow);
+
+	/* Make sure the "main" macio chip appear first */
+	if (macio_chips[0].type == macio_gatwick
+	    && macio_chips[1].type == macio_heathrow) {
+		struct macio_chip temp = macio_chips[0];
+		macio_chips[0] = macio_chips[1];
+		macio_chips[1] = temp;
+	}
+	if (macio_chips[0].type == macio_ohareII
+	    && macio_chips[1].type == macio_ohare) {
+		struct macio_chip temp = macio_chips[0];
+		macio_chips[0] = macio_chips[1];
+		macio_chips[1] = temp;
+	}
+
+	return (macio_chips[0].of_node == NULL) ? -ENODEV : 0;
+}
+
+static void __init
+set_initial_features(void)
+{
+	struct device_node* np;
+	
+	/* That hack appears to be necessary for some StarMax motherboards
+	 * but I'm not too sure it was audited for side-effects on other
+	 * ohare based machines...
+	 * Since I still have difficulties figuring the right way to
+	 * differenciate them all and since that hack was there for a long
+	 * time, I'll keep it around
+	 */
+	if (macio_chips[0].type == macio_ohare && !find_devices("via-pmu")) {
+		struct macio_chip* macio = &macio_chips[0];
+		MACIO_OUT32(OHARE_FCR, STARMAX_FEATURES);
+	} else if (macio_chips[0].type == macio_ohare) {
+		struct macio_chip* macio = &macio_chips[0];
+		MACIO_BIS(OHARE_FCR, OH_IOBUS_ENABLE);
+	} else if (macio_chips[1].type == macio_ohare) {
+		struct macio_chip* macio = &macio_chips[1];
+		MACIO_BIS(OHARE_FCR, OH_IOBUS_ENABLE);
+	}
+
+	if (macio_chips[0].type == macio_keylargo ||
+	    macio_chips[0].type == macio_pangea) {
+		/* Enable GMAC for now for PCI probing. It will be disabled
+		 * later on after PCI probe
+		 */
+		np = find_devices("ethernet");
+		while(np) {
+			if (np && device_is_compatible(np, "gmac"))
+				core99_gmac_enable(np, 0, 1);
+			np = np->next;
+		}
+
+		/* Enable FW before PCI probe. Will be disabled later on
+		 */
+		np = find_devices("firewire");
+		while(np) {
+			if (device_is_compatible(np, "pci106b,18") || 
+	     		    device_is_compatible(np, "pci106b,30")) {
+				macio_chips[0].flags |= MACIO_FLAG_FW_SUPPORTED;
+				core99_firewire_enable(np, 0, 1);
+			}
+			np = np->next;
+		}
+		
+		/* Switch airport off */
+		np = find_devices("radio");
+		while(np) {
+			if (np && np->parent == macio_chips[0].of_node) {
+				macio_chips[0].flags |= MACIO_FLAG_AIRPORT_ON;
+				core99_airport_enable(np, 0, 0);
+			}
+			np = np->next;
+		}
+	}
+
+	/* On all machines, switch sound off */
+	if (macio_chips[0].of_node)
+		pmac_do_feature_call(PMAC_FTR_SOUND_CHIP_ENABLE,
+			macio_chips[0].of_node, 0, 0);
+			
+	/* Let hardware settle down */
+	mdelay(1);
+}
+
+void __init
+pmac_feature_init(void)
+{
+	/* Detect the UniNorth memory controller */
+	probe_uninorth();
+
+	/* Probe mac-io controllers */
+	if (probe_macios()) {
+		printk(KERN_WARNING "No mac-io chip found\n");
+		return;
+	}
+
+	/* Probe machine type */
+	if (probe_motherboard())
+		printk(KERN_WARNING "Unknown PowerMac !\n");
+
+	/* Set some initial features (turn off some chips that will
+	 * be later turned on)
+	 */
+	set_initial_features();
+}
+
+void __init
+pmac_feature_late_init(void)
+{
+	struct device_node* np;
+	
+	/* Request some resources late */
+	if (uninorth_node)
+		request_OF_resource(uninorth_node, 0, NULL);
+	np = find_devices("hammerhead");
+	if (np)
+		request_OF_resource(np, 0, NULL);
+	np = find_devices("interrupt-controller");
+	if (np)
+		request_OF_resource(np, 0, NULL);
+}
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/kernel/pmac_nvram.c linux/arch/ppc/kernel/pmac_nvram.c
--- linux.orig/arch/ppc/kernel/pmac_nvram.c	Mon Jan  7 23:41:08 2002
+++ linux/arch/ppc/kernel/pmac_nvram.c	Wed Dec 26 16:28:34 2001
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.pmac_nvram.c 1.15 09/08/01 15:47:42 paulus
+ * BK Id: SCCS/s.pmac_nvram.c 1.17 12/01/01 20:09:06 benh
  */
 /*
  * Miscellaneous procedures for dealing with the PowerMac hardware.
@@ -326,7 +326,7 @@
 			break;
 		while (!req.complete)
 			pmu_poll();
-		return req.reply[1];
+		return req.reply[0];
 	}
 #endif
 	case 1:
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/kernel/pmac_pci.c linux/arch/ppc/kernel/pmac_pci.c
--- linux.orig/arch/ppc/kernel/pmac_pci.c	Mon Jan  7 23:41:08 2002
+++ linux/arch/ppc/kernel/pmac_pci.c	Wed Dec 26 16:28:34 2001
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.pmac_pci.c 1.27 09/08/01 15:47:42 paulus
+ * BK Id: SCCS/s.pmac_pci.c 1.29 12/01/01 20:09:06 benh
  */
 /*
  * Support for PCI bridges found on Power Macintoshes.
@@ -27,7 +27,7 @@
 #include <asm/prom.h>
 #include <asm/pci-bridge.h>
 #include <asm/machdep.h>
-#include <asm/feature.h>
+#include <asm/pmac_feature.h>
 
 #include "pci.h"
 
@@ -545,13 +545,13 @@
 	if (node && !strcmp(node->name, "firewire") && 
 	    (device_is_compatible(node, "pci106b,18") || 
 	     device_is_compatible(node, "pci106b,30"))) {
-		feature_set_firewire_cable_power(node, 1);
-		feature_set_firewire_power(node, 1);
+		pmac_call_feature(PMAC_FTR_1394_CABLE_POWER, node, 0, 1);
+		pmac_call_feature(PMAC_FTR_1394_ENABLE, node, 0, 1);
 		updatecfg = 1;
 	}
 	if (node && !strcmp(node->name, "ethernet") && 
 	    device_is_compatible(node, "gmac")) {
-		feature_set_gmac_power(node, 1);
+		pmac_call_feature(PMAC_FTR_GMAC_ENABLE, node, 0, 1);
 		updatecfg = 1;
 	}
 
@@ -610,8 +610,8 @@
 		if (nd->parent && (device_is_compatible(nd, "pci106b,18") ||
 					device_is_compatible(nd, "pci106b,30"))
 		    && device_is_compatible(nd->parent, "uni-north")) {
-			feature_set_firewire_power(nd, 0);
-			feature_set_firewire_cable_power(nd, 0);
+			pmac_call_feature(PMAC_FTR_1394_ENABLE, nd, 0, 0);
+			pmac_call_feature(PMAC_FTR_1394_CABLE_POWER, nd, 0, 0);
 		}
 		nd = nd->next;
 	}
@@ -619,7 +619,7 @@
 	while (nd) {
 		if (nd->parent && device_is_compatible(nd, "gmac")
 		    && device_is_compatible(nd->parent, "uni-north"))
-			feature_set_gmac_power(nd, 0);
+			pmac_call_feature(PMAC_FTR_GMAC_ENABLE, nd, 0, 0);
 		nd = nd->next;
 	}
 }
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/kernel/pmac_pic.c linux/arch/ppc/kernel/pmac_pic.c
--- linux.orig/arch/ppc/kernel/pmac_pic.c	Mon Jan  7 23:41:08 2002
+++ linux/arch/ppc/kernel/pmac_pic.c	Wed Dec 26 16:28:34 2001
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.pmac_pic.c 1.20 09/08/01 15:47:42 paulus
+ * BK Id: SCCS/s.pmac_pic.c 1.24 12/19/01 10:53:01 paulus
  */
 #include <linux/config.h>
 #include <linux/stddef.h>
@@ -18,44 +18,52 @@
 #include "pmac_pic.h"
 #include "open_pic.h"
 
-/* pmac */struct pmac_irq_hw {
-        unsigned int    flag;
+struct pmac_irq_hw {
+        unsigned int    event;
         unsigned int    enable;
         unsigned int    ack;
         unsigned int    level;
 };
 
-/* XXX these addresses should be obtained from the device tree */
-static volatile struct pmac_irq_hw *pmac_irq_hw[4] = {
+/* Default addresses */
+static volatile struct pmac_irq_hw *pmac_irq_hw[4] __pmacdata = {
         (struct pmac_irq_hw *) 0xf3000020,
         (struct pmac_irq_hw *) 0xf3000010,
         (struct pmac_irq_hw *) 0xf4000020,
         (struct pmac_irq_hw *) 0xf4000010,
 };
 
-static int max_irqs;
-static int max_real_irqs;
+#define GC_LEVEL_MASK		0x3ff00000
+#define OHARE_LEVEL_MASK	0x1ff00000
+#define HEATHROW_LEVEL_MASK	0x1ff00000
+
+static int max_irqs __pmacdata;
+static int max_real_irqs __pmacdata;
+static u32 level_mask[4] __pmacdata;
 
-static spinlock_t pmac_pic_lock = SPIN_LOCK_UNLOCKED;
+static spinlock_t pmac_pic_lock __pmacdata = SPIN_LOCK_UNLOCKED;
 
 
 #define GATWICK_IRQ_POOL_SIZE        10
-static struct interrupt_info gatwick_int_pool[GATWICK_IRQ_POOL_SIZE];
+static struct interrupt_info gatwick_int_pool[GATWICK_IRQ_POOL_SIZE] __pmacdata;
 
 /*
  * Mark an irq as "lost".  This is only used on the pmac
  * since it can lose interrupts (see pmac_set_irq_mask).
  * -- Cort
  */
-void __pmac __set_lost(unsigned long irq_nr)
+void __pmac
+__set_lost(unsigned long irq_nr, int nokick)
 {
 	if (!test_and_set_bit(irq_nr, ppc_lost_interrupts)) {
 		atomic_inc(&ppc_n_lost_interrupts);
-		set_dec(1);
+		if (!nokick)
+			set_dec(1);
 	}
 }
 
-static void __pmac pmac_mask_and_ack_irq(unsigned int irq_nr)
+static void __pmac
+pmac_mask_and_ack_irq(unsigned int irq_nr)
 {
         unsigned long bit = 1UL << (irq_nr & 0x1f);
         int i = irq_nr >> 5;
@@ -68,18 +76,18 @@
         if (test_and_clear_bit(irq_nr, ppc_lost_interrupts))
                 atomic_dec(&ppc_n_lost_interrupts);
 	spin_lock_irqsave(&pmac_pic_lock, flags);
-        out_le32(&pmac_irq_hw[i]->ack, bit);
         out_le32(&pmac_irq_hw[i]->enable, ppc_cached_irq_mask[i]);
         out_le32(&pmac_irq_hw[i]->ack, bit);
         do {
                 /* make sure ack gets to controller before we enable
                    interrupts */
                 mb();
-        } while(in_le32(&pmac_irq_hw[i]->flag) & bit);
+        } while((in_le32(&pmac_irq_hw[i]->enable) & bit)
+                != (ppc_cached_irq_mask[i] & bit));
 	spin_unlock_irqrestore(&pmac_pic_lock, flags);
 }
 
-static void __pmac pmac_set_irq_mask(unsigned int irq_nr)
+static void __pmac pmac_set_irq_mask(unsigned int irq_nr, int nokicklost)
 {
         unsigned long bit = 1UL << (irq_nr & 0x1f);
         int i = irq_nr >> 5;
@@ -104,31 +112,29 @@
          * when the device interrupt is already on *doesn't* set
          * the bit in the flag register or request another interrupt.
          */
-        if ((bit & ppc_cached_irq_mask[i])
-            && (ld_le32(&pmac_irq_hw[i]->level) & bit)
-            && !(ld_le32(&pmac_irq_hw[i]->flag) & bit))
-		__set_lost((ulong)irq_nr);
+        if (bit & ppc_cached_irq_mask[i] & in_le32(&pmac_irq_hw[i]->level))
+		__set_lost((ulong)irq_nr, nokicklost);
 	spin_unlock_irqrestore(&pmac_pic_lock, flags);
 }
 
 static void __pmac pmac_mask_irq(unsigned int irq_nr)
 {
         clear_bit(irq_nr, ppc_cached_irq_mask);
-        pmac_set_irq_mask(irq_nr);
+        pmac_set_irq_mask(irq_nr, 0);
         mb();
 }
 
 static void __pmac pmac_unmask_irq(unsigned int irq_nr)
 {
         set_bit(irq_nr, ppc_cached_irq_mask);
-        pmac_set_irq_mask(irq_nr);
+        pmac_set_irq_mask(irq_nr, 0);
 }
 
 static void __pmac pmac_end_irq(unsigned int irq_nr)
 {
 	if (!(irq_desc[irq_nr].status & (IRQ_DISABLED|IRQ_INPROGRESS))) {
         	set_bit(irq_nr, ppc_cached_irq_mask);
-	        pmac_set_irq_mask(irq_nr);
+	        pmac_set_irq_mask(irq_nr, 1);
 	}
 }
 
@@ -161,8 +167,10 @@
 	
 	for (irq = max_irqs; (irq -= 32) >= max_real_irqs; ) {
 		int i = irq >> 5;
-		bits = ld_le32(&pmac_irq_hw[i]->flag)
-			| ppc_lost_interrupts[i];
+		bits = in_le32(&pmac_irq_hw[i]->event) | ppc_lost_interrupts[i];
+		/* We must read level interrupts from the level register */
+		bits |= (in_le32(&pmac_irq_hw[i]->level) & level_mask[i]);
+		bits &= ppc_cached_irq_mask[i];
 		if (bits == 0)
 			continue;
 		irq += __ilog2(bits);
@@ -195,8 +203,10 @@
 #endif /* CONFIG_SMP */
 	for (irq = max_real_irqs; (irq -= 32) >= 0; ) {
 		int i = irq >> 5;
-		bits = ld_le32(&pmac_irq_hw[i]->flag)
-			| ppc_lost_interrupts[i];
+		bits = in_le32(&pmac_irq_hw[i]->event) | ppc_lost_interrupts[i];
+		/* We must read level interrupts from the level register */
+		bits |= (in_le32(&pmac_irq_hw[i]->level) & level_mask[i]);
+		bits &= ppc_cached_irq_mask[i];
 		if (bits == 0)
 			continue;
 		irq += __ilog2(bits);
@@ -374,6 +384,24 @@
 		irqctrler = NULL;
 	}
 
+	/* Get the level/edge settings, assume if it's not
+	 * a Grand Central nor an OHare, then it's an Heathrow
+	 * (or Paddington).
+	 */
+	if (find_devices("gc"))
+		level_mask[0] = GC_LEVEL_MASK;
+	else if (find_devices("ohare")) {
+		level_mask[0] = OHARE_LEVEL_MASK;
+		/* We might have a second cascaded ohare */
+		level_mask[1] = OHARE_LEVEL_MASK;
+	} else {
+		level_mask[0] = HEATHROW_LEVEL_MASK;
+		level_mask[1] = 0;
+		/* We might have a second cascaded heathrow */
+		level_mask[2] = HEATHROW_LEVEL_MASK;
+		level_mask[3] = 0;
+	}
+
 	/*
 	 * G3 powermacs and 1999 G3 PowerBooks have 64 interrupts,
 	 * 1998 G3 Series PowerBooks have 128, 
@@ -433,6 +461,10 @@
 	/* disable all interrupts in all controllers */
 	for (i = 0; i * 32 < max_irqs; ++i)
 		out_le32(&pmac_irq_hw[i]->enable, 0);
+	/* mark level interrupts */
+	for (i = 0; i < max_irqs; i++)
+		if (level_mask[i >> 5] & (1UL << (i & 0x1f)))
+			irq_desc[i].status = IRQ_LEVEL;
 	
 	/* get interrupt line of secondary interrupt controller */
 	if (irq_cascade >= 0) {
@@ -474,7 +506,7 @@
 	out_le32(&pmac_irq_hw[0]->enable, ppc_cached_irq_mask[0]);
 	if (max_real_irqs > 32)
 		out_le32(&pmac_irq_hw[1]->enable, ppc_cached_irq_mask[1]);
-	(void)in_le32(&pmac_irq_hw[0]->flag);
+	(void)in_le32(&pmac_irq_hw[0]->event);
 	/* make sure mask gets to controller before we return to caller */
 	mb();
         (void)in_le32(&pmac_irq_hw[0]->enable);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/kernel/pmac_setup.c linux/arch/ppc/kernel/pmac_setup.c
--- linux.orig/arch/ppc/kernel/pmac_setup.c	Mon Jan  7 23:41:08 2002
+++ linux/arch/ppc/kernel/pmac_setup.c	Wed Dec 26 16:28:34 2001
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.pmac_setup.c 1.43 11/13/01 21:26:07 paulus
+ * BK Id: SCCS/s.pmac_setup.c 1.45 12/01/01 20:09:06 benh
  */
 /*
  *  linux/arch/ppc/kernel/setup.c
@@ -61,7 +61,6 @@
 #include <asm/pci-bridge.h>
 #include <asm/ohare.h>
 #include <asm/mediabay.h>
-#include <asm/feature.h>
 #include <asm/machdep.h>
 #include <asm/keyboard.h>
 #include <asm/dma.h>
@@ -69,6 +68,7 @@
 #include <asm/cputable.h>
 #include <asm/btext.h>
 
+#include <asm/pmac_feature.h>
 #include <asm/time.h>
 #include "local_irq.h"
 #include "pmac_pic.h"
@@ -83,6 +83,10 @@
 extern void pmac_calibrate_decr(void);
 extern void pmac_pcibios_fixup(void);
 extern void pmac_find_bridges(void);
+extern int pmac_ide_check_base(ide_ioreg_t base);
+extern ide_ioreg_t pmac_ide_get_base(int index);
+extern void pmac_ide_init_hwif_ports(hw_regs_t *hw,
+	ide_ioreg_t data_port, ide_ioreg_t ctrl_port, int *irq);
 
 extern int mackbd_setkeycode(unsigned int scancode, unsigned int keycode);
 extern int mackbd_getkeycode(unsigned int scancode);
@@ -436,7 +440,8 @@
 #endif
 #ifdef CONFIG_PMAC_PBOOK
 	media_bay_init();
-#endif	
+#endif
+	pmac_feature_late_init();
 }
 
 #ifdef CONFIG_SCSI
@@ -604,14 +609,11 @@
 static int __pmac
 pmac_ide_check_region(ide_ioreg_t from, unsigned int extent)
 {
-	/*
-	 * We only do the check_region if `from' looks like a genuine
-	 * I/O port number.  If it actually refers to a memory-mapped
-	 * register, it should be OK.
-	 */
-	if (from < ~_IO_BASE)
-		return check_region(from, extent);
-	return 0;
+#ifdef CONFIG_BLK_DEV_IDE_PMAC
+	if (pmac_ide_check_base(from) >= 0)
+		return 0;
+#endif
+	return check_region(from, extent);
 }
 
 static void __pmac
@@ -619,24 +621,31 @@
 			unsigned int extent,
 			const char *name)
 {
-	if (from < ~_IO_BASE)
-		request_region(from, extent, name);
+#ifdef CONFIG_BLK_DEV_IDE_PMAC
+	if (pmac_ide_check_base(from) >= 0)
+		return;
+#endif
+	request_region(from, extent, name);
 }
 
 static void __pmac
 pmac_ide_release_region(ide_ioreg_t from,
 			unsigned int extent)
 {
-	if (from < ~_IO_BASE)
-		release_region(from, extent);
+#ifdef CONFIG_BLK_DEV_IDE_PMAC
+	if (pmac_ide_check_base(from) >= 0)
+		return;
+#endif
+	release_region(from, extent);
 }
 
+#ifndef CONFIG_BLK_DEV_IDE_PMAC
 /*
  * This is only used if we have a PCI IDE controller, not
  * for the IDE controller in the ohare/paddington/heathrow/keylargo.
  */
 static void __pmac
-pmac_ide_init_hwif_ports(hw_regs_t *hw, ide_ioreg_t data_port,
+pmac_ide_pci_init_hwif_ports(hw_regs_t *hw, ide_ioreg_t data_port,
 		ide_ioreg_t ctrl_port, int *irq)
 {
 	ide_ioreg_t reg = data_port;
@@ -648,7 +657,8 @@
 	}
 	hw->io_ports[IDE_CONTROL_OFFSET] = ctrl_port;
 }
-#endif
+#endif /* CONFIG_BLK_DEV_IDE_PMAC */
+#endif /* defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) */
 
 /*
  * Read in a property describing some pieces of memory.
@@ -806,14 +816,21 @@
 
 	ppc_md.find_end_of_memory = pmac_find_end_of_memory;
 
+	ppc_md.feature_call   = pmac_do_feature_call;
+
 	select_adb_keyboard();
 
-#if defined(CONFIG_BLK_DEV_IDE) && defined(CONFIG_BLK_DEV_IDE_PMAC)
+#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE)
         ppc_ide_md.ide_check_region	= pmac_ide_check_region;
         ppc_ide_md.ide_request_region	= pmac_ide_request_region;
         ppc_ide_md.ide_release_region	= pmac_ide_release_region;
+#ifdef CONFIG_BLK_DEV_IDE_PMAC
         ppc_ide_md.ide_init_hwif	= pmac_ide_init_hwif_ports;
-#endif /* CONFIG_BLK_DEV_IDE && CONFIG_BLK_DEV_IDE_PMAC */
+        ppc_ide_md.default_io_base	= pmac_ide_get_base;
+#else /* CONFIG_BLK_DEV_IDE_PMAC */
+        ppc_ide_md.ide_init_hwif	= pmac_ide_pci_init_hwif_ports;
+#endif /* CONFIG_BLK_DEV_IDE_PMAC */
+#endif /* defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) */
 
 #ifdef CONFIG_BOOTX_TEXT
 	ppc_md.progress = pmac_progress;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/kernel/pmac_smp.c linux/arch/ppc/kernel/pmac_smp.c
--- linux.orig/arch/ppc/kernel/pmac_smp.c	Mon Jan  7 23:41:08 2002
+++ linux/arch/ppc/kernel/pmac_smp.c	Wed Dec 26 16:28:34 2001
@@ -43,7 +43,8 @@
 #include <asm/prom.h>
 #include <asm/smp.h>
 #include <asm/residual.h>
-#include <asm/feature.h>
+#include <asm/machdep.h>
+#include <asm/pmac_feature.h>
 #include <asm/time.h>
 #include <asm/gemini.h>
 
@@ -442,7 +443,7 @@
 	flush_icache_range((unsigned long) vector, (unsigned long) vector + 4);
 	
 	/* Put some life in our friend */
-	feature_core99_kick_cpu(nr);
+	pmac_call_feature(PMAC_FTR_RESET_CPU, NULL, nr, 0);
 	
 	/* FIXME: We wait a bit for the CPU to take the exception, I should
 	 * instead wait for the entry code to set something for me. Well,
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/kernel/pmac_time.c linux/arch/ppc/kernel/pmac_time.c
--- linux.orig/arch/ppc/kernel/pmac_time.c	Mon Jan  7 23:41:08 2002
+++ linux/arch/ppc/kernel/pmac_time.c	Wed Dec 26 16:28:34 2001
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.pmac_time.c 1.16 09/08/01 15:47:42 paulus
+ * BK Id: SCCS/s.pmac_time.c 1.19 12/04/01 01:24:51 benh
  */
 /*
  * Support for periodic interrupts (100 per second) and for getting
@@ -110,11 +110,11 @@
 			return 0;
 		while (!req.complete)
 			pmu_poll();
-		if (req.reply_len != 5)
+		if (req.reply_len != 4)
 			printk(KERN_ERR "pmac_get_rtc_time: got %d byte reply\n",
 			       req.reply_len);
-		now = (req.reply[1] << 24) + (req.reply[2] << 16)
-			+ (req.reply[3] << 8) + req.reply[4];
+		now = (req.reply[0] << 24) + (req.reply[1] << 16)
+			+ (req.reply[2] << 8) + req.reply[3];
 		return now - RTC_OFFSET;
 #endif /* CONFIG_ADB_PMU */
 	default: ;
@@ -228,10 +228,6 @@
 	case PBOOK_WAKE:
 		write_lock_irqsave(&xtime_lock, flags);
 		xtime.tv_sec = pmac_get_rtc_time() + time_diff;
-		set_dec(tb_ticks_per_jiffy);
-		/* No currently-supported powerbook has a 601,
-		   so use get_tbl, not native  */
-		last_jiffy_stamp(0) = tb_last_stamp = get_tbl();
 		xtime.tv_usec = 0;
 		last_rtc_update = xtime.tv_sec;
 		write_unlock_irqrestore(&xtime_lock, flags);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/kernel/ppc8xx_pic.c linux/arch/ppc/kernel/ppc8xx_pic.c
--- linux.orig/arch/ppc/kernel/ppc8xx_pic.c	Mon Jan  7 23:41:08 2002
+++ linux/arch/ppc/kernel/ppc8xx_pic.c	Wed Dec 26 16:28:34 2001
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.ppc8xx_pic.c 1.10 05/17/01 18:14:21 cort
+ * BK Id: SCCS/s.ppc8xx_pic.c 1.13 12/01/01 17:19:48 trini
  */
 #include <linux/config.h>
 #include <linux/stddef.h>
@@ -44,6 +44,21 @@
 						ppc_cached_irq_mask[word];
 }
 
+static void m8xx_end_irq(unsigned int irq_nr)
+{
+	if (!(irq_desc[irq_nr].status & (IRQ_DISABLED|IRQ_INPROGRESS))) {
+		int bit, word;
+
+		bit = irq_nr & 0x1f;
+		word = irq_nr >> 5;
+
+		ppc_cached_irq_mask[word] |= (1 << (31-bit));
+		((immap_t *)IMAP_ADDR)->im_siu_conf.sc_simask = 
+			ppc_cached_irq_mask[word];
+	}
+}
+
+
 static void m8xx_mask_and_ack(unsigned int irq_nr)
 {
 	int	bit, word;
@@ -64,6 +79,7 @@
 	m8xx_unmask_irq,
 	m8xx_mask_irq,
 	m8xx_mask_and_ack,
+	m8xx_end_irq,
 	0
 };
 
@@ -97,19 +113,26 @@
 #endif
 
 
+/*
+ * We either return a valid interrupt or -1 if there is nothing pending
+ */
 int
 m8xx_get_irq(struct pt_regs *regs)
 {
 	int irq;
-        unsigned long bits = 0;
 
-        /* For MPC8xx, read the SIVEC register and shift the bits down
-         * to get the irq number.         */
-        bits = ((immap_t *)IMAP_ADDR)->im_siu_conf.sc_sivec;
-        irq = bits >> 26;
-#if 0
-        irq += ppc8xx_pic.irq_offset;
-#endif
+	/* For MPC8xx, read the SIVEC register and shift the bits down
+	 * to get the irq number.
+	 */
+	irq = ((immap_t *)IMAP_ADDR)->im_siu_conf.sc_sivec >> 26;
+
+	/*
+	 * When we read the sivec without an interrupt to process, we will 
+	 * get back SIU_LEVEL7.  In this case, return -1
+	 */
+	if (irq == SIU_LEVEL7)
+		return -1;
+
 	return irq;
 }
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/kernel/ppc_ksyms.c linux/arch/ppc/kernel/ppc_ksyms.c
--- linux.orig/arch/ppc/kernel/ppc_ksyms.c	Mon Jan  7 23:41:08 2002
+++ linux/arch/ppc/kernel/ppc_ksyms.c	Wed Dec 26 16:28:34 2001
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.ppc_ksyms.c 1.59 11/04/01 22:58:20 paulus
+ * BK Id: SCCS/s.ppc_ksyms.c 1.61 12/01/01 20:09:06 benh
  */
 #include <linux/config.h>
 #include <linux/module.h>
@@ -36,7 +36,7 @@
 #include <asm/system.h>
 #include <asm/pci-bridge.h>
 #include <asm/irq.h>
-#include <asm/feature.h>
+#include <asm/pmac_feature.h>
 #include <asm/dma.h>
 #include <asm/machdep.h>
 #include <asm/hw_irq.h>
@@ -247,6 +247,8 @@
 EXPORT_SYMBOL(machine_is_compatible);
 EXPORT_SYMBOL(find_all_nodes);
 EXPORT_SYMBOL(get_property);
+EXPORT_SYMBOL(request_OF_resource);
+EXPORT_SYMBOL(release_OF_resource);
 EXPORT_SYMBOL(pci_bus_io_base);
 EXPORT_SYMBOL(pci_bus_io_base_phys);
 EXPORT_SYMBOL(pci_bus_mem_base_phys);
@@ -257,16 +259,6 @@
 EXPORT_SYMBOL(pci_phys_to_bus);
 EXPORT_SYMBOL(pci_bus_to_phys);
 EXPORT_SYMBOL(pmac_newworld);
-EXPORT_SYMBOL(feature_set);
-EXPORT_SYMBOL(feature_clear);
-EXPORT_SYMBOL(feature_test);
-EXPORT_SYMBOL(feature_set_gmac_power);
-EXPORT_SYMBOL(feature_gmac_phy_reset);
-EXPORT_SYMBOL(feature_set_usb_power);
-EXPORT_SYMBOL(feature_set_firewire_power);
-EXPORT_SYMBOL(feature_set_firewire_cable_power);
-EXPORT_SYMBOL(feature_set_modem_power);
-EXPORT_SYMBOL(feature_set_airport_power);
 #endif /* defined(CONFIG_ALL_PPC) */
 #if defined(CONFIG_BOOTX_TEXT)
 EXPORT_SYMBOL(btext_update_display);
@@ -319,7 +311,9 @@
 EXPORT_SYMBOL(get_wchan);
 EXPORT_SYMBOL(console_drivers);
 #ifdef CONFIG_XMON
+extern void xmon_printf(char *fmt, ...);
 EXPORT_SYMBOL(xmon);
+EXPORT_SYMBOL(xmon_printf);
 #endif
 EXPORT_SYMBOL(__up);
 EXPORT_SYMBOL(__down);
@@ -361,3 +355,8 @@
 extern long *ret_from_intercept;
 EXPORT_SYMBOL(ret_from_intercept);
 EXPORT_SYMBOL(cur_cpu_spec);
+#if defined(CONFIG_ALL_PPC)
+extern unsigned long agp_special_page;
+EXPORT_SYMBOL_NOVERS(agp_special_page);
+#endif /* defined(CONFIG_ALL_PPC) */
+
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/kernel/prep_pci.c linux/arch/ppc/kernel/prep_pci.c
--- linux.orig/arch/ppc/kernel/prep_pci.c	Mon Jan  7 23:41:08 2002
+++ linux/arch/ppc/kernel/prep_pci.c	Wed Dec 26 16:28:34 2001
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.prep_pci.c 1.31 10/05/01 17:48:18 trini
+ * BK Id: SCCS/s.prep_pci.c 1.33 12/20/01 15:36:12 trini
  */
 /*
  * PReP pci functions.
@@ -1190,6 +1190,27 @@
 }
 
 static void __init
+prep_pcibios_after_init(void)
+{
+	struct pci_dev *dev;
+	
+	/* If there is a WD 90C, reset the IO BAR to 0x0 (it started that 
+	 * way, but the PCI layer relocated it because it thought 0x0 was
+	 * invalid for a BAR).
+	 * If you don't do this, the card's VGA base will be <IO BAR>+0xc0000
+	 * instead of 0xc0000. vgacon.c (for example) is completely unaware of
+	 * this little quirk.
+	 */
+	dev = pci_find_device(PCI_VENDOR_ID_WD, PCI_DEVICE_ID_WD_90C, NULL);
+	if (dev) {
+		dev->resource[1].end -= dev->resource[1].start;
+		dev->resource[1].start = 0;
+		/* tell the hardware */
+		pci_write_config_dword(dev, PCI_BASE_ADDRESS_1, 0x0);
+	}
+}
+
+static void __init
 prep_init_resource(struct resource *res, unsigned long start,
 		   unsigned long end, int flags)
 {
@@ -1246,4 +1267,5 @@
 	}
 
 	ppc_md.pcibios_fixup = prep_pcibios_fixup;
+	ppc_md.pcibios_after_init = prep_pcibios_after_init;
 }
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/kernel/prep_setup.c linux/arch/ppc/kernel/prep_setup.c
--- linux.orig/arch/ppc/kernel/prep_setup.c	Mon Jan  7 23:41:08 2002
+++ linux/arch/ppc/kernel/prep_setup.c	Wed Dec 26 16:28:34 2001
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.prep_setup.c 1.44 11/13/01 21:26:07 paulus
+ * BK Id: SCCS/s.prep_setup.c 1.47 12/19/01 09:45:54 trini
  */
 /*
  *  linux/arch/ppc/kernel/setup.c
@@ -189,7 +189,7 @@
 
 no_l2:
 #ifdef CONFIG_PREP_RESIDUAL
-	if (res->ResidualLength == 0) {
+	if (res->ResidualLength != 0) {
 		/* print info about SIMMs */
 		seq_printf(m, "simms\t\t: ");
 		for (i = 0; (res->ActualNumMemories) && (i < MAX_MEMS); i++) {
@@ -645,7 +645,7 @@
 static int __prep
 prep_get_irq(struct pt_regs *regs)
 {
-	return i8259_irq(smp_processor_id());
+	return i8259_irq();
 }		
 
 static void __init
@@ -657,7 +657,7 @@
 		openpic_init(1, NUM_8259_INTERRUPTS, 0, -1);
 	for ( i = 0 ; i < NUM_8259_INTERRUPTS ; i++ )
 		irq_desc[i].handler = &i8259_pic;
-	i8259_init();	
+	i8259_init(0xbffffff0); /* PCI interrupt ack address for MPC105 and 106 */
 }
 
 #if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE)
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/kernel/prom.c linux/arch/ppc/kernel/prom.c
--- linux.orig/arch/ppc/kernel/prom.c	Mon Jan  7 23:41:08 2002
+++ linux/arch/ppc/kernel/prom.c	Wed Dec 26 16:28:34 2001
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.prom.c 1.42 09/08/01 15:47:42 paulus
+ * BK Id: SCCS/s.prom.c 1.48 12/19/01 10:50:58 paulus
  */
 /*
  * Procedures for interfacing to the Open Firmware PROM on
@@ -19,6 +19,9 @@
 #include <linux/version.h>
 #include <linux/threads.h>
 #include <linux/spinlock.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
 
 #include <asm/sections.h>
 #include <asm/prom.h>
@@ -34,6 +37,7 @@
 #include <asm/bitops.h>
 #include <asm/bootinfo.h>
 #include <asm/btext.h>
+#include <asm/pci-bridge.h>
 #include "open_pic.h"
 
 #ifdef CONFIG_FB
@@ -371,8 +375,8 @@
 		btext_drawstring(RELOC(" !!! WARNING - Incompatible version of BootX !!!\n\n\n"));
 		btext_flushscreen();
 	}
-#endif	/* CONFIG_BOOTX_TEXT */	
-		
+#endif	/* CONFIG_BOOTX_TEXT */
+
 	/* New BootX enters kernel with MMU off, i/os are not allowed
 	   here. This hack will have been done by the boostrap anyway.
 	*/
@@ -388,7 +392,7 @@
 			out_le32((unsigned *)0x80880008, 1);	/* XXX */
 		}
 	}
-		
+
 	/* Move klimit to enclose device tree, args, ramdisk, etc... */
 	if (bi->version < 5) {
 		space = bi->deviceTreeOffset + bi->deviceTreeSize;
@@ -411,7 +415,7 @@
 		     ptr < (unsigned long)bi + space; ptr += PAGE_SIZE)
 			x = *(volatile unsigned long *)ptr;
 	}
-		
+
 #ifdef CONFIG_BOOTX_TEXT
 	/*
 	 * Note that after we call prepare_disp_BAT, we can't do
@@ -620,7 +624,7 @@
 		phys = 0;
 	else {
  	    if ((int) call_prom(RELOC("getprop"), 4, 1, RELOC(prom_chosen),
-			    RELOC("mmu"), &prom_mmu, sizeof(prom_mmu)) <= 0) {	
+			    RELOC("mmu"), &prom_mmu, sizeof(prom_mmu)) <= 0) {
 		prom_print(RELOC(" no MMU found\n"));
 	    } else {
 		int nargs;
@@ -653,7 +657,8 @@
 	call_prom(RELOC("quiesce"), 0, 0);
 
 #ifdef CONFIG_BOOTX_TEXT
-	btext_prepare_BAT();
+	if (RELOC(prom_disp_node) != 0)
+		btext_prepare_BAT();
 #endif
 
 	prom_print(RELOC("returning "));
@@ -793,7 +798,7 @@
 			for (i=1; i<RELOC(prom_num_displays); i++) {
 				RELOC(prom_display_paths[i-1]) = RELOC(prom_display_paths[i]);
 				RELOC(prom_display_nodes[i-1]) = RELOC(prom_display_nodes[i]);
-			}	
+			}
 			if (--RELOC(prom_num_displays) > 0)
 				RELOC(prom_disp_node) = RELOC(prom_display_nodes[0]);
 			else
@@ -880,7 +885,7 @@
 		}
 	}
 	/* kludge for valkyrie */
-	if (strcmp(name, RELOC("valkyrie")) == 0) 
+	if (strcmp(name, RELOC("valkyrie")) == 0)
 		address += 0x1000;
 
 	btext_setup_display(width, height, depth, pitch, address);
@@ -1111,10 +1116,15 @@
 	np->name = get_property(np, "name", 0);
 	np->type = get_property(np, "device_type", 0);
 
+	if (!np->name)
+		np->name = "<NULL>";
+	if (!np->type)
+		np->type = "<NULL>";
+
 	/* get the device addresses and interrupts */
-	if (ifunc != NULL) {
+	if (ifunc != NULL)
 		mem_start = ifunc(np, mem_start, naddrc, nsizec);
-	}
+
 	if (use_of_interrupt_tree)
 		mem_start = finish_node_interrupts(np, mem_start);
 
@@ -1126,11 +1136,18 @@
 	if (ip != NULL)
 		nsizec = *ip;
 
-	/* the f50 sets the name to 'display' and 'compatible' to what we
-	 * expect for the name -- Cort
+	/* 
+	 * The F50 sets the name to 'display' and 'compatible' to what we
+	 * expect for the name. -- Cort
+	 *
+	 * But sometimes you get a 'display' name for non-OF cards, and thus
+	 * no compatible property.  And very rarely we won't have a name
+	 * property either. -- Tom
 	 */
 	if (!strcmp(np->name, "display"))
 		np->name = get_property(np, "compatible", 0);
+	if (!np->name)
+		np->name = get_property(np, "name", 0);
 
 	if (np->parent == NULL)
 		ifunc = interpret_root_props;
@@ -1145,6 +1162,8 @@
 		ifunc = interpret_macio_props;
 	else if (!strcmp(np->type, "isa"))
 		ifunc = interpret_isa_props;
+	else if (!strcmp(np->name, "uni-n"))
+		ifunc = interpret_root_props;
 	else if (!((ifunc == interpret_dbdma_props
 		    || ifunc == interpret_macio_props)
 		   && (!strcmp(np->type, "escc")
@@ -1508,7 +1527,7 @@
 		i = 0;
 		adr = (struct address_range *) mem_start;
 		while ((l -= sizeof(struct reg_property)) >= 0) {
-			adr[i].space = 0;
+			adr[i].space = 2;
 			adr[i].address = rp[i].address + base_address;
 			adr[i].size = rp[i].size;
 			++i;
@@ -1544,14 +1563,13 @@
 	struct reg_property *rp;
 	struct address_range *adr;
 	unsigned long base_address;
-	int i, l, keylargo, *ip;
+	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;
-			keylargo = device_is_compatible(db, "Keylargo");
 			break;
 		}
 	}
@@ -1561,7 +1579,7 @@
 		i = 0;
 		adr = (struct address_range *) mem_start;
 		while ((l -= sizeof(struct reg_property)) >= 0) {
-			adr[i].space = 0;
+			adr[i].space = 2;
 			adr[i].address = rp[i].address + base_address;
 			adr[i].size = rp[i].size;
 			++i;
@@ -1616,7 +1634,7 @@
 
 	if (use_of_interrupt_tree)
 		return mem_start;
- 
+
 	ip = (int *) get_property(np, "interrupts", &l);
 	if (ip != 0) {
 		np->intrs = (struct interrupt_info *) mem_start;
@@ -1645,7 +1663,7 @@
 		i = 0;
 		adr = (struct address_range *) mem_start;
 		while ((l -= rpsize) >= 0) {
-			adr[i].space = (naddrc >= 2? rp[naddrc-2]: 0);
+			adr[i].space = (naddrc >= 2? rp[naddrc-2]: 2);
 			adr[i].address = rp[naddrc - 1];
 			adr[i].size = rp[naddrc + nsizec - 1];
 			++i;
@@ -1786,7 +1804,7 @@
 machine_is_compatible(const char *compat)
 {
 	struct device_node *root;
-	
+
 	root = find_path_device("/");
 	if (root == 0)
 		return 0;
@@ -1870,10 +1888,176 @@
 {
 	struct property **next = &np->properties;
 
-	prop->next = NULL;	
+	prop->next = NULL;
 	while (*next)
 		next = &(*next)->next;
 	*next = prop;
+}
+
+/* I quickly hacked that one, check against spec ! */
+static inline unsigned long __openfirmware
+bus_space_to_resource_flags(unsigned int bus_space)
+{
+	u8 space = (bus_space >> 24) & 0xf;
+	if (space == 0)
+		space = 0x02;
+	if (space == 0x02)
+		return IORESOURCE_MEM;
+	else if (space == 0x01)
+		return IORESOURCE_IO;
+	else {
+		printk(KERN_WARNING "prom.c: bus_space_to_resource_flags(), space: %x\n",
+		    	bus_space);
+		return 0;
+	}
+}
+
+static struct resource* __openfirmware
+find_parent_pci_resource(struct pci_dev* pdev, struct address_range *range)
+{
+	unsigned long mask;
+	int i;
+	
+	/* Check this one */ 
+	mask = bus_space_to_resource_flags(range->space);
+	for (i=0; i<DEVICE_COUNT_RESOURCE; i++) {
+		if ((pdev->resource[i].flags & mask) == mask &&
+			pdev->resource[i].start <= range->address &&
+			pdev->resource[i].end > range->address) {
+				if ((range->address + range->size - 1) > pdev->resource[i].end) {
+					/* Add better message */
+					printk(KERN_WARNING "PCI/OF resource overlap !\n");
+					return NULL;
+				}
+				break;
+			}
+	}
+	if (i == DEVICE_COUNT_RESOURCE)
+		return NULL;
+	return &pdev->resource[i];
+}
+
+/*
+ * Request an OF device resource. Currently handles child of PCI devices,
+ * or other nodes attached to the root node. Ultimately, put some
+ * link to resources in the OF node.
+ * WARNING: out_resource->name should be initialized before calling this
+ * function.
+ */
+struct resource* __openfirmware
+request_OF_resource(struct device_node* node, int index, const char* name_postfix)
+{
+	struct pci_dev* pcidev;
+	u8 pci_bus, pci_devfn;
+	unsigned long iomask;
+	struct device_node* nd;
+	struct resource* parent;
+	struct resource *res = NULL;
+	int nlen, plen;
+
+	if (index >= node->n_addrs)
+		goto fail;
+
+	/* Sanity check on bus space */
+	iomask = bus_space_to_resource_flags(node->addrs[index].space);
+	if (iomask & IORESOURCE_MEM)
+		parent = &iomem_resource;
+	else if (iomask & IORESOURCE_IO)
+		parent = &ioport_resource;
+	else
+		goto fail;
+		
+	/* Find a PCI parent if any */
+	nd = node;
+	pcidev = NULL;
+	while(nd) {
+		if (!pci_device_from_OF_node(nd, &pci_bus, &pci_devfn))
+			pcidev = pci_find_slot(pci_bus, pci_devfn);
+		if (pcidev) break;
+		nd = nd->parent;
+	}
+	if (pcidev)
+		parent = find_parent_pci_resource(pcidev, &node->addrs[index]);
+	if (!parent) {
+		printk(KERN_WARNING "request_OF_resource(%s), parent not found\n",
+			node->name);
+		goto fail;
+	}
+
+	res = __request_region(parent, node->addrs[index].address, node->addrs[index].size, NULL);
+	if (!res)
+		goto fail;
+	nlen = strlen(node->name);
+	plen = name_postfix ? strlen(name_postfix) : 0;
+	res->name = (const char *)kmalloc(nlen+plen+1, GFP_KERNEL);
+	if (res->name) {
+		strcpy((char *)res->name, node->name);
+		if (plen)
+			strcpy((char *)res->name+nlen, name_postfix);
+	}
+	return res;
+fail:
+	return NULL;
+}
+
+int __openfirmware
+release_OF_resource(struct device_node* node, int index)
+{
+	struct pci_dev* pcidev;
+	u8 pci_bus, pci_devfn;
+	unsigned long iomask;
+	struct device_node* nd;
+	struct resource* parent;
+	struct resource *res = NULL;
+
+	if (index >= node->n_addrs)
+		return -EINVAL;
+
+	/* Sanity check on bus space */
+	iomask = bus_space_to_resource_flags(node->addrs[index].space);
+	if (iomask & IORESOURCE_MEM)
+		parent = &iomem_resource;
+	else if (iomask & IORESOURCE_IO)
+		parent = &ioport_resource;
+	else
+		return -EINVAL;
+		
+	/* Find a PCI parent if any */
+	nd = node;
+	pcidev = NULL;
+	while(nd) {
+		if (!pci_device_from_OF_node(nd, &pci_bus, &pci_devfn))
+			pcidev = pci_find_slot(pci_bus, pci_devfn);
+		if (pcidev) break;
+		nd = nd->parent;
+	}
+	if (pcidev)
+		parent = find_parent_pci_resource(pcidev, &node->addrs[index]);
+	if (!parent) {
+		printk(KERN_WARNING "request_OF_resource(%s), parent not found\n",
+			node->name);
+		return -ENODEV;
+	}
+
+	/* Find us in the parent */
+	res = parent->child;
+	while (res) {
+		if (res->start == node->addrs[index].address &&
+		    res->end == (res->start + node->addrs[index].size - 1))
+		    	break;
+		res = res->sibling;
+	}
+	if (!res)
+		return -ENODEV;
+
+	if (res->name) {
+		kfree(res->name);
+		res->name = NULL;
+	}
+	release_resource(res);
+	kfree(res);
+
+	return 0;
 }
 
 #if 0
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/kernel/setup.c linux/arch/ppc/kernel/setup.c
--- linux.orig/arch/ppc/kernel/setup.c	Mon Jan  7 23:41:08 2002
+++ linux/arch/ppc/kernel/setup.c	Wed Dec 26 16:28:34 2001
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.setup.c 1.65 11/18/01 20:57:25 trini
+ * BK Id: SCCS/s.setup.c 1.67 12/01/01 20:09:07 benh
  */
 /*
  * Common prep/pmac/chrp boot and setup code.
@@ -43,9 +43,9 @@
 #include <asm/bootx.h>
 #include <asm/btext.h>
 #include <asm/machdep.h>
-#include <asm/feature.h>
 #include <asm/uaccess.h>
 #include <asm/system.h>
+#include <asm/pmac_feature.h>
 
 extern void platform_init(unsigned long r3, unsigned long r4,
 		unsigned long r5, unsigned long r6, unsigned long r7);
@@ -547,8 +547,13 @@
 	loops_per_jiffy = 500000000 / HZ;
 
 #ifdef CONFIG_ALL_PPC
-	feature_init();
-#endif
+	/* This could be called "early setup arch", it must be done
+	 * now because xmon need it
+	 */
+	if (_machine == _MACH_Pmac)
+		pmac_feature_init();	/* New cool way */
+#endif /* CONFIG_ALL_PPC */
+
 #ifdef CONFIG_XMON
 	xmon_map_scc();
 	if (strstr(cmd_line, "xmon"))
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/kernel/sleep.S linux/arch/ppc/kernel/sleep.S
--- linux.orig/arch/ppc/kernel/sleep.S	Mon Jan  7 23:41:08 2002
+++ linux/arch/ppc/kernel/sleep.S	Wed Dec 26 16:28:34 2001
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.sleep.S 1.13 08/19/01 22:23:04 paulus
+ * BK Id: SCCS/s.sleep.S 1.18 12/02/01 12:38:54 benh
  */
 /*
  * This file contains sleep low-level functions for PowerBook G3.
@@ -16,6 +16,7 @@
 #include "ppc_asm.tmpl"
 #include <asm/processor.h>
 #include <asm/page.h>
+#include <asm/cputable.h>
 
 #define MAGIC	0x4c617273	/* 'Lars' */
 
@@ -37,8 +38,15 @@
 #define SL_IBAT3	0x58
 #define SL_TB		0x60
 #define SL_HID0		0x68
-#define SL_R2		0x6c
-#define SL_R12		0x70	/* r12 to r31 */
+#define SL_HID1		0x6c
+#define SL_MSSCR0	0x70
+#define SL_MSSSR0	0x74
+#define SL_ICTRL	0x78
+#define SL_LDSTCR	0x7c
+#define SL_LDSTDB	0x80
+#define SL_R2		0x84
+#define SL_CR		0x88
+#define SL_R12		0x8c	/* r12 to r31 */
 #define SL_SIZE		(SL_R12 + 80)
 
 #define tophys(rd,rs)   addis   rd,rs,-KERNELBASE@h     
@@ -56,6 +64,8 @@
 	mflr	r0
 	stw	r0,4(r1)
 	stwu	r1,-SL_SIZE(r1)
+	mfcr	r0
+	stw	r0,SL_CR(r1)
 	stw	r2,SL_R2(r1)
 	stmw	r12,SL_R12(r1)
 
@@ -121,7 +131,31 @@
 	/* Save HID0 */
 	mfspr	r4,HID0
 	stw	r4,SL_HID0(r1)
-	
+
+	/* Save 7400/7410/7450 specific registers */
+	mfspr	r3,PVR
+	srwi	r3,r3,16
+	cmpli	cr0,r3,0x8000
+	cmpli	cr1,r3,0x000c
+	cmpli	cr2,r3,0x800c
+	cror	4*cr1+eq,4*cr1+eq,4*cr2+eq
+	cror	4*cr0+eq,4*cr0+eq,4*cr1+eq
+	bne	1f
+	mfspr	r4,SPRN_MSSCR0
+	stw	r4,SL_MSSCR0(r1)
+	mfspr	r4,SPRN_MSSSR0
+	stw	r4,SL_MSSSR0(r1)
+	/* Save 7450 specific registers */
+	beq	cr1,1f
+	mfspr	r4,HID1
+	stw	r4,SL_HID1(r1)
+	mfspr	r4,SPRN_ICTRL
+	stw	r4,SL_ICTRL(r1)
+	mfspr	r4,SPRN_LDSTCR
+	stw	r4,SL_LDSTCR(r1)
+	mfspr	r4,SPRN_LDSTDB
+	stw	r4,SL_LDSTDB(r1)
+1:
 	/* The ROM can wake us up via 2 different vectors:
 	 *  - On wallstreet & lombard, we must write a magic
 	 *    value 'Lars' at address 4 and a pointer to a
@@ -257,6 +291,19 @@
 	mtspr	HID0,r3
 	sync
 	
+	/* Restore the kernel's segment registers before
+	 * we do any r1 memory access as we are not sure they
+	 * are in a sane state above the first 256Mb region
+	 */
+	li	r0,16		/* load up segment register values */
+	mtctr	r0		/* for context 0 */
+	lis	r3,0x2000	/* Ku = 1, VSID = 0 */
+	li	r4,0
+3:	mtsrin	r3,r4
+	addi	r3,r3,0x111	/* increment VSID */
+	addis	r4,r4,0x1000	/* address of next segment */
+	bdnz	3b
+	
 	/* Restore the remaining bits of the HID0 register. */
 	subi	r1,r1,SL_PC
 	lwz	r3,SL_HID0(r1)
@@ -266,17 +313,52 @@
 	sync
 	isync
 
-	/* Restore the kernel's segment registers, the
-	   BATs, and SDR1.  Then we can turn on the MMU. */
-	li	r0,16		/* load up segment register values */
-	mtctr	r0		/* for context 0 */
-	lis	r3,0x2000	/* Ku = 1, VSID = 0 */
+	/* Restore 7400/7410/7450 specific registers */
+	mfspr	r3,PVR
+	srwi	r3,r3,16
+	cmpli	cr0,r3,0x8000
+	cmpli	cr1,r3,0x000c
+	cmpli	cr2,r3,0x800c
+	cror	4*cr1+eq,4*cr1+eq,4*cr2+eq
+	cror	4*cr0+eq,4*cr0+eq,4*cr1+eq
+	bne	1f
+	lwz	r4,SL_MSSCR0(r1)
+	sync
+	mtspr	SPRN_MSSCR0,r4
+	sync
+	isync
+	lwz	r4,SL_MSSSR0(r1)
+	sync
+	mtspr	SPRN_MSSSR0,r4
+	sync
+	isync
+	bne	cr2,1f
 	li	r4,0
-3:	mtsrin	r3,r4
-	addi	r3,r3,0x111	/* increment VSID */
-	addis	r4,r4,0x1000	/* address of next segment */
-	bdnz	3b
-	
+	mtspr	SPRN_L2CR2,r4
+	/* Restore 7450 specific registers */
+	beq	cr1,1f
+	lwz	r4,SL_HID1(r1)
+	sync
+	mtspr	HID1,r4
+	isync
+	sync
+	lwz	r4,SPRN_ICTRL(r1)
+	sync
+	mtspr	SPRN_ICTRL,r4
+	isync
+	sync
+	lwz	r4,SPRN_LDSTCR(r1)
+	sync
+	mtspr	SPRN_LDSTCR,r4
+	isync
+	sync
+	lwz	r4,SL_LDSTDB(r1)
+	sync
+	mtspr	SPRN_LDSTDB,r4
+	isync
+	sync
+1:
+	/* Restore the BATs, and SDR1.  Then we can turn on the MMU. */
 	lwz	r4,SL_SDR1(r1)
 	mtsdr1	r4
 	lwz	r4,SL_SPRG0(r1)
@@ -344,6 +426,8 @@
 	mttbl	r4
 
 	/* Restore the callee-saved registers and return */
+	lwz	r0,SL_CR(r1)
+	mtcr	r0
 	lwz	r2,SL_R2(r1)
 	lmw	r12,SL_R12(r1)
 	addi	r1,r1,SL_SIZE
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/kernel/time.c linux/arch/ppc/kernel/time.c
--- linux.orig/arch/ppc/kernel/time.c	Mon Jan  7 23:41:08 2002
+++ linux/arch/ppc/kernel/time.c	Wed Dec 26 16:28:34 2001
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.time.c 1.26 10/05/01 08:29:42 trini
+ * BK Id: SCCS/s.time.c 1.29 12/11/01 11:40:45 trini
  */
 /*
  * Common time routines among all ppc machines.
@@ -356,11 +356,10 @@
        do_get_fast_time = do_gettimeofday;
 }
 
-#define TICK_SIZE tick
-#define FEBRUARY	2
-#define	STARTOFTIME	1970
-#define SECDAY		86400L
-#define SECYR		(SECDAY * 365)
+#define FEBRUARY		2
+#define	STARTOFTIME		1970
+#define SECDAY			86400L
+#define SECYR			(SECDAY * 365)
 #define	leapyear(year)		((year) % 4 == 0)
 #define	days_in_year(a) 	(leapyear(a) ? 366 : 365)
 #define	days_in_month(a) 	(month_days[(a) - 1])
@@ -369,55 +368,12 @@
 	31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
 };
 
-/*
- * This only works for the Gregorian calendar - i.e. after 1752 (in the UK)
- */
-void GregorianDay(struct rtc_time * tm)
-{
-	int leapsToDate;
-	int lastYear;
-	int day;
-	int MonthOffset[] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
-
-	lastYear=tm->tm_year-1;
-
-	/*
-	 * Number of leap corrections to apply up to end of last year
-	 */
-	leapsToDate = lastYear/4 - lastYear/100 + lastYear/400;
-
-	/*
-	 * This year is a leap year if it is divisible by 4 except when it is
-	 * divisible by 100 unless it is divisible by 400
-	 *
-	 * e.g. 1904 was a leap year, 1900 was not, 1996 is, and 2000 will be
-	 */
-	if((tm->tm_year%4==0) &&
-	   ((tm->tm_year%100!=0) || (tm->tm_year%400==0)) &&
-	   (tm->tm_mon>2))
-	{
-		/*
-		 * We are past Feb. 29 in a leap year
-		 */
-		day=1;
-	}
-	else
-	{
-		day=0;
-	}
-
-	day += lastYear*365 + leapsToDate + MonthOffset[tm->tm_mon-1] +
-		   tm->tm_mday;
-
-	tm->tm_wday=day%7;
-}
-
 void to_tm(int tim, struct rtc_time * tm)
 {
-	register int    i;
-	register long   hms, day;
+	register int i;
+	register long hms, day, gday;
 
-	day = tim / SECDAY;
+	gday = day = tim / SECDAY;
 	hms = tim % SECDAY;
 
 	/* Hours, minutes, seconds are easy */
@@ -442,9 +398,9 @@
 	tm->tm_mday = day + 1;
 
 	/*
-	 * Determine the day of week
+	 * Determine the day of week. Jan. 1, 1970 was a Thursday.
 	 */
-	GregorianDay(tm);
+	tm->tm_wday = (gday + 4) % 7;
 }
 
 /* Auxiliary function to compute scaling factors */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/kernel/xics.c linux/arch/ppc/kernel/xics.c
--- linux.orig/arch/ppc/kernel/xics.c	Mon Jan  7 23:41:08 2002
+++ linux/arch/ppc/kernel/xics.c	Wed Dec 26 16:28:34 2001
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.xics.c 1.5 05/17/01 18:14:22 cort
+ * BK Id: SCCS/s.xics.c 1.8 12/19/01 09:48:40 trini
  */
 /*
  * arch/ppc/kernel/xics.c
@@ -157,7 +157,7 @@
 	vec &= 0x00ffffff;
 	/* for sanity, this had better be < NR_IRQS - 16 */
 	if( vec == XICS_IRQ_8259_CASCADE )
-		irq = i8259_irq(cpu);
+		irq = i8259_poll();
 	else if( vec == XICS_IRQ_SPURIOUS )
 		irq = -1;
 	else
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/mm/init.c linux/arch/ppc/mm/init.c
--- linux.orig/arch/ppc/mm/init.c	Mon Jan  7 23:41:08 2002
+++ linux/arch/ppc/mm/init.c	Wed Dec 26 16:28:34 2001
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.init.c 1.36 09/22/01 14:03:09 paulus
+ * BK Id: SCCS/s.init.c 1.38 12/01/01 20:09:07 benh
  */
 /*
  *  PowerPC version 
@@ -62,6 +62,9 @@
 int boot_mapsize;
 unsigned long totalram_pages;
 unsigned long totalhigh_pages;
+#ifdef CONFIG_ALL_PPC
+unsigned long agp_special_page;
+#endif
 
 extern char _end[];
 extern char etext[], _stext[];
@@ -85,7 +88,7 @@
 char *klimit = _end;
 struct mem_pieces phys_avail;
 
-extern char *sysmap; 
+extern char *sysmap;
 extern unsigned long sysmap_size;
 
 /*
@@ -352,9 +355,10 @@
 		ppc_md.progress("MMU:exit", 0x211);
 
 #ifdef CONFIG_BOOTX_TEXT
-	/* Must be done last, or ppc_md.progress will die */
-	if (have_of)
-		map_boot_text();
+	/* By default, we are no longer mapped */
+	boot_text_mapped = 0;	
+	/* Must be done last, or ppc_md.progress will die. */
+	map_boot_text();
 #endif
 }
 
@@ -474,20 +478,22 @@
 	}
 #endif /* CONFIG_BLK_DEV_INITRD */
 
-#if defined(CONFIG_ALL_PPC)	
+#if defined(CONFIG_ALL_PPC)
 	/* mark the RTAS pages as reserved */
 	if ( rtas_data )
 		for (addr = (ulong)__va(rtas_data);
 		     addr < PAGE_ALIGN((ulong)__va(rtas_data)+rtas_size) ;
 		     addr += PAGE_SIZE)
 			SetPageReserved(virt_to_page(addr));
+	if (agp_special_page)
+		SetPageReserved(virt_to_page(agp_special_page));
 #endif /* defined(CONFIG_ALL_PPC) */
 	if ( sysmap )
 		for (addr = (unsigned long)sysmap;
 		     addr < PAGE_ALIGN((unsigned long)sysmap+sysmap_size) ;
 		     addr += PAGE_SIZE)
 			SetPageReserved(virt_to_page(addr));
-	
+
 	for (addr = PAGE_OFFSET; addr < (unsigned long)end_of_DRAM;
 	     addr += PAGE_SIZE) {
 		if (!PageReserved(virt_to_page(addr)))
@@ -526,6 +532,10 @@
 	if (sysmap)
 		printk("System.map loaded at 0x%08x for debugger, size: %ld bytes\n",
 			(unsigned int)sysmap, sysmap_size);
+#if defined(CONFIG_ALL_PPC)
+	if (agp_special_page)
+		printk(KERN_INFO "AGP special page: 0x%08lx\n", agp_special_page);
+#endif /* defined(CONFIG_ALL_PPC) */
 	mem_init_done = 1;
 }
 
@@ -572,6 +582,20 @@
 	/* remove the sysmap pages from the available memory */
 	if (sysmap)
 		mem_pieces_remove(&phys_avail, __pa(sysmap), sysmap_size, 1);
+	/* Because of some uninorth weirdness, we need a page of
+	 * memory as high as possible (it must be outside of the
+	 * bus address seen as the AGP aperture). It will be used
+	 * by the r128 DRM driver
+	 * 
+	 * FIXME: We need to make sure that page doesn't overlap any of the\
+	 * above. This could be done by improving mem_pieces_find to be able
+	 * to do a backward search from the end of the list.
+	 */
+	if (_machine == _MACH_Pmac && find_devices("uni-north-agp")) {
+		agp_special_page = (total_memory - PAGE_SIZE);
+		mem_pieces_remove(&phys_avail, agp_special_page, PAGE_SIZE, 0);	
+		agp_special_page = (unsigned long)__va(agp_special_page);
+	}
 #endif /* CONFIG_ALL_PPC */
 }
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/xmon/nonstdio.h linux/arch/ppc/xmon/nonstdio.h
--- linux.orig/arch/ppc/xmon/nonstdio.h	Mon Jan  7 23:41:08 2002
+++ linux/arch/ppc/xmon/nonstdio.h	Wed Dec 26 16:28:34 2001
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.nonstdio.h 1.5 05/17/01 18:14:23 cort
+ * BK Id: SCCS/s.nonstdio.h 1.8 12/01/01 20:09:07 benh
  */
 typedef int	FILE;
 extern FILE *xmon_stdin, *xmon_stdout;
@@ -21,5 +21,6 @@
 extern void xmon_printf(const char *, ...);
 extern void xmon_fprintf(void *, const char *, ...);
 extern void xmon_sprintf(char *, const char *, ...);
+extern void xmon_puts(char*);
 
 #define perror(s)	printf("%s: no files!\n", (s))
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/xmon/start.c linux/arch/ppc/xmon/start.c
--- linux.orig/arch/ppc/xmon/start.c	Mon Jan  7 23:41:08 2002
+++ linux/arch/ppc/xmon/start.c	Wed Dec 26 16:28:34 2001
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.start.c 1.16 08/20/01 22:17:58 paulus
+ * BK Id: SCCS/s.start.c 1.18 12/01/01 20:09:07 benh
  */
 /*
  * Copyright (C) 1996 Paul Mackerras.
@@ -13,9 +13,11 @@
 #include <linux/pmu.h>
 #include <linux/cuda.h>
 #include <linux/kernel.h>
+#include <linux/errno.h>
 #include <asm/prom.h>
 #include <asm/bootx.h>
-#include <asm/feature.h>
+#include <asm/machdep.h>
+#include <asm/pmac_feature.h>
 #include <asm/processor.h>
 #include <asm/delay.h>
 #include <asm/btext.h>
@@ -32,7 +34,7 @@
 static int use_screen;
 static int via_modem;
 static int xmon_use_sccb;
-static struct device_node *macio_node;
+static struct device_node *channel_node;
 
 #define TB_SPEED	25000000
 
@@ -99,6 +101,7 @@
 				np = np->sibling;
 			if (np != NULL) {
 				/* XXX should parse this properly */
+				channel_node = np;
 				slots = get_property(np, "slot-names", &l);
 				if (slots != NULL && l >= 10
 				    && strcmp(slots+4, "Modem") == 0)
@@ -126,10 +129,8 @@
 		RXRDY = 1;
 		
 		np = find_devices("mac-io");
-		if (np && np->n_addrs) {
-			macio_node = np;
+		if (np && np->n_addrs)
 			addr = np->addrs[0].address + 0x13020;
-		}
 		base = (volatile unsigned char *) ioremap(addr & PAGE_MASK, PAGE_SIZE);
 		sccc = base + (addr & ~PAGE_MASK);
 		sccd = sccc + 0x10;
@@ -349,12 +350,19 @@
 	{
 		int i, x;
 
-		if (macio_node != 0)
-			feature_set(macio_node, FEATURE_Serial_enable);
-		if (via_modem && macio_node != 0) {
+		if (channel_node != 0)
+			pmac_call_feature(
+				PMAC_FTR_SCC_ENABLE,
+				channel_node,
+				PMAC_SCC_ASYNC | PMAC_SCC_FLAG_XMON, 1);
+			printk(KERN_INFO "Serial port locked ON by debugger !\n");
+		if (via_modem && channel_node != 0) {
 			unsigned int t0;
 
-			feature_set(macio_node, FEATURE_Modem_power);
+			pmac_call_feature(
+				PMAC_FTR_MODEM_ENABLE,
+				channel_node, 0, 1);
+			printk(KERN_INFO "Modem powered up by debugger !\n");
 			t0 = readtb();
 			while (readtb() - t0 < 3*TB_SPEED)
 				eieio();
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/xmon/subr_prf.c linux/arch/ppc/xmon/subr_prf.c
--- linux.orig/arch/ppc/xmon/subr_prf.c	Mon Jan  7 23:41:08 2002
+++ linux/arch/ppc/xmon/subr_prf.c	Wed Dec 26 16:28:34 2001
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.subr_prf.c 1.5 05/17/01 18:14:23 cort
+ * BK Id: SCCS/s.subr_prf.c 1.8 12/01/01 20:09:07 benh
  */
 /*
  * Written by Cort Dougan to replace the version originally used
@@ -51,3 +51,8 @@
 	va_end(ap);
 }
 
+void
+xmon_puts(char *s)
+{
+	xmon_write(stdout, s, strlen(s));
+}
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/xmon/xmon.c linux/arch/ppc/xmon/xmon.c
--- linux.orig/arch/ppc/xmon/xmon.c	Mon Jan  7 23:41:08 2002
+++ linux/arch/ppc/xmon/xmon.c	Wed Dec 26 16:28:34 2001
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.xmon.c 1.16 09/22/01 15:25:10 trini
+ * BK Id: SCCS/s.xmon.c 1.18 12/01/01 20:09:07 benh
  */
 /*
  * Routines providing a simple monitor for use on the PowerMac.
@@ -90,6 +90,7 @@
 static void write_spr(int, unsigned);
 static void super_regs(void);
 static void print_sysmap(void);
+static void sysmap_lookup(void);
 static void remove_bpts(void);
 static void insert_bpts(void);
 static struct bpt *at_breakpoint(unsigned pc);
@@ -98,10 +99,7 @@
 #ifdef CONFIG_SMP
 static void cpu_cmd(void);
 #endif /* CONFIG_SMP */
-#if 0 /* Makes compile with -Wall */
-static char *pretty_print_addr(unsigned long addr);
-static char *lookup_name(unsigned long addr);
-#endif
+static int pretty_print_addr(unsigned long addr);
 static void csum(void);
 
 extern int print_insn_big_powerpc(FILE *, unsigned long, unsigned);
@@ -112,6 +110,8 @@
 
 extern void xmon_enter(void);
 extern void xmon_leave(void);
+extern char* xmon_find_symbol(unsigned long addr, unsigned long* saddr);
+extern unsigned long xmon_symbol_to_addr(char* symbol);
 
 #define GETWORD(v)	(((v)[0] << 24) + ((v)[1] << 16) + ((v)[2] << 8) + (v)[3])
 
@@ -121,6 +121,7 @@
 #define isalnum(c)	(('0' <= (c) && (c) <= '9') \
 			 || ('a' <= (c) && (c) <= 'z') \
 			 || ('A' <= (c) && (c) <= 'Z'))
+#define isspace(c)	(c == ' ' || c == '\t' || c == 10 || c == 13 || c == 0)
 
 static char *help_string = "\
 Commands:\n\
@@ -138,6 +139,8 @@
   r	print registers\n\
   S	print special registers\n\
   t	print backtrace\n\
+  la	lookup address in system.map\n\
+  ls	lookup symbol in system.map\n\
   x	exit monitor\n\
 ";
 
@@ -147,6 +150,19 @@
 
 static struct pt_regs *xmon_regs[NR_CPUS];
 
+extern inline void sync(void)
+{
+	asm volatile("sync; isync");
+}
+
+extern inline void __delay(unsigned int loops)
+{
+	if (loops != 0)
+		__asm__ __volatile__("mtctr %0; 1: bdnz 1b" : :
+				     "r" (loops) : "ctr");
+}
+
+
 void
 xmon(struct pt_regs *excp)
 {
@@ -398,6 +414,9 @@
 		case 'd':
 			dump();
 			break;
+		case 'l':
+			sysmap_lookup();
+			break;
 		case 'r':
 			if (excp != NULL)
 				prregs(excp);	/* print regs */
@@ -464,8 +483,8 @@
 	if (cmd == 'i') {
 		/* interrupt other cpu(s) */
 		cpu = MSG_ALL_BUT_SELF;
-		scanhex(&cpu);
-		smp_send_xmon_break(cpu);
+		if (scanhex(&cpu))
+			smp_send_xmon_break(cpu);
 		return;
 	}
 	termch = cmd;
@@ -547,8 +566,10 @@
 	unsigned short fcs;
 	unsigned char v;
 
-	scanhex(&adrs);
-	scanhex(&ncsum);
+	if (!scanhex(&adrs))
+		return;
+	if (!scanhex(&ncsum))
+		return;
 	fcs = 0xffff;
 	for (i = 0; i < ncsum; ++i) {
 		if (mread(adrs+i, &v, 1) == 0) {
@@ -580,6 +601,11 @@
 			mode = 6;
 		else
 			termch = cmd;
+		cmd = inchar();
+		if (cmd == 'p')
+			mode &= ~4;
+		else
+			termch = cmd;
 		dabr.address = 0;
 		dabr.count = 0;
 		dabr.enabled = scanhex(&dabr.address);
@@ -588,11 +614,16 @@
 			dabr.address = (dabr.address & ~7) | mode;
 		break;
 	case 'i':
+		cmd = inchar();
+		if (cmd == 'p')
+			mode = 2;
+		else
+			mode = 3;
 		iabr.address = 0;
 		iabr.count = 0;
 		iabr.enabled = scanhex(&iabr.address);
 		if (iabr.enabled)
-			iabr.address |= 3;
+			iabr.address |= mode;
 		scanhex(&iabr.count);
 		break;
 #endif
@@ -625,6 +656,8 @@
 					printf("r");
 				if (dabr.address & 2)
 					printf("w");
+				if (dabr.address & 4)
+					printf("p");
 				printf("]\n");
 			}
 			if (iabr.enabled)
@@ -674,7 +707,8 @@
 	for (; sp != 0; sp = stack[0]) {
 		if (mread(sp, stack, sizeof(stack)) != sizeof(stack))
 			break;
-		printf("%x ", stack[1]);
+		pretty_print_addr(stack[1]);
+		printf(" ");
 		if (stack[1] == (unsigned) &ret_from_intercept
 		    || stack[1] == (unsigned) &ret_from_except
 		    || stack[1] == (unsigned) &ret_from_syscall_1
@@ -688,8 +722,8 @@
 			if (mread(sp, stack, sizeof(stack)) != sizeof(stack))
 				break;
 		}
+		printf("\n");
 	}
-	printf("\n");
 }
 
 int
@@ -707,10 +741,11 @@
 #ifdef CONFIG_SMP
 	printf("cpu %d: ", smp_processor_id());
 #endif /* CONFIG_SMP */
-	printf("vector: %x at pc = %x",
-	       fp->trap, fp->nip);
-	printf(", lr = %x, msr = %x, sp = %x [%x]\n",
-	       fp->link, fp->msr, fp->gpr[1], fp);
+	printf("vector: %x at pc = ", fp->trap);
+	pretty_print_addr(fp->nip);
+	printf(", lr = ");
+	pretty_print_addr(fp->link);
+	printf("\nmsr = %x, sp = %x [%x]\n", fp->msr, fp->gpr[1], fp);
 	if (fp->trap == 0x300 || fp->trap == 0x600)
 		printf("dar = %x, dsisr = %x\n", fp->dar, fp->dsisr);
 	if (current)
@@ -795,8 +830,16 @@
 print_sysmap(void)
 {
 	extern char *sysmap;
-	if ( sysmap )
-		printf("System.map: \n%s", sysmap);
+	if ( sysmap ) {
+		printf("System.map: \n");
+		if( setjmp(bus_error_jmp) == 0 ) {
+			debugger_fault_handler = handle_fault;
+			sync();
+			xmon_puts(sysmap);
+			sync();
+		}
+		debugger_fault_handler = 0;
+	}
 	else
 		printf("No System.map\n");
 }
@@ -1028,17 +1071,6 @@
 /*
  * Stuff for reading and writing memory safely
  */
-extern inline void sync(void)
-{
-	asm volatile("sync; isync");
-}
-
-extern inline void __delay(unsigned int loops)
-{
-	if (loops != 0)
-		__asm__ __volatile__("mtctr %0; 1: bdnz 1b" : :
-				     "r" (loops) : "ctr");
-}
 
 int
 mread(unsigned adrs, void *buf, int size)
@@ -1565,6 +1597,24 @@
 		}
 		printf("invalid register name '%%%s'\n", regname);
 		return 0;
+	} else if (c == '$') {
+		static char symname[64];
+		int i;
+		for (i=0; i<63; i++) {
+			c = inchar();
+			if (isspace(c)) {
+				termch = c;
+				break;
+			}
+			symname[i] = c;
+		}
+		symname[i++] = 0;
+		*vp = xmon_symbol_to_addr(symname);
+		if (!(*vp)) {
+			printf("unknown symbol\n");
+			return 0;
+		}
+		return 1;
 	}
 
 	d = hexdigit(c);
@@ -1652,38 +1702,169 @@
 	lineptr = str;
 }
 
-#if 0 /* Makes compile with -Wall */
-static char *pretty_print_addr(unsigned long addr)
+void
+sysmap_lookup(void)
 {
-	printf("%08x", addr);
-	if ( lookup_name(addr) )
-		printf(" %s", lookup_name(addr) );
-	return NULL;
+	int type = inchar();
+	unsigned addr;
+	static char tmp[64];
+	char* cur;
+
+	extern char *sysmap;
+	extern unsigned long sysmap_size;
+	if ( !sysmap || !sysmap_size )
+		return;
+
+	switch(type) {
+		case 'a':
+			if (scanhex(&addr)) {
+				pretty_print_addr(addr);
+				printf("\n");
+			}
+			termch = 0;
+			break;
+		case 's':
+			getstring(tmp, 64);
+			if( setjmp(bus_error_jmp) == 0 ) {
+				debugger_fault_handler = handle_fault;
+				sync();
+				cur = sysmap;
+				do {
+					cur = strstr(cur, tmp);
+					if (cur) {
+						static char res[64];
+						char *p, *d;
+						p = cur;
+						while(p > sysmap && *p != 10)
+							p--;
+						if (*p == 10) p++;
+						d = res;
+						while(*p && p < (sysmap + sysmap_size) && *p != 10)
+							*(d++) = *(p++);
+						*(d++) = 0;
+						printf("%s\n", res);
+						cur++;
+					}
+				} while (cur);
+				sync();
+			}
+			debugger_fault_handler = 0;
+			termch = 0;
+			break;
+	}
 }
-#endif
 
-#if 0 /* Makes compile with -Wall */
-static char *lookup_name(unsigned long addr)
+static int
+pretty_print_addr(unsigned long addr)
 {
+	char *sym;
+	unsigned long saddr;
+	
+	printf("%08x", addr);
+	sym = xmon_find_symbol(addr, &saddr);
+	if (sym)
+		printf(" (%s+0x%x)", sym, addr-saddr);
+	return (sym != 0);
+}
+
+char*
+xmon_find_symbol(unsigned long addr, unsigned long* saddr)
+{
+	static char rbuffer[64];
+	char *p, *ep, *limit;
+	unsigned long prev, next;
+	char* psym;
+
 	extern char *sysmap;
 	extern unsigned long sysmap_size;
-	char *c = sysmap;
-	unsigned long cmp;
 	if ( !sysmap || !sysmap_size )
 		return NULL;
-return NULL;	
-#if 0
-	cmp = simple_strtoul(c, &c, 8);
-	/* XXX crap, we don't want the whole of the rest of the map - paulus */
-	strcpy( last, strsep( &c, "\n"));
-	while ( c < (sysmap+sysmap_size) )
-	{
-		cmp = simple_strtoul(c, &c, 8);
-		if ( cmp < addr )
-			break;
-		strcpy( last, strsep( &c, "\n"));
+	
+	prev = 0;
+	psym = NULL;
+	p = sysmap;
+	limit = p + sysmap_size;
+	if( setjmp(bus_error_jmp) == 0 ) {
+		debugger_fault_handler = handle_fault;
+		sync();
+		do {
+			next = simple_strtoul(p, &p, 16);
+			if (next > addr && prev <= addr) {
+				if (!psym)
+					goto bail;
+				ep = rbuffer;
+				p = psym;
+				while(*p && p < limit && *p == 32)
+					p++;
+				while(*p && p < limit && *p != 10 && (ep - rbuffer) < 63)
+					*(ep++) = *(p++);
+				*(ep++) = 0;
+				if (saddr)
+					*saddr = prev;
+				debugger_fault_handler = 0;
+				return rbuffer;
+			}
+			prev = next;
+			psym = p;
+			while(*p && p < limit && *p != 10)
+				p++;
+			if (*p) p++;
+		} while(*p && p < limit && next);
+bail:
+		sync();
 	}
-	return last;
-#endif	
+	debugger_fault_handler = 0;
+	return NULL;
 }
-#endif
+
+unsigned long
+xmon_symbol_to_addr(char* symbol)
+{
+	char *p, *cur;
+	char *match;
+	int goodness = 0;
+	int result = 0;
+	
+	extern char *sysmap;
+	extern unsigned long sysmap_size;
+	if ( !sysmap || !sysmap_size )
+		return 0;
+
+	if( setjmp(bus_error_jmp) == 0 ) {
+		debugger_fault_handler = handle_fault;
+		sync();
+		cur = sysmap;
+		while(cur) {
+			cur = strstr(cur, symbol);
+			if (cur) {
+				int gd = 1;
+
+				/* best match if equal, better match if
+				 * begins with
+				 */
+				if (cur == sysmap || *(cur-1) == ' ') {
+					gd++;
+					if (cur[strlen(symbol)] == 10)
+						gd++;
+				}
+				if (gd > goodness) {
+					match = cur;
+					goodness = gd;
+					if (gd == 3)
+						break;
+				}
+				cur++;
+			}
+		}	
+		if (goodness) {
+			p = match;
+			while(p > sysmap && *p != 10)
+				p--;
+			if (*p == 10) p++;
+			result = simple_strtoul(p, &p, 16);
+		}
+		sync();
+	}
+	debugger_fault_handler = 0;
+	return result;
+}		
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/s390/Makefile linux/arch/s390/Makefile
--- linux.orig/arch/s390/Makefile	Mon Jan  7 23:41:08 2002
+++ linux/arch/s390/Makefile	Fri Dec 21 16:25:30 2001
@@ -65,3 +65,6 @@
 
 archdep:
 	@$(MAKEBOOT) dep
+
+install: vmlinux
+	@$(MAKEBOOT) BOOTIMAGE=image install
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/s390/boot/Makefile linux/arch/s390/boot/Makefile
--- linux.orig/arch/s390/boot/Makefile	Mon Jan  7 23:41:08 2002
+++ linux/arch/s390/boot/Makefile	Fri Dec 21 16:25:30 2001
@@ -35,3 +35,6 @@
 clean:
 	rm -f image listing iplfba.boot ipleckd.boot ipldump.boot
 
+install: $(CONFIGURE) $(BOOTIMAGE)
+	sh -x ./install.sh $(KERNELRELEASE) $(BOOTIMAGE) $(TOPDIR)/System.map $(TOPDIR)/Kerntypes "$(INSTALL_PATH)"
+
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/s390/config.in linux/arch/s390/config.in
--- linux.orig/arch/s390/config.in	Mon Jan  7 23:41:08 2002
+++ linux/arch/s390/config.in	Fri Dec 21 16:25:30 2001
@@ -67,9 +67,9 @@
 comment 'Kernel hacking'
 
 #bool 'Debug kmalloc/kfree' CONFIG_DEBUG_MALLOC
-if [ "$CONFIG_CTC" = "y" ]; then
-  bool 'Remote GDB kernel debugging' CONFIG_REMOTE_DEBUG
-fi
+#if [ "$CONFIG_CTC" = "y" ]; then
+#  bool 'Remote GDB kernel debugging' CONFIG_REMOTE_DEBUG
+#fi
 bool 'Magic SysRq key' CONFIG_MAGIC_SYSRQ
 endmenu
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/s390/defconfig linux/arch/s390/defconfig
--- linux.orig/arch/s390/defconfig	Mon Jan  7 23:41:08 2002
+++ linux/arch/s390/defconfig	Fri Dec 21 16:25:30 2001
@@ -7,7 +7,7 @@
 CONFIG_UID16=y
 CONFIG_RWSEM_GENERIC_SPINLOCK=y
 # CONFIG_RWSEM_XCHGADD_ALGORITHM is not set
-CONFIG_GENERIC_BUST_SPINLOCK=n
+# CONFIG_GENERIC_BUST_SPINLOCK is not set
 CONFIG_ARCH_S390=y
 
 #
@@ -103,8 +103,8 @@
 #
 # S/390 tape hardware support
 #
-CONFIG_S390_TAPE_3490=y
-CONFIG_S390_TAPE_3480=y
+CONFIG_S390_TAPE_3490=m
+CONFIG_S390_TAPE_3480=m
 
 #
 # Network device drivers
@@ -150,6 +150,7 @@
 # CONFIG_IPV6_NETLINK is not set
 # CONFIG_KHTTPD is not set
 # CONFIG_ATM is not set
+# CONFIG_VLAN_8021Q is not set
 
 #
 #  
@@ -180,12 +181,12 @@
 # CONFIG_AUTOFS4_FS is not set
 # CONFIG_REISERFS_FS is not set
 # CONFIG_REISERFS_CHECK is not set
+# CONFIG_REISERFS_PROC_INFO is not set
 # CONFIG_ADFS_FS is not set
 # CONFIG_ADFS_FS_RW is not set
 # CONFIG_AFFS_FS is not set
 # CONFIG_HFS_FS is not set
 # CONFIG_BFS_FS is not set
-# CONFIG_CMS_FS is not set
 # CONFIG_EXT3_FS is not set
 # CONFIG_JBD is not set
 # CONFIG_JBD_DEBUG is not set
@@ -203,7 +204,7 @@
 # CONFIG_JOLIET is not set
 # CONFIG_ZISOFS is not set
 # CONFIG_MINIX_FS is not set
-# CONFIG_FREEVXFS_FS is not set
+# CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
 # CONFIG_NTFS_RW is not set
 # CONFIG_HPFS_FS is not set
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/s390/kernel/debug.c linux/arch/s390/kernel/debug.c
--- linux.orig/arch/s390/kernel/debug.c	Mon Jan  7 23:41:08 2002
+++ linux/arch/s390/kernel/debug.c	Fri Dec 21 16:25:30 2001
@@ -228,8 +228,10 @@
 	strncpy(rc->name, name, MIN(strlen(name), (DEBUG_MAX_PROCF_LEN - 1)));
 	rc->name[MIN(strlen(name), (DEBUG_MAX_PROCF_LEN - 1))] = 0;
 	memset(rc->views, 0, DEBUG_MAX_VIEWS * sizeof(struct debug_view *));
+#ifdef CONFIG_PROC_FS
 	memset(rc->proc_entries, 0 ,DEBUG_MAX_VIEWS *
 		sizeof(struct proc_dir_entry*));
+#endif /* CONFIG_PROC_FS */
 	atomic_set(&(rc->ref_count), 0);
 
 	return rc;
@@ -346,8 +348,10 @@
 	if (!db_info)
 		return;
 	if (atomic_dec_and_test(&db_info->ref_count)) {
+#ifdef DEBUG
 		printk(KERN_INFO "debug: freeing debug area %p (%s)\n",
 		       db_info, db_info->name);
+#endif
 		for (i = 0; i < DEBUG_MAX_VIEWS; i++) {
 			if (db_info->views[i] != NULL)
 				debug_delete_proc_dir_entry
@@ -541,14 +545,18 @@
 	debug_info_snapshot = debug_info_copy(debug_info);
 
 	if(!debug_info_snapshot){
+#ifdef DEBUG
 		printk(KERN_ERR "debug_open: debug_info_copy failed (out of mem)\n");
+#endif
 		rc = -ENOMEM;
 		goto out;
 	}
 
 	if ((file->private_data =
 	     kmalloc(sizeof(file_private_info_t), GFP_ATOMIC)) == 0) {
+#ifdef DEBUG
 		printk(KERN_ERR "debug_open: kmalloc failed\n");
+#endif
 		debug_info_free(debug_info_snapshot);	
 		rc = -ENOMEM;
 		goto out;
@@ -602,6 +610,7 @@
 {
 	struct proc_dir_entry *rc = NULL;
 
+#ifdef CONFIG_PROC_FS
 #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,98))
 	const char *fn = name;
 	int len;
@@ -634,6 +643,7 @@
 #endif
 
       out:
+#endif /* CONFIG_PROC_FS */
 	return rc;
 }
 
@@ -646,12 +656,14 @@
     (struct proc_dir_entry *root, struct proc_dir_entry *proc_entry) 
 {
 
+#ifdef CONFIG_PROC_FS
 #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,98))
 	proc_unregister(root, proc_entry->low_ino);
 	kfree(proc_entry);
 #else
 	remove_proc_entry(proc_entry->name, root);
 #endif
+#endif /* CONFIG_PROC_FS */
 }
 
 /*
@@ -677,9 +689,11 @@
 		goto out;
 	debug_register_view(rc, &debug_level_view);
         debug_register_view(rc, &debug_flush_view);
+#ifdef DEBUG
 	printk(KERN_INFO
 	       "debug: reserved %d areas of %d pages for debugging %s\n",
 	       nr_areas, 1 << page_order, rc->name);
+#endif
       out:
         if (rc == NULL){
 		printk(KERN_ERR "debug: debug_register failed for %s\n",name);
@@ -699,7 +713,9 @@
 	if (!id)
 		goto out;
 	down(&debug_lock);
+#ifdef DEBUG
 	printk(KERN_INFO "debug: unregistering %s\n", id->name);
+#endif
 	debug_info_put(id);
 	up(&debug_lock);
 
@@ -906,11 +922,13 @@
 
 	down(&debug_lock);
 	if (!initialized) {
+#ifdef CONFIG_PROC_FS
 		debug_proc_root_entry =
 		    debug_create_proc_dir_entry(&proc_root, DEBUG_DIR_ROOT,
 						S_IFDIR | S_IRUGO | S_IXUGO
 						| S_IWUSR | S_IWGRP, NULL,
 						NULL);
+#endif /* CONFIG_PROC_FS */
 		printk(KERN_INFO "debug: Initialization complete\n");
 		initialized = 1;
 	}
@@ -1271,7 +1289,9 @@
 #ifdef DEBUG
 	printk("debug_cleanup_module: \n");
 #endif
+#ifdef CONFIG_PROC_FS
 	debug_delete_proc_dir_entry(&proc_root, debug_proc_root_entry);
+#endif /* CONFIG_PROC_FS */
 	return;
 }
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/s390/kernel/entry.S linux/arch/s390/kernel/entry.S
--- linux.orig/arch/s390/kernel/entry.S	Mon Jan  7 23:41:08 2002
+++ linux/arch/s390/kernel/entry.S	Fri Dec 21 16:25:30 2001
@@ -79,7 +79,7 @@
 sigpending   =  8
 need_resched = 24
 tsk_ptrace   = 28
-processor    = 56
+processor    = 52
 
 /*
  * Base Address of this Module --- saved in __LC_ENTRY_BASE
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/s390/kernel/init_task.c linux/arch/s390/kernel/init_task.c
--- linux.orig/arch/s390/kernel/init_task.c	Mon Jan  7 23:41:08 2002
+++ linux/arch/s390/kernel/init_task.c	Fri Dec 21 16:25:30 2001
@@ -12,7 +12,6 @@
 #include <asm/uaccess.h>
 #include <asm/pgtable.h>
 
-static struct vm_area_struct init_mmap = INIT_MMAP;
 static struct fs_struct init_fs = INIT_FS;
 static struct files_struct init_files = INIT_FILES;
 static struct signal_struct init_signals = INIT_SIGNALS;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/s390/kernel/process.c linux/arch/s390/kernel/process.c
--- linux.orig/arch/s390/kernel/process.c	Mon Jan  7 23:41:08 2002
+++ linux/arch/s390/kernel/process.c	Fri Dec 21 16:25:30 2001
@@ -75,173 +75,24 @@
 	}
 }
 
-/*
-  As all the register will only be made displayable to the root
-  user ( via printk ) or checking if the uid of the user is 0 from
-  the /proc filesystem please god this will be secure enough DJB.
-  The lines are given one at a time so as not to chew stack space in
-  printk on a crash & also for the proc filesystem when you get
-  0 returned you know you've got all the lines
- */
-
-static int sprintf_regs(int line, char *buff, struct task_struct *task, struct pt_regs *regs)
-{
-	int linelen=0;
-	int regno,chaincnt;
-	u32 backchain,prev_backchain,endchain;
-	u32 ksp = 0;
-	char *mode = "???";
-
-	enum
-	{
-		sp_linefeed,
-		sp_psw,
-		sp_ksp,
-		sp_gprs,
-		sp_gprs1,
-		sp_gprs2,
-		sp_gprs3,
-		sp_gprs4,
-		sp_acrs,
-		sp_acrs1,
-		sp_acrs2,
-		sp_acrs3,
-		sp_acrs4,
-		sp_kern_backchain,
-		sp_kern_backchain1
-	};
-
-	if (task)
-		ksp = task->thread.ksp;
-	if (regs && !(regs->psw.mask & PSW_PROBLEM_STATE))
-		ksp = regs->gprs[15];
-
-	if (regs)
-		mode = (regs->psw.mask & PSW_PROBLEM_STATE)?
-		       "User" : "Kernel";
-
-	switch(line)
-	{
-	case sp_linefeed: 
-		linelen=sprintf(buff,"\n");
-		break;
-	case sp_psw:
-		if(regs)
-			linelen=sprintf(buff, "%s PSW:    %08lx %08lx    %s\n", mode,
-				(unsigned long) regs->psw.mask,
-				(unsigned long) regs->psw.addr,
-				print_tainted());
-		else
-			linelen=sprintf(buff,"pt_regs=NULL some info unavailable\n");
-		break;
-	case sp_ksp:
-		linelen=sprintf(&buff[linelen],
-				"task: %08x ksp: %08x pt_regs: %08x\n",
-				(addr_t)task, (addr_t)ksp, (addr_t)regs);
-		break;
-	case sp_gprs:
-		if(regs)
-			linelen=sprintf(buff, "%s GPRS:\n", mode);
-		break;
-	case sp_gprs1 ... sp_gprs4:
-		if(regs)
-		{
-			regno=(line-sp_gprs1)*4;
-			linelen=sprintf(buff,"%08x  %08x  %08x  %08x\n",
-					regs->gprs[regno], 
-					regs->gprs[regno+1],
-					regs->gprs[regno+2],
-					regs->gprs[regno+3]);
-		}
-		break;
-	case sp_acrs:
-		if(regs)
-			linelen=sprintf(buff, "%s ACRS:\n", mode);
-		break;	
-        case sp_acrs1 ... sp_acrs4:
-		if(regs)
-		{
-			regno=(line-sp_acrs1)*4;
-			linelen=sprintf(buff,"%08x  %08x  %08x  %08x\n",
-					regs->acrs[regno],
-					regs->acrs[regno+1],
-					regs->acrs[regno+2],
-					regs->acrs[regno+3]);
-		}
-		break;
-	case sp_kern_backchain:
-		if (regs && (regs->psw.mask & PSW_PROBLEM_STATE))
-			break;	
-		if (ksp)
-			linelen=sprintf(buff, "Kernel BackChain  CallChain\n");
-		break;
-	default:
-		if (ksp)
-		{
-			
-			backchain=ksp&PSW_ADDR_MASK;
-			endchain=((backchain&(-8192))+8192);
-			prev_backchain=backchain-1;
-			line-=sp_kern_backchain1;
-			for(chaincnt=0;;chaincnt++)
-			{
-				if((backchain==0)||(backchain>=endchain)
-				   ||(chaincnt>=8)||(prev_backchain>=backchain))
-					break;
-				if(chaincnt==line)
-				{
-					linelen+=sprintf(&buff[linelen],"       %08x   [<%08lx>]\n",
-							 backchain,
-							 *(u32 *)(backchain+56)&PSW_ADDR_MASK);
-					break;
-				}
-				prev_backchain=backchain;
-				backchain=(*((u32 *)backchain))&PSW_ADDR_MASK;
-			}
-		}
-	}
-	return(linelen);
-}
-
+extern void show_registers(struct pt_regs *regs);
+extern void show_trace(unsigned long *sp);
 
 void show_regs(struct pt_regs *regs)
 {
-	char buff[80];
-	int i, line;
+	struct task_struct *tsk = current;
 
-        printk("CPU:    %d\n",smp_processor_id());
-        printk("Process %s (pid: %d, stackpage=%08X)\n",
-                current->comm, current->pid, 4096+(addr_t)current);
-	
-	for (line = 0; sprintf_regs(line, buff, current, regs); line++)
-		printk(buff);
-
-	if (regs->psw.mask & PSW_PROBLEM_STATE)
-	{
-		printk("User Code:\n");
-		memset(buff, 0, 20);
-		copy_from_user(buff,
-			       (char *) (regs->psw.addr & PSW_ADDR_MASK), 20);
-		for (i = 0; i < 20; i++)
-			printk("%02x ", buff[i]);
-		printk("\n");
-	}
+        printk("CPU:    %d    %s\n", tsk->processor, print_tainted());
+        printk("Process %s (pid: %d, task: %08lx, ksp: %08x)\n",
+	       current->comm, current->pid, (unsigned long) tsk,
+	       tsk->thread.ksp);
+
+	show_registers(regs);
+	/* Show stack backtrace if pt_regs is from kernel mode */
+	if (!(regs->psw.mask & PSW_PROBLEM_STATE))
+		show_trace((unsigned long *) regs->gprs[15]);
 }
 
-char *task_show_regs(struct task_struct *task, char *buffer)
-{
-	int line, len;
-
-	for (line = 0; ; line++)
-	{
-		len = sprintf_regs(line, buffer, task, task->thread.regs);
-		if (!len) break;
-		buffer += len;
-	}
-	return buffer;
-}
-
-
 int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
 {
         int clone_arg = flags | CLONE_VM;
@@ -301,16 +152,10 @@
             unsigned long gprs[10];    /* gprs 6 -15                       */
             unsigned long fprs[4];     /* fpr 4 and 6                      */
             unsigned long empty[4];
-#if CONFIG_REMOTE_DEBUG
-	    struct gdb_pt_regs childregs;
-#else
             struct pt_regs childregs;
-#endif
           } *frame;
 
         frame = (struct stack_frame *) (2*PAGE_SIZE + (unsigned long) p) -1;
-        frame = (struct stack_frame *) (((unsigned long) frame)&-8L);
-        p->thread.regs = (struct pt_regs *)&frame->childregs;
         p->thread.ksp = (unsigned long) frame;
         memcpy(&frame->childregs,regs,sizeof(struct pt_regs));
         frame->childregs.gprs[15] = new_stackp;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/s390/kernel/ptrace.c linux/arch/s390/kernel/ptrace.c
--- linux.orig/arch/s390/kernel/ptrace.c	Mon Jan  7 23:41:08 2002
+++ linux/arch/s390/kernel/ptrace.c	Fri Dec 21 16:25:30 2001
@@ -41,7 +41,7 @@
 
 void FixPerRegisters(struct task_struct *task)
 {
-	struct pt_regs *regs = task->thread.regs;
+	struct pt_regs *regs = __KSTK_PTREGS(task);
 	per_struct *per_info=
 			(per_struct *)&task->thread.per_info;
 
@@ -155,7 +155,7 @@
 		mask=0xffffffff;
 		if(useraddr<PT_FPC)
 		{
-			realuseraddr=(addr_t)&(((u8 *)task->thread.regs)[useraddr]);
+			realuseraddr=((addr_t) __KSTK_PTREGS(task)) + useraddr;
 			if(useraddr<PT_PSWMASK)
 			{
 				copymax=PT_PSWMASK;
@@ -217,7 +217,6 @@
 {
 	struct task_struct *child;
 	int ret = -EPERM;
-	unsigned long flags;
 	unsigned long tmp;
 	int copied;
 	ptrace_area   parea; 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/s390/kernel/s390_ksyms.c linux/arch/s390/kernel/s390_ksyms.c
--- linux.orig/arch/s390/kernel/s390_ksyms.c	Mon Jan  7 23:41:08 2002
+++ linux/arch/s390/kernel/s390_ksyms.c	Fri Dec 21 16:25:30 2001
@@ -18,8 +18,9 @@
 EXPORT_SYMBOL_NOVERS(_oi_bitmap);
 EXPORT_SYMBOL_NOVERS(_ni_bitmap);
 EXPORT_SYMBOL_NOVERS(_zb_findmap);
-EXPORT_SYMBOL_NOVERS(__copy_from_user_fixup);
-EXPORT_SYMBOL_NOVERS(__copy_to_user_fixup);
+EXPORT_SYMBOL_NOVERS(__copy_from_user_asm);
+EXPORT_SYMBOL_NOVERS(__copy_to_user_asm);
+EXPORT_SYMBOL_NOVERS(__clear_user_asm);
 
 /*
  * semaphore ops
@@ -35,6 +36,7 @@
 EXPORT_SYMBOL_NOVERS(memcmp);
 EXPORT_SYMBOL_NOVERS(memset);
 EXPORT_SYMBOL_NOVERS(memmove);
+EXPORT_SYMBOL_NOVERS(memscan);
 EXPORT_SYMBOL_NOVERS(strlen);
 EXPORT_SYMBOL_NOVERS(strchr);
 EXPORT_SYMBOL_NOVERS(strcmp);
@@ -57,5 +59,3 @@
 EXPORT_SYMBOL(console_mode);
 EXPORT_SYMBOL(console_device);
 EXPORT_SYMBOL_NOVERS(do_call_softirq);
-
-
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/s390/kernel/setup.c linux/arch/s390/kernel/setup.c
--- linux.orig/arch/s390/kernel/setup.c	Mon Jan  7 23:41:08 2002
+++ linux/arch/s390/kernel/setup.c	Fri Dec 21 16:25:30 2001
@@ -439,6 +439,7 @@
 	lowcore->kernel_stack = ((__u32) &init_task_union) + 8192;
 	lowcore->async_stack = (__u32)
 		__alloc_bootmem(2*PAGE_SIZE, 2*PAGE_SIZE, 0) + 8192;
+	lowcore->jiffy_timer = -1LL;
 	set_prefix((__u32) lowcore);
         cpu_init();
         boot_cpu_addr = S390_lowcore.cpu_data.cpu_addr;
@@ -485,15 +486,16 @@
 static int show_cpuinfo(struct seq_file *m, void *v)
 {
         struct cpuinfo_S390 *cpuinfo;
-	unsigned n = v;
+	unsigned long n = (unsigned long) v - 1;
 
-	if (!n--) {
+	if (!n) {
 		seq_printf(m, "vendor_id       : IBM/S390\n"
 			       "# processors    : %i\n"
 			       "bogomips per cpu: %lu.%02lu\n",
 			       smp_num_cpus, loops_per_jiffy/(500000/HZ),
 			       (loops_per_jiffy/(5000/HZ))%100);
-	} else if (cpu_online_map & (1 << n)) {
+	} 
+	if (cpu_online_map & (1 << n)) {
 		cpuinfo = &safe_get_cpu_lowcore(n).cpu_data;
 		seq_printf(m, "processor %i: "
 			       "version = %02X,  "
@@ -508,7 +510,7 @@
 
 static void *c_start(struct seq_file *m, loff_t *pos)
 {
-	return *pos <= NR_CPUS ? (void)(*pos+1) : NULL;
+	return *pos <= NR_CPUS ? (void *)((unsigned long) *pos + 1) : NULL;
 }
 static void *c_next(struct seq_file *m, void *v, loff_t *pos)
 {
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/s390/kernel/smp.c linux/arch/s390/kernel/smp.c
--- linux.orig/arch/s390/kernel/smp.c	Mon Jan  7 23:41:08 2002
+++ linux/arch/s390/kernel/smp.c	Fri Dec 21 16:25:30 2001
@@ -49,9 +49,6 @@
 static int       max_cpus = NR_CPUS;	  /* Setup configured maximum number of CPUs to activate	*/
 int              smp_num_cpus;
 struct _lowcore *lowcore_ptr[NR_CPUS];
-unsigned int     prof_multiplier[NR_CPUS];
-unsigned int     prof_old_multiplier[NR_CPUS];
-unsigned int     prof_counter[NR_CPUS];
 cycles_t         cacheflush_time=0;
 int              smp_threads_ready=0;      /* Set when the idlers are all forked. */
 static atomic_t  smp_commenced = ATOMIC_INIT(0);
@@ -473,7 +470,7 @@
 /*
  *      Activate a secondary processor.
  */
-extern void init_100hz_timer(void);
+extern void init_cpu_timer(void);
 extern int pfault_init(void);
 extern int pfault_token(void);
 
@@ -486,8 +483,8 @@
         /* Wait for completion of smp startup */
         while (!atomic_read(&smp_commenced))
                 /* nothing */ ;
-        /* init per CPU 100 hz timer */
-        init_100hz_timer();
+        /* init per CPU timer */
+        init_cpu_timer();
 #ifdef CONFIG_PFAULT
 	/* Enable pfault pseudo page faults on this cpu. */
 	pfault_init();
@@ -540,7 +537,7 @@
 
         cpu_lowcore=&get_cpu_lowcore(cpu);
 	cpu_lowcore->save_area[15] = idle->thread.ksp;
-	cpu_lowcore->kernel_stack = (idle->thread.ksp | 8191) + 1;
+	cpu_lowcore->kernel_stack = (__u32) idle + 8192;
         __asm__ __volatile__("la    1,%0\n\t"
 			     "stctl 0,15,0(1)\n\t"
 			     "la    1,%1\n\t"
@@ -591,15 +588,7 @@
         
         /*
          *      Initialize the logical to physical CPU number mapping
-         *      and the per-CPU profiling counter/multiplier
          */
-        
-        for (i = 0; i < NR_CPUS; i++) {
-                prof_counter[i] = 1;
-                prof_old_multiplier[i] = 1;
-                prof_multiplier[i] = 1;
-        }
-
         print_cpu_info(&safe_get_cpu_lowcore(0).cpu_data);
 
         for(i = 0; i < smp_num_cpus; i++)
@@ -646,58 +635,6 @@
 int setup_profiling_timer(unsigned int multiplier)
 {
         return 0;
-}
-
-/*
- * Local timer interrupt handler. It does both profiling and
- * process statistics/rescheduling.
- *
- * We do profiling in every local tick, statistics/rescheduling
- * happen only every 'profiling multiplier' ticks. The default
- * multiplier is 1 and it can be changed by writing the new multiplier
- * value into /proc/profile.
- */
-
-void smp_local_timer_interrupt(struct pt_regs * regs)
-{
-	int user = (user_mode(regs) != 0);
-        int cpu = smp_processor_id();
-
-        /*
-         * The profiling function is SMP safe. (nothing can mess
-         * around with "current", and the profiling counters are
-         * updated with atomic operations). This is especially
-         * useful with a profiling multiplier != 1
-         */
-        if (!user_mode(regs))
-                s390_do_profile(regs->psw.addr);
-
-        if (!--prof_counter[cpu]) {
-
-                /*
-                 * The multiplier may have changed since the last time we got
-                 * to this point as a result of the user writing to
-                 * /proc/profile.  In this case we need to adjust the APIC
-                 * timer accordingly.
-                 *
-                 * Interrupts are already masked off at this point.
-                 */
-                prof_counter[cpu] = prof_multiplier[cpu];
-                if (prof_counter[cpu] != prof_old_multiplier[cpu]) {
-			/* FIXME setup_APIC_timer(calibration_result/prof_counter[cpu]
-			   ); */
-                  prof_old_multiplier[cpu] = prof_counter[cpu];
-                }
-
-                /*
-                 * 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.
-                 */
-
-		update_process_times(user);
-        }
 }
 
 EXPORT_SYMBOL(lowcore_ptr);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/s390/kernel/time.c linux/arch/s390/kernel/time.c
--- linux.orig/arch/s390/kernel/time.c	Mon Jan  7 23:41:08 2002
+++ linux/arch/s390/kernel/time.c	Fri Dec 21 16:25:30 2001
@@ -33,19 +33,18 @@
 
 #include <asm/irq.h>
 
-
 /* change this if you have some constant time drift */
-#define USECS_PER_JIFFY ((signed long)1000000/HZ)
-#define CLK_TICKS_PER_JIFFY ((signed long)USECS_PER_JIFFY<<12)
+#define USECS_PER_JIFFY     ((unsigned long) 1000000/HZ)
+#define CLK_TICKS_PER_JIFFY ((unsigned long) USECS_PER_JIFFY << 12)
 
 #define TICK_SIZE tick
 
-static uint64_t init_timer_cc, last_timer_cc;
+static uint64_t init_timer_cc;
 
 extern rwlock_t xtime_lock;
 extern unsigned long wall_jiffies;
 
-void tod_to_timeval(uint64_t todval, struct timeval *xtime)
+void tod_to_timeval(__u64 todval, struct timeval *xtime)
 {
         const int high_bit = 0x80000000L;
         const int c_f4240 = 0xf4240L;
@@ -79,13 +78,15 @@
 		      : "cc", "memory", "2", "3", "4" );
 }
 
-unsigned long do_gettimeoffset(void) 
+static inline unsigned long do_gettimeoffset(void) 
 {
-	__u64 timer_cc;
+	__u64 now;
 
-	asm volatile ("STCK %0" : "=m" (timer_cc));
-        /* We require the offset from the previous interrupt */
-        return ((unsigned long)((timer_cc - last_timer_cc)>>12));
+	asm ("STCK %0" : "=m" (now));
+        now = (now - init_timer_cc) >> 12;
+	/* We require the offset from the latest update of xtime */
+	now -= (__u64) wall_jiffies*USECS_PER_JIFFY;
+	return (unsigned long) now;
 }
 
 /*
@@ -95,15 +96,10 @@
 {
 	unsigned long flags;
 	unsigned long usec, sec;
-	unsigned long lost_ticks;
 
 	read_lock_irqsave(&xtime_lock, flags);
-	lost_ticks = jiffies - wall_jiffies;
-	usec = do_gettimeoffset();
-	if (lost_ticks)
-		usec +=(USECS_PER_JIFFY*lost_ticks);
 	sec = xtime.tv_sec;
-	usec += xtime.tv_usec;
+	usec = xtime.tv_usec + do_gettimeoffset();
 	read_unlock_irqrestore(&xtime_lock, flags);
 
 	while (usec >= 1000000) {
@@ -149,51 +145,31 @@
 extern __u16 boot_cpu_addr;
 #endif
 
-void do_timer_interrupt(struct pt_regs *regs, __u16 error_code)
+static void do_comparator_interrupt(struct pt_regs *regs, __u16 error_code)
 {
 	int cpu = smp_processor_id();
 
 	irq_enter(cpu, 0);
 
-        /*
-         * reset timer to 10ms minus time already elapsed
-         * since timer-interrupt pending
-         */
+	/*
+	 * set clock comparator for next tick
+	 */
+        S390_lowcore.jiffy_timer += CLK_TICKS_PER_JIFFY;
+        asm volatile ("SCKC %0" : : "m" (S390_lowcore.jiffy_timer));
+
 #ifdef CONFIG_SMP
-	if(S390_lowcore.cpu_data.cpu_addr==boot_cpu_addr) {
+	if (S390_lowcore.cpu_data.cpu_addr == boot_cpu_addr)
 		write_lock(&xtime_lock);
-		last_timer_cc = S390_lowcore.jiffy_timer_cc;
-	}
-#else
-        last_timer_cc = S390_lowcore.jiffy_timer_cc;
-#endif
-        /* set clock comparator */
-        S390_lowcore.jiffy_timer_cc += CLK_TICKS_PER_JIFFY;
-        asm volatile ("SCKC %0" : : "m" (S390_lowcore.jiffy_timer_cc));
 
-/*
- * In the SMP case we use the local timer interrupt to do the
- * profiling, except when we simulate SMP mode on a uniprocessor
- * system, in that case we have to call the local interrupt handler.
- */
-#ifdef CONFIG_SMP
-        /* when SMP, do smp_local_timer_interrupt for *all* CPUs,
-           but only do the rest for the boot CPU */
-        smp_local_timer_interrupt(regs);
-#else
-        if (!user_mode(regs))
-                s390_do_profile(regs->psw.addr);
-#endif
+	update_process_times(user_mode(regs));
 
-#ifdef CONFIG_SMP
-	if(S390_lowcore.cpu_data.cpu_addr==boot_cpu_addr)
-#endif
-	{
+	if (S390_lowcore.cpu_data.cpu_addr == boot_cpu_addr) {
 		do_timer(regs);
-#ifdef CONFIG_SMP
 		write_unlock(&xtime_lock);
-#endif
 	}
+#else
+	do_timer(regs);
+#endif
 
 	irq_exit(cpu, 0);
 }
@@ -201,19 +177,17 @@
 /*
  * Start the clock comparator on the current CPU
  */
-static long cr0 __attribute__ ((aligned (8)));
-
-void init_100hz_timer(void)
+void init_cpu_timer(void)
 {
+	unsigned long cr0;
+
         /* allow clock comparator timer interrupt */
         asm volatile ("STCTL 0,0,%0" : "=m" (cr0) : : "memory");
         cr0 |= 0x800;
         asm volatile ("LCTL 0,0,%0" : : "m" (cr0) : "memory");
-        /* set clock comparator */
-        /* read the TOD clock */
-        asm volatile ("STCK %0" : "=m" (S390_lowcore.jiffy_timer_cc));
-        S390_lowcore.jiffy_timer_cc += CLK_TICKS_PER_JIFFY;
-        asm volatile ("SCKC %0" : : "m" (S390_lowcore.jiffy_timer_cc));
+	S390_lowcore.jiffy_timer = (__u64) jiffies * CLK_TICKS_PER_JIFFY;
+	S390_lowcore.jiffy_timer += init_timer_cc + CLK_TICKS_PER_JIFFY;
+	asm volatile ("SCKC %0" : : "m" (S390_lowcore.jiffy_timer));
 }
 
 /*
@@ -222,6 +196,7 @@
  */
 void __init time_init(void)
 {
+        __u64 set_time_cc;
 	int cc;
 
         /* kick the TOD clock */
@@ -241,14 +216,18 @@
                 printk("time_init: TOD clock stopped/non-operational\n");
                 break;
         }
+
+	/* set xtime */
+        set_time_cc = init_timer_cc - 0x8126d60e46000000LL +
+                      (0x3c26700LL*1000000*4096);
+        tod_to_timeval(set_time_cc, &xtime);
+
         /* request the 0x1004 external interrupt */
-        if (register_external_interrupt(0x1004, do_timer_interrupt) != 0)
-                panic("Couldn't request external interrupts 0x1004");
-        init_100hz_timer();
-        init_timer_cc = S390_lowcore.jiffy_timer_cc;
-        init_timer_cc -= 0x8126d60e46000000LL -
-                         (0x3c26700LL*1000000*4096);
-        tod_to_timeval(init_timer_cc, &xtime);
+        if (register_external_interrupt(0x1004, do_comparator_interrupt) != 0)
+                panic("Couldn't request external interrupt 0x1004");
+
+        /* init CPU timer */
+        init_cpu_timer();
 
 	/* Set do_get_fast_time function pointer.  */
 	do_get_fast_time = do_gettimeofday;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/s390/kernel/traps.c linux/arch/s390/kernel/traps.c
--- linux.orig/arch/s390/kernel/traps.c	Mon Jan  7 23:41:08 2002
+++ linux/arch/s390/kernel/traps.c	Fri Dec 21 16:25:30 2001
@@ -26,15 +26,13 @@
 #include <linux/smp_lock.h>
 #include <linux/init.h>
 #include <linux/delay.h>
+#include <linux/module.h>
 
 #include <asm/system.h>
 #include <asm/uaccess.h>
 #include <asm/io.h>
 #include <asm/atomic.h>
 #include <asm/mathemu.h>
-#if CONFIG_REMOTE_DEBUG
-#include <asm/gdb-stub.h>
-#endif
 #include <asm/cpcmd.h>
 #include <asm/s390_ext.h>
 
@@ -60,6 +58,203 @@
 extern void pfault_interrupt(struct pt_regs *regs, __u16 error_code);
 #endif
 
+int kstack_depth_to_print = 12;
+
+/*
+ * If the address is either in the .text section of the
+ * kernel, or in the vmalloc'ed module regions, it *may* 
+ * be the address of a calling routine
+ */
+extern char _stext, _etext;
+
+#ifdef CONFIG_MODULES
+
+extern struct module *module_list;
+extern struct module kernel_module;
+
+static inline int kernel_text_address(unsigned long addr)
+{
+	int retval = 0;
+	struct module *mod;
+
+	if (addr >= (unsigned long) &_stext &&
+	    addr <= (unsigned long) &_etext)
+		return 1;
+
+	for (mod = module_list; mod != &kernel_module; mod = mod->next) {
+		/* mod_bound tests for addr being inside the vmalloc'ed
+		 * module area. Of course it'd be better to test only
+		 * for the .text subset... */
+		if (mod_bound(addr, 0, mod)) {
+			retval = 1;
+			break;
+		}
+	}
+
+	return retval;
+}
+
+#else
+
+static inline int kernel_text_address(unsigned long addr)
+{
+	return (addr >= (unsigned long) &_stext &&
+		addr <= (unsigned long) &_etext);
+}
+
+#endif
+
+void show_trace(unsigned long * stack)
+{
+	unsigned long backchain, low_addr, high_addr, ret_addr;
+	int i;
+
+	if (!stack)
+		stack = (unsigned long*)&stack;
+
+	printk("Call Trace: ");
+	low_addr = ((unsigned long) stack) & PSW_ADDR_MASK;
+	high_addr = (low_addr & (-THREAD_SIZE)) + THREAD_SIZE;
+	/* Skip the first frame (biased stack) */
+	backchain = *((unsigned long *) low_addr) & PSW_ADDR_MASK;
+	/* Print up to 8 lines */
+	for (i = 0; i < 8; i++) {
+		if (backchain < low_addr || backchain >= high_addr)
+			break;
+		ret_addr = *((unsigned long *) (backchain+56)) & PSW_ADDR_MASK;
+		if (!kernel_text_address(ret_addr))
+			break;
+		if (i && ((i % 6) == 0))
+			printk("\n   ");
+		printk("[<%08lx>] ", ret_addr);
+		low_addr = backchain;
+		backchain = *((unsigned long *) backchain) & PSW_ADDR_MASK;
+	}
+	printk("\n");
+}
+
+void show_trace_task(struct task_struct *tsk)
+{
+	/*
+	 * We can't print the backtrace of a running process. It is
+	 * unreliable at best and can cause kernel oopses.
+	 */
+	if (task_has_cpu(tsk))
+		return;
+	show_trace((unsigned long *) tsk->thread.ksp);
+}
+
+void show_stack(unsigned long *sp)
+{
+	unsigned long *stack;
+	int i;
+
+	// debugging aid: "show_stack(NULL);" prints the
+	// back trace for this cpu.
+
+	if(sp == NULL)
+		sp = (unsigned long*) &sp;
+
+	stack = sp;
+	for (i = 0; i < kstack_depth_to_print; i++) {
+		if (((addr_t) stack & (THREAD_SIZE-1)) == 0)
+			break;
+		if (i && ((i % 8) == 0))
+			printk("\n       ");
+		printk("%08lx ", *stack++);
+	}
+	printk("\n");
+	show_trace(sp);
+}
+
+void show_registers(struct pt_regs *regs)
+{
+	mm_segment_t old_fs;
+	char *mode;
+	int i;
+
+	mode = (regs->psw.mask & PSW_PROBLEM_STATE) ? "User" : "Krnl";
+	printk("%s PSW : %08lx %08lx\n",
+	       mode, (unsigned long) regs->psw.mask,
+	       (unsigned long) regs->psw.addr);
+	printk("%s GPRS: %08x %08x %08x %08x\n", mode,
+	       regs->gprs[0], regs->gprs[1], regs->gprs[2], regs->gprs[3]);
+	printk("           %08x %08x %08x %08x\n",
+	       regs->gprs[4], regs->gprs[5], regs->gprs[6], regs->gprs[7]);
+	printk("           %08x %08x %08x %08x\n",
+	       regs->gprs[8], regs->gprs[9], regs->gprs[10], regs->gprs[11]);
+	printk("           %08x %08x %08x %08x\n",
+	       regs->gprs[12], regs->gprs[13], regs->gprs[14], regs->gprs[15]);
+	printk("%s ACRS: %08x %08x %08x %08x\n", mode,
+	       regs->acrs[0], regs->acrs[1], regs->acrs[2], regs->acrs[3]);
+	printk("           %08x %08x %08x %08x\n",
+	       regs->acrs[4], regs->acrs[5], regs->acrs[6], regs->acrs[7]);
+	printk("           %08x %08x %08x %08x\n",
+	       regs->acrs[8], regs->acrs[9], regs->acrs[10], regs->acrs[11]);
+	printk("           %08x %08x %08x %08x\n",
+	       regs->acrs[12], regs->acrs[13], regs->acrs[14], regs->acrs[15]);
+
+	/*
+	 * Print the first 20 byte of the instruction stream at the
+	 * time of the fault.
+	 */
+	old_fs = get_fs();
+	if (regs->psw.mask & PSW_PROBLEM_STATE)
+		set_fs(USER_DS);
+	else
+		set_fs(KERNEL_DS);
+	printk("%s Code: ", mode);
+	for (i = 0; i < 20; i++) {
+		unsigned char c;
+		if (__get_user(c, (char *)(regs->psw.addr + i))) {
+			printk(" Bad PSW.");
+			break;
+		}
+		printk("%02x ", c);
+	}
+	set_fs(old_fs);
+
+	printk("\n");
+}	
+
+/* This is called from fs/proc/array.c */
+char *task_show_regs(struct task_struct *task, char *buffer)
+{
+	struct pt_regs *regs;
+
+	regs = __KSTK_PTREGS(task);
+	buffer += sprintf(buffer, "task: %08lx, ksp: %08x\n",
+			  (unsigned long) task, task->thread.ksp);
+	buffer += sprintf(buffer, "User PSW : %08lx %08lx\n",
+			  (unsigned long) regs->psw.mask, 
+			  (unsigned long) regs->psw.addr);
+	buffer += sprintf(buffer, "User GPRS: %08x %08x %08x %08x\n",
+			  regs->gprs[0], regs->gprs[1],
+			  regs->gprs[2], regs->gprs[3]);
+	buffer += sprintf(buffer, "           %08x %08x %08x %08x\n",
+			  regs->gprs[4], regs->gprs[5],
+			  regs->gprs[6], regs->gprs[7]);
+	buffer += sprintf(buffer, "           %08x %08x %08x %08x\n",
+			  regs->gprs[8], regs->gprs[9],
+			  regs->gprs[10], regs->gprs[11]);
+	buffer += sprintf(buffer, "           %08x %08x %08x %08x\n",
+			  regs->gprs[12], regs->gprs[13],
+			  regs->gprs[14], regs->gprs[15]);
+	buffer += sprintf(buffer, "User ACRS: %08x %08x %08x %08x\n",
+			  regs->acrs[0], regs->acrs[1],
+			  regs->acrs[2], regs->acrs[3]);
+	buffer += sprintf(buffer, "           %08x %08x %08x %08x\n",
+			  regs->acrs[4], regs->acrs[5],
+			  regs->acrs[6], regs->acrs[7]);
+	buffer += sprintf(buffer, "           %08x %08x %08x %08x\n",
+			  regs->acrs[8], regs->acrs[9],
+			  regs->acrs[10], regs->acrs[11]);
+	buffer += sprintf(buffer, "           %08x %08x %08x %08x\n",
+			  regs->acrs[12], regs->acrs[13],
+			  regs->acrs[14], regs->acrs[15]);
+	return buffer;
+}
+
 spinlock_t die_lock = SPIN_LOCK_UNLOCKED;
 
 void die(const char * str, struct pt_regs * regs, long err)
@@ -145,7 +340,7 @@
 #if CONFIG_REMOTE_DEBUG
 		if(gdb_stub_initialised)
 		{
-			gdb_stub_handle_exception((struct gdb_pt_regs *)regs,signal);
+			gdb_stub_handle_exception(regs, signal);
 			return 0;
 		}
 #endif
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/s390/lib/uaccess.S linux/arch/s390/lib/uaccess.S
--- linux.orig/arch/s390/lib/uaccess.S	Mon Jan  7 23:41:08 2002
+++ linux/arch/s390/lib/uaccess.S	Fri Dec 21 16:25:30 2001
@@ -6,46 +6,92 @@
  *    Copyright (C) 2000 IBM Deutschland Entwicklung GmbH, IBM Corporation
  *    Authors(s): Martin Schwidefsky (schwidefsky@de.ibm.com)
  *
- *  These functions have a non-standard call interface
+ *  These functions have standard call interface
  */
 
 #include <asm/lowcore.h>
 
         .text
         .align 4
-        .globl __copy_from_user_fixup
-__copy_from_user_fixup:
-        l     1,__LC_PGM_OLD_PSW+4
-        sll   4,1
-        srl   4,1
-0:      lhi   3,-4096
-        sll   3,1
-        srl   3,1
-        n     3,__LC_TRANS_EXC_ADDR
-        sr    3,4
-        bm    4(1)
-1:      mvcle 2,4,0
-        b     4(1)
+        .globl __copy_from_user_asm
+__copy_from_user_asm:
+	lr	%r5,%r3
+	sacf	512
+0:	mvcle	%r2,%r4,0
+	jo	0b
+	sacf	0
+	lr	%r2,%r5
+	br	%r14
+1:	l	%r1,__LC_PGM_OLD_PSW+4
+        sll	%r4,1
+        srl	%r4,1
+2:      lhi	%r3,-4096
+        sll	%r3,1
+        srl	%r3,1
+        n	%r3,__LC_TRANS_EXC_ADDR
+        sr	%r3,%r4
+        bm	4(%r1)
+3:      mvcle	%r2,%r4,0
+        b	4(%r1)
         .section __ex_table,"a"
-	.long 1b,0b
+	.long	0b,1b
+	.long	3b,2b
         .previous
 
         .align 4
         .text
-        .globl __copy_to_user_fixup
-__copy_to_user_fixup:
-        l     1,__LC_PGM_OLD_PSW+4
-        sll   4,1
-        srl   4,1
-0:      lhi   5,-4096
-        sll   5,1
-        srl   5,1
-        n     5,__LC_TRANS_EXC_ADDR
-        sr    5,4
-        bm    4(1)
-1:      mvcle 4,2,0
-        b     4(1)
+        .globl __copy_to_user_asm
+__copy_to_user_asm:
+	lr	%r5,%r3
+	sacf	512
+0:	mvcle	%r4,%r2,0
+	jo	0b
+	sacf	0
+	lr	%r2,%r3
+	br	%r14
+1:	l	%r1,__LC_PGM_OLD_PSW+4
+        sll	%r4,1
+        srl	%r4,1
+2:      lhi	%r5,-4096
+        sll	%r5,1
+        srl	%r5,1
+        n	%r5,__LC_TRANS_EXC_ADDR
+        sr	%r5,%r4
+        bm	4(%r1)
+3:      mvcle	%r4,%r2,0
+        b	4(%r1)
         .section __ex_table,"a"
-        .long 1b,0b
+	.long   0b,1b
+        .long	3b,2b
+        .previous
+
+        .align 4
+        .text
+        .globl __clear_user_asm
+__clear_user_asm:
+	lr	%r4,%r2
+	lr	%r5,%r3
+	sr	%r2,%r2
+	sr	%r3,%r3
+	sacf	512
+0:	mvcle	%r4,%r2,0
+	jo	0b
+	sacf	0
+	lr	%r2,%r3
+	br	%r14
+1:	l	%r1,__LC_PGM_OLD_PSW+4
+        sll	%r4,1
+        srl	%r4,1
+2:      lhi	%r5,-4096
+        sll	%r5,1
+        srl	%r5,1
+        n	%r5,__LC_TRANS_EXC_ADDR
+        sr	%r5,%r4
+        bm	4(%r1)
+3:      mvcle	%r4,%r2,0
+        b	4(%r1)
+        .section __ex_table,"a"
+	.long   0b,1b
+        .long	3b,2b
         .previous
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/s390/math-emu/math.c linux/arch/s390/math-emu/math.c
--- linux.orig/arch/s390/math-emu/math.c	Mon Jan  7 23:41:08 2002
+++ linux/arch/s390/math-emu/math.c	Fri Dec 21 16:25:30 2001
@@ -96,7 +96,7 @@
                         return SIGSEGV; \
         } while (0)
 
-static void display_emulation_not_implemented(char *instr)
+static void display_emulation_not_implemented(struct pt_regs *regs, char *instr)
 {
         struct pt_regs *regs;
         __u16 *location;
@@ -105,7 +105,6 @@
         if(sysctl_ieee_emulation_warnings)
 #endif
         {
-                regs = current->thread.regs;
                 location = (__u16 *)(regs->psw.addr-S390_lowcore.pgm_ilc);
                 printk("%s ieee fpu instruction not emulated "
                        "process name: %s pid: %d \n",
@@ -116,10 +115,9 @@
         }
 }
 
-static inline void emu_set_CC (int cc)
+static inline void emu_set_CC (struct pt_regs *regs, int cc)
 {
-        current->thread.regs->psw.mask = 
-		(current->thread.regs->psw.mask & 0xFFFFCFFF) | ((cc&3) << 12);
+        regs->psw.mask = (regs->psw.mask & 0xFFFFCFFF) | ((cc&3) << 12);
 }
 
 /*
@@ -129,24 +127,24 @@
  *  2 : Result is greater than zero
  *  3 : Result is NaN or INF
  */
-static inline void emu_set_CC_cs(int class, int sign)
+static inline void emu_set_CC_cs(struct pt_regs *regs, int class, int sign)
 {
         switch (class) {
         case FP_CLS_NORMAL:
         case FP_CLS_INF:
-                emu_set_CC(sign ? 1 : 2);
+                emu_set_CC(regs, sign ? 1 : 2);
                 break;
         case FP_CLS_ZERO:
-                emu_set_CC(0);
+                emu_set_CC(regs, 0);
                 break;
         case FP_CLS_NAN:
-                emu_set_CC(3);
+                emu_set_CC(regs, 3);
                 break;
         }
 }
 
 /* Add long double */
-static int emu_axbr (int rx, int ry) {
+static int emu_axbr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_Q(QA); FP_DECL_Q(QB); FP_DECL_Q(QR);
         FP_DECL_EX;
 	mathemu_ldcv cvt;
@@ -163,12 +161,12 @@
         FP_PACK_QP(&cvt.ld, QR);
         current->thread.fp_regs.fprs[rx].ui = cvt.w.high;
         current->thread.fp_regs.fprs[rx+2].ui = cvt.w.low;
-        emu_set_CC_cs(QR_c, QR_s);
+        emu_set_CC_cs(regs, QR_c, QR_s);
         return _fex;
 }
 
 /* Add double */
-static int emu_adbr (int rx, int ry) {
+static int emu_adbr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_D(DA); FP_DECL_D(DB); FP_DECL_D(DR);
         FP_DECL_EX;
         int mode;
@@ -178,12 +176,12 @@
         FP_UNPACK_DP(DB, &current->thread.fp_regs.fprs[ry].d);
         FP_ADD_D(DR, DA, DB);
 	FP_PACK_DP(&current->thread.fp_regs.fprs[rx].d, DR);
-        emu_set_CC_cs(DR_c, DR_s);
+        emu_set_CC_cs(regs, DR_c, DR_s);
         return _fex;
 }
 
 /* Add double */
-static int emu_adb (int rx, double *val) {
+static int emu_adb (struct pt_regs *regs, int rx, double *val) {
         FP_DECL_D(DA); FP_DECL_D(DB); FP_DECL_D(DR);
         FP_DECL_EX;
         int mode;
@@ -193,12 +191,12 @@
         FP_UNPACK_DP(DB, val);
         FP_ADD_D(DR, DA, DB);
 	FP_PACK_DP(&current->thread.fp_regs.fprs[rx].d, DR);
-        emu_set_CC_cs(DR_c, DR_s);
+        emu_set_CC_cs(regs, DR_c, DR_s);
         return _fex;
 }
 
 /* Add float */
-static int emu_aebr (int rx, int ry) {
+static int emu_aebr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_S(SA); FP_DECL_S(SB); FP_DECL_S(SR);
         FP_DECL_EX;
         int mode;
@@ -208,12 +206,12 @@
         FP_UNPACK_SP(SB, &current->thread.fp_regs.fprs[ry].f);
         FP_ADD_S(SR, SA, SB);
 	FP_PACK_SP(&current->thread.fp_regs.fprs[rx].f, SR);
-        emu_set_CC_cs(SR_c, SR_s);
+        emu_set_CC_cs(regs, SR_c, SR_s);
         return _fex;
 }
 
 /* Add float */
-static int emu_aeb (int rx, float *val) {
+static int emu_aeb (struct pt_regs *regs, int rx, float *val) {
         FP_DECL_S(SA); FP_DECL_S(SB); FP_DECL_S(SR);
         FP_DECL_EX;
         int mode;
@@ -223,12 +221,12 @@
         FP_UNPACK_SP(SB, val);
         FP_ADD_S(SR, SA, SB);
 	FP_PACK_SP(&current->thread.fp_regs.fprs[rx].f, SR);
-        emu_set_CC_cs(SR_c, SR_s);
+        emu_set_CC_cs(regs, SR_c, SR_s);
         return _fex;
 }
 
 /* Compare long double */
-static int emu_cxbr (int rx, int ry) {
+static int emu_cxbr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_Q(QA); FP_DECL_Q(QB);
 	mathemu_ldcv cvt;
         int IR;
@@ -244,12 +242,12 @@
          * IR == -1 if DA < DB, IR == 0 if DA == DB,
          * IR == 1 if DA > DB and IR == 3 if unorderded
          */
-        emu_set_CC((IR == -1) ? 1 : (IR == 1) ? 2 : IR);
+        emu_set_CC(regs, (IR == -1) ? 1 : (IR == 1) ? 2 : IR);
         return 0;
 }
 
 /* Compare double */
-static int emu_cdbr (int rx, int ry) {
+static int emu_cdbr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_D(DA); FP_DECL_D(DB);
         int IR;
 
@@ -260,12 +258,12 @@
          * IR == -1 if DA < DB, IR == 0 if DA == DB,
          * IR == 1 if DA > DB and IR == 3 if unorderded
          */
-        emu_set_CC((IR == -1) ? 1 : (IR == 1) ? 2 : IR);
+        emu_set_CC(regs, (IR == -1) ? 1 : (IR == 1) ? 2 : IR);
         return 0;
 }
 
 /* Compare double */
-static int emu_cdb (int rx, double *val) {
+static int emu_cdb (struct pt_regs *regs, int rx, double *val) {
         FP_DECL_D(DA); FP_DECL_D(DB);
         int IR;
 
@@ -276,12 +274,12 @@
          * IR == -1 if DA < DB, IR == 0 if DA == DB,
          * IR == 1 if DA > DB and IR == 3 if unorderded
          */
-        emu_set_CC((IR == -1) ? 1 : (IR == 1) ? 2 : IR);
+        emu_set_CC(regs, (IR == -1) ? 1 : (IR == 1) ? 2 : IR);
         return 0;
 }
 
 /* Compare float */
-static int emu_cebr (int rx, int ry) {
+static int emu_cebr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_S(SA); FP_DECL_S(SB);
         int IR;
 
@@ -292,12 +290,12 @@
          * IR == -1 if DA < DB, IR == 0 if DA == DB,
          * IR == 1 if DA > DB and IR == 3 if unorderded
          */
-        emu_set_CC((IR == -1) ? 1 : (IR == 1) ? 2 : IR);
+        emu_set_CC(regs, (IR == -1) ? 1 : (IR == 1) ? 2 : IR);
         return 0;
 }
 
 /* Compare float */
-static int emu_ceb (int rx, float *val) {
+static int emu_ceb (struct pt_regs *regs, int rx, float *val) {
         FP_DECL_S(SA); FP_DECL_S(SB);
         int IR;
 
@@ -308,12 +306,12 @@
          * IR == -1 if DA < DB, IR == 0 if DA == DB,
          * IR == 1 if DA > DB and IR == 3 if unorderded
          */
-        emu_set_CC((IR == -1) ? 1 : (IR == 1) ? 2 : IR);
+        emu_set_CC(regs, (IR == -1) ? 1 : (IR == 1) ? 2 : IR);
         return 0;
 }
 
 /* Compare and signal long double */
-static int emu_kxbr (int rx, int ry) {
+static int emu_kxbr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_Q(QA); FP_DECL_Q(QB);
         FP_DECL_EX;
 	mathemu_ldcv cvt;
@@ -330,14 +328,14 @@
          * IR == -1 if DA < DB, IR == 0 if DA == DB,
          * IR == 1 if DA > DB and IR == 3 if unorderded
          */
-        emu_set_CC((IR == -1) ? 1 : (IR == 1) ? 2 : IR);
+        emu_set_CC(regs, (IR == -1) ? 1 : (IR == 1) ? 2 : IR);
         if (IR == 3)
                 FP_SET_EXCEPTION (FP_EX_INVALID);
         return _fex;
 }
 
 /* Compare and signal double */
-static int emu_kdbr (int rx, int ry) {
+static int emu_kdbr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_D(DA); FP_DECL_D(DB);
         FP_DECL_EX;
         int IR;
@@ -349,14 +347,14 @@
          * IR == -1 if DA < DB, IR == 0 if DA == DB,
          * IR == 1 if DA > DB and IR == 3 if unorderded
          */
-        emu_set_CC((IR == -1) ? 1 : (IR == 1) ? 2 : IR);
+        emu_set_CC(regs, (IR == -1) ? 1 : (IR == 1) ? 2 : IR);
         if (IR == 3)
                 FP_SET_EXCEPTION (FP_EX_INVALID);
         return _fex;
 }
 
 /* Compare and signal double */
-static int emu_kdb (int rx, double *val) {
+static int emu_kdb (struct pt_regs *regs, int rx, double *val) {
         FP_DECL_D(DA); FP_DECL_D(DB);
         FP_DECL_EX;
         int IR;
@@ -368,14 +366,14 @@
          * IR == -1 if DA < DB, IR == 0 if DA == DB,
          * IR == 1 if DA > DB and IR == 3 if unorderded
          */
-        emu_set_CC((IR == -1) ? 1 : (IR == 1) ? 2 : IR);
+        emu_set_CC(regs, (IR == -1) ? 1 : (IR == 1) ? 2 : IR);
         if (IR == 3)
                 FP_SET_EXCEPTION (FP_EX_INVALID);
         return _fex;
 }
 
 /* Compare and signal float */
-static int emu_kebr (int rx, int ry) {
+static int emu_kebr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_S(SA); FP_DECL_S(SB);
         FP_DECL_EX;
         int IR;
@@ -387,14 +385,14 @@
          * IR == -1 if DA < DB, IR == 0 if DA == DB,
          * IR == 1 if DA > DB and IR == 3 if unorderded
          */
-        emu_set_CC((IR == -1) ? 1 : (IR == 1) ? 2 : IR);
+        emu_set_CC(regs, (IR == -1) ? 1 : (IR == 1) ? 2 : IR);
         if (IR == 3)
                 FP_SET_EXCEPTION (FP_EX_INVALID);
         return _fex;
 }
 
 /* Compare and signal float */
-static int emu_keb (int rx, float *val) {
+static int emu_keb (struct pt_regs *regs, int rx, float *val) {
         FP_DECL_S(SA); FP_DECL_S(SB);
         FP_DECL_EX;
         int IR;
@@ -406,14 +404,14 @@
          * IR == -1 if DA < DB, IR == 0 if DA == DB,
          * IR == 1 if DA > DB and IR == 3 if unorderded
          */
-        emu_set_CC((IR == -1) ? 1 : (IR == 1) ? 2 : IR);
+        emu_set_CC(regs, (IR == -1) ? 1 : (IR == 1) ? 2 : IR);
         if (IR == 3)
                 FP_SET_EXCEPTION (FP_EX_INVALID);
         return _fex;
 }
 
 /* Convert from fixed long double */
-static int emu_cxfbr (int rx, int ry) {
+static int emu_cxfbr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_Q(QR);
         FP_DECL_EX;
 	mathemu_ldcv cvt;
@@ -421,7 +419,7 @@
         int mode;
 
 	mode = current->thread.fp_regs.fpc & 3;
-        si = current->thread.regs->gprs[ry];
+        si = regs->gprs[ry];
         FP_FROM_INT_Q(QR, si, 32, int);
         FP_PACK_QP(&cvt.ld, QR);
         current->thread.fp_regs.fprs[rx].ui = cvt.w.high;
@@ -430,35 +428,35 @@
 }
 
 /* Convert from fixed double */
-static int emu_cdfbr (int rx, int ry) {
+static int emu_cdfbr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_D(DR);
         FP_DECL_EX;
         __s32 si;
         int mode;
 
 	mode = current->thread.fp_regs.fpc & 3;
-        si = current->thread.regs->gprs[ry];
+        si = regs->gprs[ry];
         FP_FROM_INT_D(DR, si, 32, int);
         FP_PACK_DP(&current->thread.fp_regs.fprs[rx].d, DR);
         return _fex;
 }
 
 /* Convert from fixed float */
-static int emu_cefbr (int rx, int ry) {
+static int emu_cefbr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_S(SR);
         FP_DECL_EX;
         __s32 si;
         int mode;
 
 	mode = current->thread.fp_regs.fpc & 3;
-        si = current->thread.regs->gprs[ry];
+        si = regs->gprs[ry];
         FP_FROM_INT_S(SR, si, 32, int);
         FP_PACK_SP(&current->thread.fp_regs.fprs[rx].f, SR);
         return _fex;
 }
 
 /* Convert to fixed long double */
-static int emu_cfxbr (int rx, int ry, int mask) {
+static int emu_cfxbr (struct pt_regs *regs, int rx, int ry, int mask) {
         FP_DECL_Q(QA);
         FP_DECL_EX;
 	mathemu_ldcv cvt;
@@ -475,13 +473,13 @@
         cvt.w.low = current->thread.fp_regs.fprs[ry+2].ui;
         FP_UNPACK_QP(QA, &cvt.ld);
         FP_TO_INT_ROUND_Q(si, QA, 32, 1);
-        current->thread.regs->gprs[rx] = si;
-        emu_set_CC_cs(QA_c, QA_s);
+        regs->gprs[rx] = si;
+        emu_set_CC_cs(regs, QA_c, QA_s);
         return _fex;
 }
 
 /* Convert to fixed double */
-static int emu_cfdbr (int rx, int ry, int mask) {
+static int emu_cfdbr (struct pt_regs *regs, int rx, int ry, int mask) {
         FP_DECL_D(DA);
         FP_DECL_EX;
         __s32 si;
@@ -495,13 +493,13 @@
 		mode = mask - 4;
         FP_UNPACK_DP(DA, &current->thread.fp_regs.fprs[ry].d);
         FP_TO_INT_ROUND_D(si, DA, 32, 1);
-        current->thread.regs->gprs[rx] = si;
-        emu_set_CC_cs(DA_c, DA_s);
+        regs->gprs[rx] = si;
+        emu_set_CC_cs(regs, DA_c, DA_s);
         return _fex;
 }
 
 /* Convert to fixed float */
-static int emu_cfebr (int rx, int ry, int mask) {
+static int emu_cfebr (struct pt_regs *regs, int rx, int ry, int mask) {
         FP_DECL_S(SA);
         FP_DECL_EX;
         __s32 si;
@@ -515,13 +513,13 @@
 		mode = mask - 4;
         FP_UNPACK_SP(SA, &current->thread.fp_regs.fprs[ry].f);
         FP_TO_INT_ROUND_S(si, SA, 32, 1);
-        current->thread.regs->gprs[rx] = si;
-        emu_set_CC_cs(SA_c, SA_s);
+        regs->gprs[rx] = si;
+        emu_set_CC_cs(regs, SA_c, SA_s);
         return _fex;
 }
 
 /* Divide long double */
-static int emu_dxbr (int rx, int ry) {
+static int emu_dxbr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_Q(QA); FP_DECL_Q(QB); FP_DECL_Q(QR);
         FP_DECL_EX;
 	mathemu_ldcv cvt;
@@ -542,7 +540,7 @@
 }
 
 /* Divide double */
-static int emu_ddbr (int rx, int ry) {
+static int emu_ddbr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_D(DA); FP_DECL_D(DB); FP_DECL_D(DR);
         FP_DECL_EX;
         int mode;
@@ -556,7 +554,7 @@
 }
 
 /* Divide double */
-static int emu_ddb (int rx, double *val) {
+static int emu_ddb (struct pt_regs *regs, int rx, double *val) {
         FP_DECL_D(DA); FP_DECL_D(DB); FP_DECL_D(DR);
         FP_DECL_EX;
         int mode;
@@ -570,7 +568,7 @@
 }
 
 /* Divide float */
-static int emu_debr (int rx, int ry) {
+static int emu_debr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_S(SA); FP_DECL_S(SB); FP_DECL_S(SR);
         FP_DECL_EX;
         int mode;
@@ -584,7 +582,7 @@
 }
 
 /* Divide float */
-static int emu_deb (int rx, float *val) {
+static int emu_deb (struct pt_regs *regs, int rx, float *val) {
         FP_DECL_S(SA); FP_DECL_S(SB); FP_DECL_S(SR);
         FP_DECL_EX;
         int mode;
@@ -598,25 +596,25 @@
 }
 
 /* Divide to integer double */
-static int emu_didbr (int rx, int ry, int mask) {
-        display_emulation_not_implemented("didbr");
+static int emu_didbr (struct pt_regs *regs, int rx, int ry, int mask) {
+        display_emulation_not_implemented(regs, "didbr");
         return 0;
 }
 
 /* Divide to integer float */
-static int emu_diebr (int rx, int ry, int mask) {
-        display_emulation_not_implemented("diebr");
+static int emu_diebr (struct pt_regs *regs, int rx, int ry, int mask) {
+        display_emulation_not_implemented(regs, "diebr");
         return 0;
 }
 
 /* Extract fpc */
-static int emu_efpc (int rx, int ry) {
-        current->thread.regs->gprs[rx] = current->thread.fp_regs.fpc;
+static int emu_efpc (struct pt_regs *regs, int rx, int ry) {
+        regs->gprs[rx] = current->thread.fp_regs.fpc;
         return 0;
 }
 
 /* Load and test long double */
-static int emu_ltxbr (int rx, int ry) {
+static int emu_ltxbr (struct pt_regs *regs, int rx, int ry) {
         s390_fp_regs *fp_regs = &current->thread.fp_regs;
 	mathemu_ldcv cvt;
         FP_DECL_Q(QA);
@@ -627,36 +625,36 @@
         FP_UNPACK_QP(QA, &cvt.ld);
         fp_regs->fprs[rx].ui = fp_regs->fprs[ry].ui;
         fp_regs->fprs[rx+2].ui = fp_regs->fprs[ry+2].ui;
-        emu_set_CC_cs(QA_c, QA_s);
+        emu_set_CC_cs(regs, QA_c, QA_s);
         return _fex;
 }
 
 /* Load and test double */
-static int emu_ltdbr (int rx, int ry) {
+static int emu_ltdbr (struct pt_regs *regs, int rx, int ry) {
         s390_fp_regs *fp_regs = &current->thread.fp_regs;
         FP_DECL_D(DA);
         FP_DECL_EX;
 
         FP_UNPACK_DP(DA, &fp_regs->fprs[ry].d);
         fp_regs->fprs[rx].ui = fp_regs->fprs[ry].ui;
-        emu_set_CC_cs(DA_c, DA_s);
+        emu_set_CC_cs(regs, DA_c, DA_s);
         return _fex;
 }
 
 /* Load and test double */
-static int emu_ltebr (int rx, int ry) {
+static int emu_ltebr (struct pt_regs *regs, int rx, int ry) {
         s390_fp_regs *fp_regs = &current->thread.fp_regs;
         FP_DECL_S(SA);
         FP_DECL_EX;
 
         FP_UNPACK_SP(SA, &fp_regs->fprs[ry].f);
         fp_regs->fprs[rx].ui = fp_regs->fprs[ry].ui;
-        emu_set_CC_cs(SA_c, SA_s);
+        emu_set_CC_cs(regs, SA_c, SA_s);
         return _fex;
 }
 
 /* Load complement long double */
-static int emu_lcxbr (int rx, int ry) {
+static int emu_lcxbr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_Q(QA); FP_DECL_Q(QR);
         FP_DECL_EX;
 	mathemu_ldcv cvt;
@@ -670,12 +668,12 @@
         FP_PACK_QP(&cvt.ld, QR);
         current->thread.fp_regs.fprs[rx].ui = cvt.w.high;
         current->thread.fp_regs.fprs[rx+2].ui = cvt.w.low;
-        emu_set_CC_cs(QR_c, QR_s);
+        emu_set_CC_cs(regs, QR_c, QR_s);
         return _fex;
 }
 
 /* Load complement double */
-static int emu_lcdbr (int rx, int ry) {
+static int emu_lcdbr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_D(DA); FP_DECL_D(DR);
         FP_DECL_EX;
         int mode;
@@ -684,12 +682,12 @@
         FP_UNPACK_DP(DA, &current->thread.fp_regs.fprs[ry].d);
 	FP_NEG_D(DR, DA);
 	FP_PACK_DP(&current->thread.fp_regs.fprs[rx].d, DR);
-        emu_set_CC_cs(DR_c, DR_s);
+        emu_set_CC_cs(regs, DR_c, DR_s);
         return _fex;
 }
 
 /* Load complement float */
-static int emu_lcebr (int rx, int ry) {
+static int emu_lcebr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_S(SA); FP_DECL_S(SR);
         FP_DECL_EX;
         int mode;
@@ -698,12 +696,12 @@
         FP_UNPACK_SP(SA, &current->thread.fp_regs.fprs[ry].f);
 	FP_NEG_S(SR, SA);
 	FP_PACK_SP(&current->thread.fp_regs.fprs[rx].f, SR);
-        emu_set_CC_cs(SR_c, SR_s);
+        emu_set_CC_cs(regs, SR_c, SR_s);
         return _fex;
 }
 
 /* Load floating point integer long double */
-static int emu_fixbr (int rx, int ry, int mask) {
+static int emu_fixbr (struct pt_regs *regs, int rx, int ry, int mask) {
         s390_fp_regs *fp_regs = &current->thread.fp_regs;
         FP_DECL_Q(QA);
         FP_DECL_EX;
@@ -728,7 +726,7 @@
 }
 
 /* Load floating point integer double */
-static int emu_fidbr (int rx, int ry, int mask) {
+static int emu_fidbr (struct pt_regs *regs, int rx, int ry, int mask) {
 	/* FIXME: rounding mode !! */
         s390_fp_regs *fp_regs = &current->thread.fp_regs;
         FP_DECL_D(DA);
@@ -749,7 +747,7 @@
 }
 
 /* Load floating point integer float */
-static int emu_fiebr (int rx, int ry, int mask) {
+static int emu_fiebr (struct pt_regs *regs, int rx, int ry, int mask) {
         s390_fp_regs *fp_regs = &current->thread.fp_regs;
         FP_DECL_S(SA);
         FP_DECL_EX;
@@ -769,7 +767,7 @@
 }
 
 /* Load lengthened double to long double */
-static int emu_lxdbr (int rx, int ry) {
+static int emu_lxdbr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_D(DA); FP_DECL_Q(QR);
 	FP_DECL_EX;
 	mathemu_ldcv cvt;
@@ -785,7 +783,7 @@
 }
 
 /* Load lengthened double to long double */
-static int emu_lxdb (int rx, double *val) {
+static int emu_lxdb (struct pt_regs *regs, int rx, double *val) {
         FP_DECL_D(DA); FP_DECL_Q(QR);
 	FP_DECL_EX;
 	mathemu_ldcv cvt;
@@ -801,7 +799,7 @@
 }
 
 /* Load lengthened float to long double */
-static int emu_lxebr (int rx, int ry) {
+static int emu_lxebr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_S(SA); FP_DECL_Q(QR);
 	FP_DECL_EX;
 	mathemu_ldcv cvt;
@@ -817,7 +815,7 @@
 }
 
 /* Load lengthened float to long double */
-static int emu_lxeb (int rx, float *val) {
+static int emu_lxeb (struct pt_regs *regs, int rx, float *val) {
         FP_DECL_S(SA); FP_DECL_Q(QR);
 	FP_DECL_EX;
 	mathemu_ldcv cvt;
@@ -833,7 +831,7 @@
 }
 
 /* Load lengthened float to double */
-static int emu_ldebr (int rx, int ry) {
+static int emu_ldebr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_S(SA); FP_DECL_D(DR);
 	FP_DECL_EX;
         int mode;
@@ -846,7 +844,7 @@
 }
 
 /* Load lengthened float to double */
-static int emu_ldeb (int rx, float *val) {
+static int emu_ldeb (struct pt_regs *regs, int rx, float *val) {
         FP_DECL_S(SA); FP_DECL_D(DR);
 	FP_DECL_EX;
         int mode;
@@ -859,7 +857,7 @@
 }
 
 /* Load negative long double */
-static int emu_lnxbr (int rx, int ry) {
+static int emu_lnxbr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_Q(QA); FP_DECL_Q(QR);
 	FP_DECL_EX;
 	mathemu_ldcv cvt;
@@ -880,12 +878,12 @@
 		current->thread.fp_regs.fprs[rx+2].ui =
 			current->thread.fp_regs.fprs[ry+2].ui;
 	}
-	emu_set_CC_cs(QR_c, QR_s);
+	emu_set_CC_cs(regs, QR_c, QR_s);
         return _fex;
 }
 
 /* Load negative double */
-static int emu_lndbr (int rx, int ry) {
+static int emu_lndbr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_D(DA); FP_DECL_D(DR);
 	FP_DECL_EX;
         int mode;
@@ -898,12 +896,12 @@
 	} else
 		current->thread.fp_regs.fprs[rx].ui =
 			current->thread.fp_regs.fprs[ry].ui;
-	emu_set_CC_cs(DR_c, DR_s);
+	emu_set_CC_cs(regs, DR_c, DR_s);
         return _fex;
 }
 
 /* Load negative float */
-static int emu_lnebr (int rx, int ry) {
+static int emu_lnebr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_S(SA); FP_DECL_S(SR);
 	FP_DECL_EX;
         int mode;
@@ -916,12 +914,12 @@
 	} else
 		current->thread.fp_regs.fprs[rx].ui =
 			current->thread.fp_regs.fprs[ry].ui;
-	emu_set_CC_cs(SR_c, SR_s);
+	emu_set_CC_cs(regs, SR_c, SR_s);
         return _fex;
 }
 
 /* Load positive long double */
-static int emu_lpxbr (int rx, int ry) {
+static int emu_lpxbr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_Q(QA); FP_DECL_Q(QR);
 	FP_DECL_EX;
 	mathemu_ldcv cvt;
@@ -942,12 +940,12 @@
 		current->thread.fp_regs.fprs[rx+2].ui =
 			current->thread.fp_regs.fprs[ry+2].ui;
 	}
-	emu_set_CC_cs(QR_c, QR_s);
+	emu_set_CC_cs(regs, QR_c, QR_s);
         return _fex;
 }
 
 /* Load positive double */
-static int emu_lpdbr (int rx, int ry) {
+static int emu_lpdbr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_D(DA); FP_DECL_D(DR);
 	FP_DECL_EX;
         int mode;
@@ -960,12 +958,12 @@
 	} else
 		current->thread.fp_regs.fprs[rx].ui =
 			current->thread.fp_regs.fprs[ry].ui;
-	emu_set_CC_cs(DR_c, DR_s);
+	emu_set_CC_cs(regs, DR_c, DR_s);
         return _fex;
 }
 
 /* Load positive float */
-static int emu_lpebr (int rx, int ry) {
+static int emu_lpebr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_S(SA); FP_DECL_S(SR);
 	FP_DECL_EX;
         int mode;
@@ -978,12 +976,12 @@
 	} else
 		current->thread.fp_regs.fprs[rx].ui =
 			current->thread.fp_regs.fprs[ry].ui;
-	emu_set_CC_cs(SR_c, SR_s);
+	emu_set_CC_cs(regs, SR_c, SR_s);
         return _fex;
 }
 
 /* Load rounded long double to double */
-static int emu_ldxbr (int rx, int ry) {
+static int emu_ldxbr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_Q(QA); FP_DECL_D(DR);
 	FP_DECL_EX;
 	mathemu_ldcv cvt;
@@ -999,7 +997,7 @@
 }
 
 /* Load rounded long double to float */
-static int emu_lexbr (int rx, int ry) {
+static int emu_lexbr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_Q(QA); FP_DECL_S(SR);
 	FP_DECL_EX;
 	mathemu_ldcv cvt;
@@ -1015,7 +1013,7 @@
 }
 
 /* Load rounded double to float */
-static int emu_ledbr (int rx, int ry) {
+static int emu_ledbr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_D(DA); FP_DECL_S(SR);
 	FP_DECL_EX;
         int mode;
@@ -1028,7 +1026,7 @@
 }
 
 /* Multiply long double */
-static int emu_mxbr (int rx, int ry) {
+static int emu_mxbr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_Q(QA); FP_DECL_Q(QB); FP_DECL_Q(QR);
         FP_DECL_EX;
 	mathemu_ldcv cvt;
@@ -1049,7 +1047,7 @@
 }
 
 /* Multiply double */
-static int emu_mdbr (int rx, int ry) {
+static int emu_mdbr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_D(DA); FP_DECL_D(DB); FP_DECL_D(DR);
         FP_DECL_EX;
         int mode;
@@ -1063,7 +1061,7 @@
 }
 
 /* Multiply double */
-static int emu_mdb (int rx, double *val) {
+static int emu_mdb (struct pt_regs *regs, int rx, double *val) {
         FP_DECL_D(DA); FP_DECL_D(DB); FP_DECL_D(DR);
         FP_DECL_EX;
         int mode;
@@ -1077,7 +1075,7 @@
 }
 
 /* Multiply double to long double */
-static int emu_mxdbr (int rx, int ry) {
+static int emu_mxdbr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_D(DA); FP_DECL_Q(QA); FP_DECL_Q(QB); FP_DECL_Q(QR);
 	FP_DECL_EX;
 	mathemu_ldcv cvt;
@@ -1096,7 +1094,7 @@
 }
 
 /* Multiply double to long double */
-static int emu_mxdb (int rx, long double *val) {
+static int emu_mxdb (struct pt_regs *regs, int rx, long double *val) {
         FP_DECL_Q(QA); FP_DECL_Q(QB); FP_DECL_Q(QR);
         FP_DECL_EX;
 	mathemu_ldcv cvt;
@@ -1115,7 +1113,7 @@
 }
 
 /* Multiply float */
-static int emu_meebr (int rx, int ry) {
+static int emu_meebr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_S(SA); FP_DECL_S(SB); FP_DECL_S(SR);
         FP_DECL_EX;
         int mode;
@@ -1129,7 +1127,7 @@
 }
 
 /* Multiply float */
-static int emu_meeb (int rx, float *val) {
+static int emu_meeb (struct pt_regs *regs, int rx, float *val) {
         FP_DECL_S(SA); FP_DECL_S(SB); FP_DECL_S(SR);
         FP_DECL_EX;
         int mode;
@@ -1143,7 +1141,7 @@
 }
 
 /* Multiply float to double */
-static int emu_mdebr (int rx, int ry) {
+static int emu_mdebr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_S(SA); FP_DECL_D(DA); FP_DECL_D(DB); FP_DECL_D(DR);
 	FP_DECL_EX;
         int mode;
@@ -1159,7 +1157,7 @@
 }
 
 /* Multiply float to double */
-static int emu_mdeb (int rx, float *val) {
+static int emu_mdeb (struct pt_regs *regs, int rx, float *val) {
         FP_DECL_S(SA); FP_DECL_D(DA); FP_DECL_D(DB); FP_DECL_D(DR);
 	FP_DECL_EX;
         int mode;
@@ -1175,7 +1173,7 @@
 }
 
 /* Multiply and add double */
-static int emu_madbr (int rx, int ry, int rz) {
+static int emu_madbr (struct pt_regs *regs, int rx, int ry, int rz) {
         FP_DECL_D(DA); FP_DECL_D(DB); FP_DECL_D(DC); FP_DECL_D(DR);
         FP_DECL_EX;
         int mode;
@@ -1191,7 +1189,7 @@
 }
 
 /* Multiply and add double */
-static int emu_madb (int rx, double *val, int rz) {
+static int emu_madb (struct pt_regs *regs, int rx, double *val, int rz) {
         FP_DECL_D(DA); FP_DECL_D(DB); FP_DECL_D(DC); FP_DECL_D(DR);
         FP_DECL_EX;
         int mode;
@@ -1207,7 +1205,7 @@
 }
 
 /* Multiply and add float */
-static int emu_maebr (int rx, int ry, int rz) {
+static int emu_maebr (struct pt_regs *regs, int rx, int ry, int rz) {
         FP_DECL_S(SA); FP_DECL_S(SB); FP_DECL_S(SC); FP_DECL_S(SR);
         FP_DECL_EX;
         int mode;
@@ -1223,7 +1221,7 @@
 }
 
 /* Multiply and add float */
-static int emu_maeb (int rx, float *val, int rz) {
+static int emu_maeb (struct pt_regs *regs, int rx, float *val, int rz) {
         FP_DECL_S(SA); FP_DECL_S(SB); FP_DECL_S(SC); FP_DECL_S(SR);
         FP_DECL_EX;
         int mode;
@@ -1239,7 +1237,7 @@
 }
 
 /* Multiply and subtract double */
-static int emu_msdbr (int rx, int ry, int rz) {
+static int emu_msdbr (struct pt_regs *regs, int rx, int ry, int rz) {
         FP_DECL_D(DA); FP_DECL_D(DB); FP_DECL_D(DC); FP_DECL_D(DR);
         FP_DECL_EX;
         int mode;
@@ -1255,7 +1253,7 @@
 }
 
 /* Multiply and subtract double */
-static int emu_msdb (int rx, double *val, int rz) {
+static int emu_msdb (struct pt_regs *regs, int rx, double *val, int rz) {
         FP_DECL_D(DA); FP_DECL_D(DB); FP_DECL_D(DC); FP_DECL_D(DR);
         FP_DECL_EX;
         int mode;
@@ -1271,7 +1269,7 @@
 }
 
 /* Multiply and subtract float */
-static int emu_msebr (int rx, int ry, int rz) {
+static int emu_msebr (struct pt_regs *regs, int rx, int ry, int rz) {
         FP_DECL_S(SA); FP_DECL_S(SB); FP_DECL_S(SC); FP_DECL_S(SR);
         FP_DECL_EX;
         int mode;
@@ -1287,7 +1285,7 @@
 }
 
 /* Multiply and subtract float */
-static int emu_mseb (int rx, float *val, int rz) {
+static int emu_mseb (struct pt_regs *regs, int rx, float *val, int rz) {
         FP_DECL_S(SA); FP_DECL_S(SB); FP_DECL_S(SC); FP_DECL_S(SR);
         FP_DECL_EX;
         int mode;
@@ -1303,10 +1301,10 @@
 }
 
 /* Set floating point control word */
-static int emu_sfpc (int rx, int ry) {
+static int emu_sfpc (struct pt_regs *regs, int rx, int ry) {
         __u32 temp;
 
-        temp = current->thread.regs->gprs[rx];
+        temp = regs->gprs[rx];
         if ((temp & ~FPC_VALID_MASK) != 0)
 		return SIGILL;
 	current->thread.fp_regs.fpc = temp;
@@ -1314,7 +1312,7 @@
 }
 
 /* Square root long double */
-static int emu_sqxbr (int rx, int ry) {
+static int emu_sqxbr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_Q(QA); FP_DECL_Q(QR);
         FP_DECL_EX;
 	mathemu_ldcv cvt;
@@ -1328,12 +1326,12 @@
         FP_PACK_QP(&cvt.ld, QR);
         current->thread.fp_regs.fprs[rx].ui = cvt.w.high;
         current->thread.fp_regs.fprs[rx+2].ui = cvt.w.low;
-        emu_set_CC_cs(QR_c, QR_s);
+        emu_set_CC_cs(regs, QR_c, QR_s);
         return _fex;
 }
 
 /* Square root double */
-static int emu_sqdbr (int rx, int ry) {
+static int emu_sqdbr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_D(DA); FP_DECL_D(DR);
         FP_DECL_EX;
         int mode;
@@ -1342,12 +1340,12 @@
         FP_UNPACK_DP(DA, &current->thread.fp_regs.fprs[ry].d);
 	FP_SQRT_D(DR, DA);
 	FP_PACK_DP(&current->thread.fp_regs.fprs[rx].d, DR);
-        emu_set_CC_cs(DR_c, DR_s);
+        emu_set_CC_cs(regs, DR_c, DR_s);
         return _fex;
 }
 
 /* Square root double */
-static int emu_sqdb (int rx, double *val) {
+static int emu_sqdb (struct pt_regs *regs, int rx, double *val) {
         FP_DECL_D(DA); FP_DECL_D(DR);
         FP_DECL_EX;
         int mode;
@@ -1356,12 +1354,12 @@
         FP_UNPACK_DP(DA, val);
 	FP_SQRT_D(DR, DA);
 	FP_PACK_DP(&current->thread.fp_regs.fprs[rx].d, DR);
-        emu_set_CC_cs(DR_c, DR_s);
+        emu_set_CC_cs(regs, DR_c, DR_s);
         return _fex;
 }
 
 /* Square root float */
-static int emu_sqebr (int rx, int ry) {
+static int emu_sqebr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_S(SA); FP_DECL_S(SR);
         FP_DECL_EX;
         int mode;
@@ -1370,12 +1368,12 @@
         FP_UNPACK_SP(SA, &current->thread.fp_regs.fprs[ry].f);
 	FP_SQRT_S(SR, SA);
 	FP_PACK_SP(&current->thread.fp_regs.fprs[rx].f, SR);
-        emu_set_CC_cs(SR_c, SR_s);
+        emu_set_CC_cs(regs, SR_c, SR_s);
         return _fex;
 }
 
 /* Square root float */
-static int emu_sqeb (int rx, float *val) {
+static int emu_sqeb (struct pt_regs *regs, int rx, float *val) {
         FP_DECL_S(SA); FP_DECL_S(SR);
         FP_DECL_EX;
         int mode;
@@ -1384,12 +1382,12 @@
         FP_UNPACK_SP(SA, val);
 	FP_SQRT_S(SR, SA);
 	FP_PACK_SP(&current->thread.fp_regs.fprs[rx].f, SR);
-        emu_set_CC_cs(SR_c, SR_s);
+        emu_set_CC_cs(regs, SR_c, SR_s);
         return _fex;
 }
 
 /* Subtract long double */
-static int emu_sxbr (int rx, int ry) {
+static int emu_sxbr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_Q(QA); FP_DECL_Q(QB); FP_DECL_Q(QR);
         FP_DECL_EX;
 	mathemu_ldcv cvt;
@@ -1406,12 +1404,12 @@
         FP_PACK_QP(&cvt.ld, QR);
         current->thread.fp_regs.fprs[rx].ui = cvt.w.high;
         current->thread.fp_regs.fprs[rx+2].ui = cvt.w.low;
-        emu_set_CC_cs(QR_c, QR_s);
+        emu_set_CC_cs(regs, QR_c, QR_s);
         return _fex;
 }
 
 /* Subtract double */
-static int emu_sdbr (int rx, int ry) {
+static int emu_sdbr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_D(DA); FP_DECL_D(DB); FP_DECL_D(DR);
         FP_DECL_EX;
         int mode;
@@ -1421,12 +1419,12 @@
         FP_UNPACK_DP(DB, &current->thread.fp_regs.fprs[ry].d);
         FP_SUB_D(DR, DA, DB);
 	FP_PACK_DP(&current->thread.fp_regs.fprs[rx].d, DR);
-        emu_set_CC_cs(DR_c, DR_s);
+        emu_set_CC_cs(regs, DR_c, DR_s);
         return _fex;
 }
 
 /* Subtract double */
-static int emu_sdb (int rx, double *val) {
+static int emu_sdb (struct pt_regs *regs, int rx, double *val) {
         FP_DECL_D(DA); FP_DECL_D(DB); FP_DECL_D(DR);
         FP_DECL_EX;
         int mode;
@@ -1436,12 +1434,12 @@
         FP_UNPACK_DP(DB, val);
         FP_SUB_D(DR, DA, DB);
 	FP_PACK_DP(&current->thread.fp_regs.fprs[rx].d, DR);
-        emu_set_CC_cs(DR_c, DR_s);
+        emu_set_CC_cs(regs, DR_c, DR_s);
         return _fex;
 }
 
 /* Subtract float */
-static int emu_sebr (int rx, int ry) {
+static int emu_sebr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_S(SA); FP_DECL_S(SB); FP_DECL_S(SR);
         FP_DECL_EX;
         int mode;
@@ -1451,12 +1449,12 @@
         FP_UNPACK_SP(SB, &current->thread.fp_regs.fprs[ry].f);
         FP_SUB_S(SR, SA, SB);
 	FP_PACK_SP(&current->thread.fp_regs.fprs[rx].f, SR);
-        emu_set_CC_cs(SR_c, SR_s);
+        emu_set_CC_cs(regs, SR_c, SR_s);
         return _fex;
 }
 
 /* Subtract float */
-static int emu_seb (int rx, float *val) {
+static int emu_seb (struct pt_regs *regs, int rx, float *val) {
         FP_DECL_S(SA); FP_DECL_S(SB); FP_DECL_S(SR);
         FP_DECL_EX;
         int mode;
@@ -1466,12 +1464,12 @@
         FP_UNPACK_SP(SB, val);
         FP_SUB_S(SR, SA, SB);
 	FP_PACK_SP(&current->thread.fp_regs.fprs[rx].f, SR);
-        emu_set_CC_cs(SR_c, SR_s);
+        emu_set_CC_cs(regs, SR_c, SR_s);
         return _fex;
 }
 
 /* Test data class long double */
-static int emu_tcxb (int rx, long val) {
+static int emu_tcxb (struct pt_regs *regs, int rx, long val) {
         FP_DECL_Q(QA);
 	mathemu_ldcv cvt;
 	int bit;
@@ -1500,12 +1498,12 @@
 	}
 	if (!QA_s)
 		bit++;
-	emu_set_CC(((__u32) val >> bit) & 1);
+	emu_set_CC(regs, ((__u32) val >> bit) & 1);
         return 0;
 }
 
 /* Test data class double */
-static int emu_tcdb (int rx, long val) {
+static int emu_tcdb (struct pt_regs *regs, int rx, long val) {
         FP_DECL_D(DA);
 	int bit;
 
@@ -1531,12 +1529,12 @@
 	}
 	if (!DA_s)
 		bit++;
-	emu_set_CC(((__u32) val >> bit) & 1);
+	emu_set_CC(regs, ((__u32) val >> bit) & 1);
         return 0;
 }
 
 /* Test data class float */
-static int emu_tceb (int rx, long val) {
+static int emu_tceb (struct pt_regs *regs, int rx, long val) {
         FP_DECL_S(SA);
 	int bit;
 
@@ -1562,7 +1560,7 @@
 	}
 	if (!SA_s)
 		bit++;
-	emu_set_CC(((__u32) val >> bit) & 1);
+	emu_set_CC(regs, ((__u32) val >> bit) & 1);
         return 0;
 }
 
@@ -1666,8 +1664,9 @@
                 emu_store_regd(opcode[3] & 15);
                 emu_store_regd((opcode[3] & 15) + 2);
                 /* call the emulation function */
-                _fex = ((int (*)(int, int)) jump_table[opcode[1]])
-                        (opcode[3] >> 4, opcode[3] & 15);
+                _fex = ((int (*)(struct pt_regs *,int, int))
+			jump_table[opcode[1]])
+                        (regs, opcode[3] >> 4, opcode[3] & 15);
                 emu_load_regd((opcode[3] >> 4) & 15);
                 emu_load_regd(((opcode[3] >> 4) & 15) + 2);
                 emu_load_regd(opcode[3] & 15);
@@ -1677,8 +1676,9 @@
                 emu_store_regd((opcode[3] >> 4) & 15);
                 emu_store_regd(opcode[3] & 15);
                 /* call the emulation function */
-                _fex = ((int (*)(int, int)) jump_table[opcode[1]])
-                        (opcode[3] >> 4, opcode[3] & 15);
+                _fex = ((int (*)(struct pt_regs *, int, int))
+			jump_table[opcode[1]])
+                        (regs, opcode[3] >> 4, opcode[3] & 15);
                 emu_load_regd((opcode[3] >> 4) & 15);
                 emu_load_regd(opcode[3] & 15);
 		break;
@@ -1686,8 +1686,9 @@
                 emu_store_rege((opcode[3] >> 4) & 15);
                 emu_store_rege(opcode[3] & 15);
                 /* call the emulation function */
-                _fex = ((int (*)(int, int)) jump_table[opcode[1]])
-                        (opcode[3] >> 4, opcode[3] & 15);
+                _fex = ((int (*)(struct pt_regs *, int, int))
+			jump_table[opcode[1]])
+                        (regs, opcode[3] >> 4, opcode[3] & 15);
                 emu_load_rege((opcode[3] >> 4) & 15);
                 emu_load_rege(opcode[3] & 15);
 		break;
@@ -1699,8 +1700,9 @@
                 emu_store_regd(opcode[3] & 15);
                 emu_store_regd((opcode[3] & 15) + 2);
                 /* call the emulation function */
-                _fex = ((int (*)(int, int, int)) jump_table[opcode[1]])
-                        (opcode[3] >> 4, opcode[3] & 15, opcode[2] >> 4);
+                _fex = ((int (*)(struct pt_regs *, int, int, int))
+			jump_table[opcode[1]])
+                        (regs, opcode[3] >> 4, opcode[3] & 15, opcode[2] >> 4);
                 emu_load_regd((opcode[3] >> 4) & 15);
                 emu_load_regd(((opcode[3] >> 4) & 15) + 2);
                 emu_load_regd(opcode[3] & 15);
@@ -1711,8 +1713,9 @@
                 emu_store_regd((opcode[3] >> 4) & 15);
                 emu_store_regd(opcode[3] & 15);
                 /* call the emulation function */
-                _fex = ((int (*)(int, int, int)) jump_table[opcode[1]])
-                        (opcode[3] >> 4, opcode[3] & 15, opcode[2] >> 4);
+                _fex = ((int (*)(struct pt_regs *, int, int, int))
+			jump_table[opcode[1]])
+                        (regs, opcode[3] >> 4, opcode[3] & 15, opcode[2] >> 4);
                 emu_load_regd((opcode[2] >> 4) & 15);
                 emu_load_regd((opcode[3] >> 4) & 15);
                 emu_load_regd(opcode[3] & 15);
@@ -1722,8 +1725,9 @@
                 emu_store_rege((opcode[3] >> 4) & 15);
                 emu_store_rege(opcode[3] & 15);
                 /* call the emulation function */
-                _fex = ((int (*)(int, int, int)) jump_table[opcode[1]])
-                        (opcode[3] >> 4, opcode[3] & 15, opcode[2] >> 4);
+                _fex = ((int (*)(struct pt_regs *, int, int, int))
+			jump_table[opcode[1]])
+                        (regs, opcode[3] >> 4, opcode[3] & 15, opcode[2] >> 4);
                 emu_load_rege((opcode[2] >> 4) & 15);
                 emu_load_rege((opcode[3] >> 4) & 15);
                 emu_load_rege(opcode[3] & 15);
@@ -1732,21 +1736,24 @@
                 /* call the emulation function */
                 if (opcode[3] & 0x20)
 			return SIGILL;
-                _fex = ((int (*)(int, int)) jump_table[opcode[1]])
-                        (opcode[3] >> 4, opcode[3] & 15);
+                _fex = ((int (*)(struct pt_regs *, int, int))
+			jump_table[opcode[1]])
+                        (regs, opcode[3] >> 4, opcode[3] & 15);
                 emu_load_regd((opcode[3] >> 4) & 15);
                 emu_load_regd(((opcode[3] >> 4) & 15) + 2);
 		break;
         case 8: /* RRE format, cdfbr instruction */
                 /* call the emulation function */
-                _fex = ((int (*)(int, int)) jump_table[opcode[1]])
-                        (opcode[3] >> 4, opcode[3] & 15);
+                _fex = ((int (*)(struct pt_regs *, int, int))
+			jump_table[opcode[1]])
+                        (regs, opcode[3] >> 4, opcode[3] & 15);
                 emu_load_regd((opcode[3] >> 4) & 15);
 		break;
         case 9: /* RRE format, cefbr instruction */
                 /* call the emulation function */
-                _fex = ((int (*)(int, int)) jump_table[opcode[1]])
-                        (opcode[3] >> 4, opcode[3] & 15);
+                _fex = ((int (*)(struct pt_regs *, int, int))
+			jump_table[opcode[1]])
+                        (regs, opcode[3] >> 4, opcode[3] & 15);
                 emu_load_rege((opcode[3] >> 4) & 15);
 		break;
         case 10: /* RRF format, cfxbr instruction */
@@ -1758,8 +1765,9 @@
                 emu_store_regd(opcode[3] & 15);
                 emu_store_regd((opcode[3] & 15) + 2);
                 /* call the emulation function */
-                _fex = ((int (*)(int, int, int)) jump_table[opcode[1]])
-                        (opcode[3] >> 4, opcode[3] & 15, opcode[2] >> 4);
+                _fex = ((int (*)(struct pt_regs *, int, int, int))
+			jump_table[opcode[1]])
+                        (regs, opcode[3] >> 4, opcode[3] & 15, opcode[2] >> 4);
 		break;
         case 11: /* RRF format, cfdbr instruction */
                 if ((opcode[2] & 128) == 128 || (opcode[2] & 96) == 32)
@@ -1767,8 +1775,9 @@
 			return SIGILL;
                 emu_store_regd(opcode[3] & 15);
                 /* call the emulation function */
-                _fex = ((int (*)(int, int, int)) jump_table[opcode[1]])
-                        (opcode[3] >> 4, opcode[3] & 15, opcode[2] >> 4);
+                _fex = ((int (*)(struct pt_regs *, int, int, int))
+			jump_table[opcode[1]])
+                        (regs, opcode[3] >> 4, opcode[3] & 15, opcode[2] >> 4);
 		break;
         case 12: /* RRF format, cfebr instruction */
                 if ((opcode[2] & 128) == 128 || (opcode[2] & 96) == 32)
@@ -1776,8 +1785,9 @@
 			return SIGILL;
                 emu_store_rege(opcode[3] & 15);
                 /* call the emulation function */
-                _fex = ((int (*)(int, int, int)) jump_table[opcode[1]])
-                        (opcode[3] >> 4, opcode[3] & 15, opcode[2] >> 4);
+                _fex = ((int (*)(struct pt_regs *, int, int, int))
+			jump_table[opcode[1]])
+                        (regs, opcode[3] >> 4, opcode[3] & 15, opcode[2] >> 4);
 		break;
         case 13: /* RRE format, ldxbr & mdxbr instruction */
                 /* double store but long double load */
@@ -1786,8 +1796,9 @@
                 emu_store_regd((opcode[3] >> 4) & 15);
                 emu_store_regd(opcode[3]  & 15);
                 /* call the emulation function */
-                _fex = ((int (*)(int, int)) jump_table[opcode[1]])
-                        (opcode[3] >> 4, opcode[3] & 15);
+                _fex = ((int (*)(struct pt_regs *, int, int))
+			jump_table[opcode[1]])
+                        (regs, opcode[3] >> 4, opcode[3] & 15);
                 emu_load_regd((opcode[3] >> 4) & 15);
                 emu_load_regd(((opcode[3] >> 4) & 15) + 2);
 		break;
@@ -1798,8 +1809,9 @@
                 emu_store_rege((opcode[3] >> 4) & 15);
                 emu_store_rege(opcode[3]  & 15);
                 /* call the emulation function */
-                _fex = ((int (*)(int, int)) jump_table[opcode[1]])
-                        (opcode[3] >> 4, opcode[3] & 15);
+                _fex = ((int (*)(struct pt_regs *, int, int))
+			jump_table[opcode[1]])
+                        (regs, opcode[3] >> 4, opcode[3] & 15);
                 emu_load_regd((opcode[3] >> 4) & 15);
                 emu_load_regd(((opcode[3] >> 4) & 15) + 2);
 		break;
@@ -1808,8 +1820,9 @@
                 emu_store_rege((opcode[3] >> 4) & 15);
                 emu_store_rege(opcode[3]  & 15);
                 /* call the emulation function */
-                _fex = ((int (*)(int, int)) jump_table[opcode[1]])
-                        (opcode[3] >> 4, opcode[3] & 15);
+                _fex = ((int (*)(struct pt_regs *, int, int))
+			jump_table[opcode[1]])
+                        (regs, opcode[3] >> 4, opcode[3] & 15);
                 emu_load_regd((opcode[3] >> 4) & 15);
 		break;
         case 16: /* RRE format, ldxbr instruction */
@@ -1819,8 +1832,9 @@
                 emu_store_regd(opcode[3] & 15);
                 emu_store_regd((opcode[3] & 15) + 2);
                 /* call the emulation function */
-                _fex = ((int (*)(int, int)) jump_table[opcode[1]])
-                        (opcode[3] >> 4, opcode[3] & 15);
+                _fex = ((int (*)(struct pt_regs *, int, int))
+			jump_table[opcode[1]])
+                        (regs, opcode[3] >> 4, opcode[3] & 15);
                 emu_load_regd((opcode[3] >> 4) & 15);
                 break;
         case 17: /* RRE format, ldxbr instruction */
@@ -1830,22 +1844,25 @@
                 emu_store_regd(opcode[3] & 15);
                 emu_store_regd((opcode[3] & 15) + 2);
                 /* call the emulation function */
-                _fex = ((int (*)(int, int)) jump_table[opcode[1]])
-                        (opcode[3] >> 4, opcode[3] & 15);
+                _fex = ((int (*)(struct pt_regs *, int, int))
+			jump_table[opcode[1]])
+                        (regs, opcode[3] >> 4, opcode[3] & 15);
                 emu_load_rege((opcode[3] >> 4) & 15);
                 break;
         case 18: /* RRE format, ledbr instruction */
                 /* double store but float load */
                 emu_store_regd(opcode[3] & 15);
                 /* call the emulation function */
-                _fex = ((int (*)(int, int)) jump_table[opcode[1]])
-                        (opcode[3] >> 4, opcode[3] & 15);
+                _fex = ((int (*)(struct pt_regs *, int, int))
+			jump_table[opcode[1]])
+                        (regs, opcode[3] >> 4, opcode[3] & 15);
                 emu_load_rege((opcode[3] >> 4) & 15);
                 break;
         case 19: /* RRE format, efpc & sfpc instruction */
                 /* call the emulation function */
-                _fex = ((int (*)(int, int)) jump_table[opcode[1]])
-                        (opcode[3] >> 4, opcode[3] & 15);
+                _fex = ((int (*)(struct pt_regs *, int, int))
+			jump_table[opcode[1]])
+                        (regs, opcode[3] >> 4, opcode[3] & 15);
                 break;
         default: /* invalid operation */
                 return SIGILL;
@@ -1904,8 +1921,9 @@
                 dxb = (__u64 *) calc_addr(regs, opc >> 16, opc >> 12, opc);
                 mathemu_copy_from_user(&temp, dxb, 8);
                 /* call the emulation function */
-                _fex = ((int (*)(int, double *)) jump_table[opcode[5]])
-                        (opcode[1] >> 4, (double *) &temp);
+                _fex = ((int (*)(struct pt_regs *, int, double *))
+			jump_table[opcode[5]])
+                        (regs, opcode[1] >> 4, (double *) &temp);
                 emu_load_regd((opcode[1] >> 4) & 15);
                 break;
         }
@@ -1918,8 +1936,9 @@
                 dxb = (__u32 *) calc_addr(regs, opc >> 16, opc >> 12, opc);
                 mathemu_get_user(temp, dxb);
                 /* call the emulation function */
-                _fex = ((int (*)(int, float *)) jump_table[opcode[5]])
-                        (opcode[1] >> 4, (float *) &temp);
+                _fex = ((int (*)(struct pt_regs *, int, float *))
+			jump_table[opcode[5]])
+                        (regs, opcode[1] >> 4, (float *) &temp);
                 emu_load_rege((opcode[1] >> 4) & 15);
                 break;
         }
@@ -1933,8 +1952,9 @@
                 dxb = (__u64 *) calc_addr(regs, opc >> 16, opc >> 12, opc);
                 mathemu_copy_from_user(&temp, dxb, 8);
                 /* call the emulation function */
-                _fex = ((int (*)(int, double *, int)) jump_table[opcode[5]])
-                        (opcode[1] >> 4, (double *) &temp, opcode[4] >> 4);
+                _fex = ((int (*)(struct pt_regs *, int, double *, int))
+			jump_table[opcode[5]])
+                        (regs, opcode[1] >> 4, (double *) &temp, opcode[4] >> 4);
                 emu_load_regd((opcode[1] >> 4) & 15);
                 break;
         }
@@ -1948,8 +1968,9 @@
                 dxb = (__u32 *) calc_addr(regs, opc >> 16, opc >> 12, opc);
                 mathemu_get_user(temp, dxb);
                 /* call the emulation function */
-                _fex = ((int (*)(int, float *, int)) jump_table[opcode[5]])
-                        (opcode[1] >> 4, (float *) &temp, opcode[4] >> 4);
+                _fex = ((int (*)(struct pt_regs *, int, float *, int))
+			jump_table[opcode[5]])
+                        (regs, opcode[1] >> 4, (float *) &temp, opcode[4] >> 4);
                 emu_load_rege((opcode[4] >> 4) & 15);
                 break;
         }
@@ -1965,8 +1986,9 @@
                 dxb = (__u64 *) calc_addr(regs, opc >> 16, opc >> 12, opc);
                 mathemu_copy_from_user(&temp, dxb, 8);
                 /* call the emulation function */
-                _fex = ((int (*)(int, double *)) jump_table[opcode[5]])
-                        (opcode[1] >> 4, (double *) &temp);
+                _fex = ((int (*)(struct pt_regs *, int, double *))
+			jump_table[opcode[5]])
+                        (regs, opcode[1] >> 4, (double *) &temp);
                 emu_load_regd((opcode[1] >> 4) & 15);
                 emu_load_regd(((opcode[1] >> 4) & 15) + 2);
                 break;
@@ -1981,8 +2003,9 @@
                 dxb = (__u32 *) calc_addr(regs, opc >> 16, opc >> 12, opc);
                 mathemu_get_user(temp, dxb);
                 /* call the emulation function */
-                _fex = ((int (*)(int, float *)) jump_table[opcode[5]])
-                        (opcode[1] >> 4, (float *) &temp);
+                _fex = ((int (*)(struct pt_regs *, int, float *))
+			jump_table[opcode[5]])
+                        (regs, opcode[1] >> 4, (float *) &temp);
                 emu_load_regd((opcode[1] >> 4) & 15);
                 break;
         }
@@ -1998,8 +2021,9 @@
                 dxb = (__u32 *) calc_addr(regs, opc >> 16, opc >> 12, opc);
                 mathemu_get_user(temp, dxb);
                 /* call the emulation function */
-                _fex = ((int (*)(int, float *)) jump_table[opcode[5]])
-                        (opcode[1] >> 4, (float *) &temp);
+                _fex = ((int (*)(struct pt_regs *, int, float *))
+			jump_table[opcode[5]])
+                        (regs, opcode[1] >> 4, (float *) &temp);
                 emu_load_regd((opcode[1] >> 4) & 15);
                 emu_load_regd(((opcode[1] >> 4) & 15) + 2);
                 break;
@@ -2012,8 +2036,9 @@
                 opc = *((__u32 *) opcode);
                 dxb = (__u64) calc_addr(regs, opc >> 16, opc >> 12, opc);
                 /* call the emulation function */
-                _fex = ((int (*)(int, long)) jump_table[opcode[5]])
-                        (opcode[1] >> 4, dxb);
+                _fex = ((int (*)(struct pt_regs *, int, long))
+			jump_table[opcode[5]])
+                        (regs, opcode[1] >> 4, dxb);
                 break;
         }
         case 9: /* RXE format, RX address used as int value */ {
@@ -2024,8 +2049,9 @@
                 opc = *((__u32 *) opcode);
                 dxb = (__u64) calc_addr(regs, opc >> 16, opc >> 12, opc);
                 /* call the emulation function */
-                _fex = ((int (*)(int, long)) jump_table[opcode[5]])
-                        (opcode[1] >> 4, dxb);
+                _fex = ((int (*)(struct pt_regs *, int, long))
+			jump_table[opcode[5]])
+                        (regs, opcode[1] >> 4, dxb);
                 break;
         }
         case 10: /* RXE format, RX address used as int value */ {
@@ -2039,8 +2065,9 @@
                 opc = *((__u32 *) opcode);
                 dxb = (__u64) calc_addr(regs, opc >> 16, opc >> 12, opc);
                 /* call the emulation function */
-                _fex = ((int (*)(int, long)) jump_table[opcode[5]])
-                        (opcode[1] >> 4, dxb);
+                _fex = ((int (*)(struct pt_regs *, int, long))
+			jump_table[opcode[5]])
+                        (regs, opcode[1] >> 4, dxb);
                 break;
         }
         default: /* invalid operation */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/s390x/Makefile linux/arch/s390x/Makefile
--- linux.orig/arch/s390x/Makefile	Mon Jan  7 23:41:08 2002
+++ linux/arch/s390x/Makefile	Fri Dec 21 16:25:30 2001
@@ -55,6 +55,9 @@
 image: vmlinux 
 	@$(MAKEBOOT) image
 
+install: vmlinux
+	@$(MAKEBOOT) BOOTIMAGE=image install
+
 archclean:
 	@$(MAKEBOOT) clean
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/s390x/boot/Makefile linux/arch/s390x/boot/Makefile
--- linux.orig/arch/s390x/boot/Makefile	Mon Jan  7 23:41:08 2002
+++ linux/arch/s390x/boot/Makefile	Fri Dec 21 16:25:30 2001
@@ -35,3 +35,5 @@
 clean:
 	rm -f image listing iplfba.boot ipleckd.boot ipldump.boot
 
+install: $(CONFIGURE) $(BOOTIMAGE)
+	sh -x ./install.sh $(KERNELRELEASE) $(BOOTIMAGE) $(TOPDIR)/System.map $(TOPDIR)/Kerntypes "$(INSTALL_PATH)"
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/s390x/config.in linux/arch/s390x/config.in
--- linux.orig/arch/s390x/config.in	Mon Jan  7 23:41:08 2002
+++ linux/arch/s390x/config.in	Fri Dec 21 16:25:30 2001
@@ -71,9 +71,9 @@
 comment 'Kernel hacking'
 
 #bool 'Debug kmalloc/kfree' CONFIG_DEBUG_MALLOC
-if [ "$CONFIG_CTC" = "y" ]; then
-  bool 'Remote GDB kernel debugging' CONFIG_REMOTE_DEBUG
-fi
+#if [ "$CONFIG_CTC" = "y" ]; then
+#  bool 'Remote GDB kernel debugging' CONFIG_REMOTE_DEBUG
+#fi
 bool 'Magic SysRq key' CONFIG_MAGIC_SYSRQ
 endmenu
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/s390x/defconfig linux/arch/s390x/defconfig
--- linux.orig/arch/s390x/defconfig	Mon Jan  7 23:41:08 2002
+++ linux/arch/s390x/defconfig	Fri Dec 21 16:25:30 2001
@@ -6,7 +6,7 @@
 # CONFIG_MCA is not set
 CONFIG_RWSEM_GENERIC_SPINLOCK=y
 # CONFIG_RWSEM_XCHGADD_ALGORITHM is not set
-CONFIG_GENERIC_BUST_SPINLOCK=n
+# CONFIG_GENERIC_BUST_SPINLOCK is not set
 CONFIG_ARCH_S390=y
 CONFIG_ARCH_S390X=y
 
@@ -103,8 +103,8 @@
 #
 # S/390 tape hardware support
 #
-CONFIG_S390_TAPE_3490=y
-CONFIG_S390_TAPE_3480=y
+CONFIG_S390_TAPE_3490=m
+CONFIG_S390_TAPE_3480=m
 
 #
 # Network device drivers
@@ -150,6 +150,7 @@
 # CONFIG_IPV6_NETLINK is not set
 # CONFIG_KHTTPD is not set
 # CONFIG_ATM is not set
+# CONFIG_VLAN_8021Q is not set
 
 #
 #  
@@ -180,12 +181,12 @@
 # CONFIG_AUTOFS4_FS is not set
 # CONFIG_REISERFS_FS is not set
 # CONFIG_REISERFS_CHECK is not set
+# CONFIG_REISERFS_PROC_INFO is not set
 # CONFIG_ADFS_FS is not set
 # CONFIG_ADFS_FS_RW is not set
 # CONFIG_AFFS_FS is not set
 # CONFIG_HFS_FS is not set
 # CONFIG_BFS_FS is not set
-# CONFIG_CMS_FS is not set
 # CONFIG_EXT3_FS is not set
 # CONFIG_JBD is not set
 # CONFIG_JBD_DEBUG is not set
@@ -203,7 +204,7 @@
 # CONFIG_JOLIET is not set
 # CONFIG_ZISOFS is not set
 # CONFIG_MINIX_FS is not set
-# CONFIG_FREEVXFS_FS is not set
+# CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
 # CONFIG_NTFS_RW is not set
 # CONFIG_HPFS_FS is not set
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/s390x/kernel/debug.c linux/arch/s390x/kernel/debug.c
--- linux.orig/arch/s390x/kernel/debug.c	Mon Jan  7 23:41:08 2002
+++ linux/arch/s390x/kernel/debug.c	Fri Dec 21 16:25:30 2001
@@ -228,8 +228,10 @@
 	strncpy(rc->name, name, MIN(strlen(name), (DEBUG_MAX_PROCF_LEN - 1)));
 	rc->name[MIN(strlen(name), (DEBUG_MAX_PROCF_LEN - 1))] = 0;
 	memset(rc->views, 0, DEBUG_MAX_VIEWS * sizeof(struct debug_view *));
+#ifdef CONFIG_PROC_FS
 	memset(rc->proc_entries, 0 ,DEBUG_MAX_VIEWS *
 		sizeof(struct proc_dir_entry*));
+#endif /* CONFIG_PROC_FS */
 	atomic_set(&(rc->ref_count), 0);
 
 	return rc;
@@ -346,8 +348,10 @@
 	if (!db_info)
 		return;
 	if (atomic_dec_and_test(&db_info->ref_count)) {
+#ifdef DEBUG
 		printk(KERN_INFO "debug: freeing debug area %p (%s)\n",
 		       db_info, db_info->name);
+#endif
 		for (i = 0; i < DEBUG_MAX_VIEWS; i++) {
 			if (db_info->views[i] != NULL)
 				debug_delete_proc_dir_entry
@@ -541,14 +545,18 @@
 	debug_info_snapshot = debug_info_copy(debug_info);
 
 	if(!debug_info_snapshot){
+#ifdef DEBUG
 		printk(KERN_ERR "debug_open: debug_info_copy failed (out of mem)\n");
+#endif
 		rc = -ENOMEM;
 		goto out;
 	}
 
 	if ((file->private_data =
 	     kmalloc(sizeof(file_private_info_t), GFP_ATOMIC)) == 0) {
+#ifdef DEBUG
 		printk(KERN_ERR "debug_open: kmalloc failed\n");
+#endif
 		debug_info_free(debug_info_snapshot);	
 		rc = -ENOMEM;
 		goto out;
@@ -602,6 +610,7 @@
 {
 	struct proc_dir_entry *rc = NULL;
 
+#ifdef CONFIG_PROC_FS
 #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,98))
 	const char *fn = name;
 	int len;
@@ -634,6 +643,7 @@
 #endif
 
       out:
+#endif /* CONFIG_PROC_FS */
 	return rc;
 }
 
@@ -646,12 +656,14 @@
     (struct proc_dir_entry *root, struct proc_dir_entry *proc_entry) 
 {
 
+#ifdef CONFIG_PROC_FS
 #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,98))
 	proc_unregister(root, proc_entry->low_ino);
 	kfree(proc_entry);
 #else
 	remove_proc_entry(proc_entry->name, root);
 #endif
+#endif /* CONFIG_PROC_FS */
 }
 
 /*
@@ -677,9 +689,11 @@
 		goto out;
 	debug_register_view(rc, &debug_level_view);
         debug_register_view(rc, &debug_flush_view);
+#ifdef DEBUG
 	printk(KERN_INFO
 	       "debug: reserved %d areas of %d pages for debugging %s\n",
 	       nr_areas, 1 << page_order, rc->name);
+#endif
       out:
         if (rc == NULL){
 		printk(KERN_ERR "debug: debug_register failed for %s\n",name);
@@ -699,7 +713,9 @@
 	if (!id)
 		goto out;
 	down(&debug_lock);
+#ifdef DEBUG
 	printk(KERN_INFO "debug: unregistering %s\n", id->name);
+#endif
 	debug_info_put(id);
 	up(&debug_lock);
 
@@ -906,11 +922,13 @@
 
 	down(&debug_lock);
 	if (!initialized) {
+#ifdef CONFIG_PROC_FS
 		debug_proc_root_entry =
 		    debug_create_proc_dir_entry(&proc_root, DEBUG_DIR_ROOT,
 						S_IFDIR | S_IRUGO | S_IXUGO
 						| S_IWUSR | S_IWGRP, NULL,
 						NULL);
+#endif /* CONFIG_PROC_FS */
 		printk(KERN_INFO "debug: Initialization complete\n");
 		initialized = 1;
 	}
@@ -1271,7 +1289,9 @@
 #ifdef DEBUG
 	printk("debug_cleanup_module: \n");
 #endif
+#ifdef CONFIG_PROC_FS
 	debug_delete_proc_dir_entry(&proc_root, debug_proc_root_entry);
+#endif /* CONFIG_PROC_FS */
 	return;
 }
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/s390x/kernel/entry.S linux/arch/s390x/kernel/entry.S
--- linux.orig/arch/s390x/kernel/entry.S	Mon Jan  7 23:41:08 2002
+++ linux/arch/s390x/kernel/entry.S	Fri Dec 21 16:25:30 2001
@@ -79,7 +79,7 @@
 sigpending   = 16
 need_resched = 32
 tsk_ptrace   = 40
-processor    = 92
+processor    = 88
 
 /*
  * Register usage in interrupt handlers:
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/s390x/kernel/init_task.c linux/arch/s390x/kernel/init_task.c
--- linux.orig/arch/s390x/kernel/init_task.c	Mon Jan  7 23:41:08 2002
+++ linux/arch/s390x/kernel/init_task.c	Fri Dec 21 16:25:30 2001
@@ -12,7 +12,6 @@
 #include <asm/uaccess.h>
 #include <asm/pgtable.h>
 
-static struct vm_area_struct init_mmap = INIT_MMAP;
 static struct fs_struct init_fs = INIT_FS;
 static struct files_struct init_files = INIT_FILES;
 static struct signal_struct init_signals = INIT_SIGNALS;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/s390x/kernel/ioctl32.c linux/arch/s390x/kernel/ioctl32.c
--- linux.orig/arch/s390x/kernel/ioctl32.c	Mon Jan  7 23:41:08 2002
+++ linux/arch/s390x/kernel/ioctl32.c	Fri Dec 21 16:25:30 2001
@@ -24,6 +24,7 @@
 #include <linux/route.h>
 #include <linux/ext2_fs.h>
 #include <linux/hdreg.h>
+#include <linux/if_bonding.h>
 #include <asm/types.h>
 #include <asm/uaccess.h>
 #include <asm/dasd.h>
@@ -195,6 +196,58 @@
 out:
 	if(ifc.ifc_buf != NULL)
 		kfree (ifc.ifc_buf);
+	return err;
+}
+
+static int bond_ioctl(unsigned long fd, unsigned int cmd, unsigned long arg)
+{
+	struct ifreq ifr;
+	mm_segment_t old_fs;
+	int err, len;
+	u32 data;
+	
+	if (copy_from_user(&ifr, (struct ifreq32 *)arg, sizeof(struct ifreq32)))
+		return -EFAULT;
+	ifr.ifr_data = (__kernel_caddr_t)get_free_page(GFP_KERNEL);
+	if (!ifr.ifr_data)
+		return -EAGAIN;
+
+	switch (cmd) {
+	case SIOCBONDENSLAVE:
+	case SIOCBONDRELEASE:
+	case SIOCBONDSETHWADDR:
+	case SIOCBONDCHANGEACTIVE:
+		len = IFNAMSIZ * sizeof(char);
+		break;
+	case SIOCBONDSLAVEINFOQUERY:
+		len = sizeof(struct ifslave);
+		break;
+	case SIOCBONDINFOQUERY:
+		len = sizeof(struct ifbond);
+		break;
+	default:
+		err = -EINVAL;
+		goto out;
+	};
+
+	__get_user(data, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_data));
+	if (copy_from_user(ifr.ifr_data, (char *)A(data), len)) {
+		err = -EFAULT;
+		goto out;
+	}
+
+	old_fs = get_fs();
+	set_fs (KERNEL_DS);
+	err = sys_ioctl (fd, cmd, (unsigned long)&ifr);
+	set_fs (old_fs);
+	if (!err) {
+		len = copy_to_user((char *)A(data), ifr.ifr_data, len);
+		if (len)
+			err = -EFAULT;
+	}
+
+out:
+	free_page((unsigned long)ifr.ifr_data);
 	return err;
 }
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/s390x/kernel/linux32.c linux/arch/s390x/kernel/linux32.c
--- linux.orig/arch/s390x/kernel/linux32.c	Mon Jan  7 23:41:08 2002
+++ linux/arch/s390x/kernel/linux32.c	Fri Dec 21 16:25:30 2001
@@ -897,24 +897,24 @@
 	return sys32_fcntl(fd, cmd, arg);
 }
 
-struct mem_dqblk32 {
+struct dqblk32 {
+    __u32 dqb_bhardlimit;
+    __u32 dqb_bsoftlimit;
+    __u32 dqb_curblocks;
     __u32 dqb_ihardlimit;
     __u32 dqb_isoftlimit;
     __u32 dqb_curinodes;
-    __u32 dqb_bhardlimit;
-    __u32 dqb_bsoftlimit;
-    __u64 dqb_curspace;
     __kernel_time_t32 dqb_btime;
     __kernel_time_t32 dqb_itime;
 };
                                 
-extern asmlinkage long sys_quotactl(int cmd, const char *special, int id, __kernel_caddr_t addr);
+extern asmlinkage int sys_quotactl(int cmd, const char *special, int id, caddr_t addr);
 
 asmlinkage int sys32_quotactl(int cmd, const char *special, int id, unsigned long addr)
 {
 	int cmds = cmd >> SUBCMDSHIFT;
 	int err;
-	struct mem_dqblk d;
+	struct dqblk d;
 	mm_segment_t old_fs;
 	char *spec;
 	
@@ -924,32 +924,32 @@
 	case Q_SETQUOTA:
 	case Q_SETUSE:
 	case Q_SETQLIM:
-		if (copy_from_user (&d, (struct mem_dqblk32 *)addr,
-				    sizeof (struct mem_dqblk32)))
+		if (copy_from_user (&d, (struct dqblk32 *)addr,
+				    sizeof (struct dqblk32)))
 			return -EFAULT;
-		d.dqb_itime = ((struct mem_dqblk32 *)&d)->dqb_itime;
-		d.dqb_btime = ((struct mem_dqblk32 *)&d)->dqb_btime;
+		d.dqb_itime = ((struct dqblk32 *)&d)->dqb_itime;
+		d.dqb_btime = ((struct dqblk32 *)&d)->dqb_btime;
 		break;
 	default:
 		return sys_quotactl(cmd, special,
-				    id, (__kernel_caddr_t)addr);
+				    id, (caddr_t)addr);
 	}
 	spec = getname (special);
 	err = PTR_ERR(spec);
 	if (IS_ERR(spec)) return err;
 	old_fs = get_fs ();
 	set_fs (KERNEL_DS);
-	err = sys_quotactl(cmd, (const char *)spec, id, (__kernel_caddr_t)&d);
+	err = sys_quotactl(cmd, (const char *)spec, id, (caddr_t)&d);
 	set_fs (old_fs);
 	putname (spec);
 	if (err)
 		return err;
 	if (cmds == Q_GETQUOTA) {
 		__kernel_time_t b = d.dqb_btime, i = d.dqb_itime;
-		((struct mem_dqblk32 *)&d)->dqb_itime = i;
-		((struct mem_dqblk32 *)&d)->dqb_btime = b;
-		if (copy_to_user ((struct mem_dqblk32 *)addr, &d,
-				  sizeof (struct mem_dqblk32)))
+		((struct dqblk32 *)&d)->dqb_itime = i;
+		((struct dqblk32 *)&d)->dqb_btime = b;
+		if (copy_to_user ((struct dqblk32 *)addr, &d,
+				  sizeof (struct dqblk32)))
 			return -EFAULT;
 	}
 	return 0;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/s390x/kernel/process.c linux/arch/s390x/kernel/process.c
--- linux.orig/arch/s390x/kernel/process.c	Mon Jan  7 23:41:08 2002
+++ linux/arch/s390x/kernel/process.c	Fri Dec 21 16:25:30 2001
@@ -75,171 +75,22 @@
 	}
 }
 
-/*
-  As all the register will only be made displayable to the root
-  user ( via printk ) or checking if the uid of the user is 0 from
-  the /proc filesystem please god this will be secure enough DJB.
-  The lines are given one at a time so as not to chew stack space in
-  printk on a crash & also for the proc filesystem when you get
-  0 returned you know you've got all the lines
- */
-
-static int sprintf_regs(int line, char *buff, struct task_struct *task, struct pt_regs *regs)
-{
-	int linelen=0;
-	int regno,chaincnt;
-	u64 backchain,prev_backchain,endchain;
-	u64 ksp = 0;
-	char *mode = "???";
-
-	enum
-	{
-		sp_linefeed,
-		sp_psw,
-		sp_ksp,
-		sp_gprs,
-		sp_gprs1,
-		sp_gprs2,
-		sp_gprs3,
-		sp_gprs4,
-		sp_gprs5,
-		sp_gprs6,
-		sp_gprs7,
-		sp_gprs8,
-		sp_acrs,
-		sp_acrs1,
-		sp_acrs2,
-		sp_acrs3,
-		sp_acrs4,
-		sp_kern_backchain,
-		sp_kern_backchain1
-	};
-
-	if (task)
-		ksp = task->thread.ksp;
-	if (regs && !(regs->psw.mask & PSW_PROBLEM_STATE))
-		ksp = regs->gprs[15];
-
-	if (regs)
-		mode = (regs->psw.mask & PSW_PROBLEM_STATE)?
-		       "User" : "Kernel";
-
-	switch(line)
-	{
-	case sp_linefeed: 
-		linelen=sprintf(buff,"\n");
-		break;
-	case sp_psw:
-		if(regs)
-			linelen=sprintf(buff, "%s PSW:    %016lx %016lx    %s\n", mode,
-				(unsigned long) regs->psw.mask,
-				(unsigned long) regs->psw.addr,
-				print_tainted());
-		else
-			linelen=sprintf(buff,"pt_regs=NULL some info unavailable\n");
-		break;
-	case sp_ksp:
-		linelen=sprintf(&buff[linelen],
-				"task: %016lx ksp: %016lx pt_regs: %016lx\n",
-				(addr_t)task, (addr_t)ksp, (addr_t)regs);
-		break;
-	case sp_gprs:
-		if(regs)
-			linelen=sprintf(buff, "%s GPRS:\n", mode);
-		break;
-	case sp_gprs1 ... sp_gprs8:
-		if(regs)
-		{
-			regno=(line-sp_gprs1)*2;
-			linelen = sprintf(buff,"%016lx  %016lx\n",
-					  regs->gprs[regno],
-					  regs->gprs[regno+1]);
-		}
-		break;
-	case sp_acrs:
-		if(regs)
-			linelen=sprintf(buff, "%s ACRS:\n", mode);
-		break;	
-        case sp_acrs1 ... sp_acrs4:
-		if(regs)
-		{
-			regno=(line-sp_acrs1)*4;
-			linelen=sprintf(buff,"%08x  %08x  %08x  %08x\n",
-					regs->acrs[regno],
-					regs->acrs[regno+1],
-					regs->acrs[regno+2],
-					regs->acrs[regno+3]);
-		}
-		break;
-	case sp_kern_backchain:
-		if (regs && (regs->psw.mask & PSW_PROBLEM_STATE))
-			break;
-		if (ksp)
-			linelen=sprintf(buff, "Kernel BackChain          CallChain\n");
-		break;
-	default:
-		if (ksp)
-		{
-			
-			backchain=ksp&PSW_ADDR_MASK;
-			endchain=((backchain&(-THREAD_SIZE))+THREAD_SIZE);
-			prev_backchain=backchain-1;
-			line-=sp_kern_backchain1;
-			for(chaincnt=0;;chaincnt++)
-			{
-				if((backchain==0)||(backchain>=endchain)
-				   ||(chaincnt>=8)||(prev_backchain>=backchain))
-					break;
-				if(chaincnt==line)
-				{
-					linelen+=sprintf(&buff[linelen],"       %016lx   [<%016lx>]\n",
-							 backchain,
-							 *(u64 *)(backchain+112)&PSW_ADDR_MASK);
-					break;
-				}
-				prev_backchain=backchain;
-				backchain=(*((u64 *)backchain))&PSW_ADDR_MASK;
-			}
-		}
-	}
-	return(linelen);
-}
+extern void show_registers(struct pt_regs *regs);
+extern void show_trace(unsigned long *sp);
 
 void show_regs(struct pt_regs *regs)
 {
-	char buff[80];
-	int i, line;
-
-        printk("CPU:    %d\n",smp_processor_id());
-        printk("Process %s (pid: %d, stackpage=%016lX)\n",
-                current->comm, current->pid, 4096+(addr_t)current);
-	
-	for (line = 0; sprintf_regs(line, buff, current, regs); line++)
-		printk(buff);
-
-	if (regs->psw.mask & PSW_PROBLEM_STATE)
-	{
-		printk("User Code:\n");
-		memset(buff, 0, 20);
-		copy_from_user(buff,
-			       (char *) (regs->psw.addr & PSW_ADDR_MASK), 20);
-		for (i = 0; i < 20; i++)
-			printk("%02x ", buff[i]);
-		printk("\n");
-	}
-}
-
-char *task_show_regs(struct task_struct *task, char *buffer)
-{
-	int line, len;
+	struct task_struct *tsk = current;
 
-	for (line = 0; ; line++)
-	{
-		len = sprintf_regs(line, buffer, task, NULL);
-		if (!len) break;
-		buffer += len;
-	}
-	return buffer;
+        printk("CPU:    %d    %s\n", tsk->processor, print_tainted());
+        printk("Process %s (pid: %d, task: %016lx, ksp: %016lx)\n",
+	       current->comm, current->pid, (unsigned long) tsk,
+	       tsk->thread.ksp);
+
+	show_registers(regs);
+	/* Show stack backtrace if pt_regs is from kernel mode */
+	if (!(regs->psw.mask & PSW_PROBLEM_STATE))
+		show_trace((unsigned long *) regs->gprs[15]);
 }
 
 int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
@@ -301,16 +152,10 @@
             unsigned long gprs[10];    /* gprs 6 -15                       */
             unsigned long fprs[2];     /* fpr 4 and 6                      */
             unsigned long empty[2];
-#if CONFIG_REMOTE_DEBUG
-	    struct gdb_pt_regs childregs;
-#else
             struct pt_regs childregs;
-#endif
           } *frame;
 
         frame = (struct stack_frame *) (4*PAGE_SIZE + (unsigned long) p) -1;
-        frame = (struct stack_frame *) (((unsigned long) frame)&-8L);
-        p->thread.regs = &frame->childregs;
         p->thread.ksp = (unsigned long) frame;
         frame->childregs = *regs;
         frame->childregs.gprs[15] = new_stackp;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/s390x/kernel/ptrace.c linux/arch/s390x/kernel/ptrace.c
--- linux.orig/arch/s390x/kernel/ptrace.c	Mon Jan  7 23:41:08 2002
+++ linux/arch/s390x/kernel/ptrace.c	Fri Dec 21 16:25:30 2001
@@ -41,7 +41,7 @@
 
 void FixPerRegisters(struct task_struct *task)
 {
-	struct pt_regs *regs = task->thread.regs;
+	struct pt_regs *regs = __KSTK_PTREGS(task);
 	per_struct *per_info=
 			(per_struct *)&task->thread.per_info;
 
@@ -169,7 +169,7 @@
 		mask=PSW_ADDR_MASK;
 		if(useraddr<PT_FPC)
 		{
-			realuseraddr=(addr_t)&(((u8 *)task->thread.regs)[useraddr]);
+			realuseraddr=((addr_t) __KSTK_PTREGS(task)) + useraddr;
 			if(useraddr<PT_PSWMASK)
 			{
 				copymax=PT_PSWMASK;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/s390x/kernel/s390_ksyms.c linux/arch/s390x/kernel/s390_ksyms.c
--- linux.orig/arch/s390x/kernel/s390_ksyms.c	Mon Jan  7 23:41:08 2002
+++ linux/arch/s390x/kernel/s390_ksyms.c	Fri Dec 21 16:25:30 2001
@@ -21,8 +21,9 @@
 EXPORT_SYMBOL_NOVERS(_oi_bitmap);
 EXPORT_SYMBOL_NOVERS(_ni_bitmap);
 EXPORT_SYMBOL_NOVERS(_zb_findmap);
-EXPORT_SYMBOL_NOVERS(__copy_from_user_fixup);
-EXPORT_SYMBOL_NOVERS(__copy_to_user_fixup);
+EXPORT_SYMBOL_NOVERS(__copy_from_user_asm);
+EXPORT_SYMBOL_NOVERS(__copy_to_user_asm);
+EXPORT_SYMBOL_NOVERS(__clear_user_asm);
 
 /*
  * semaphore ops
@@ -38,6 +39,7 @@
 EXPORT_SYMBOL_NOVERS(memcmp);
 EXPORT_SYMBOL_NOVERS(memset);
 EXPORT_SYMBOL_NOVERS(memmove);
+EXPORT_SYMBOL_NOVERS(memscan);
 EXPORT_SYMBOL_NOVERS(strlen);
 EXPORT_SYMBOL_NOVERS(strchr);
 EXPORT_SYMBOL_NOVERS(strcmp);
@@ -67,4 +69,3 @@
 EXPORT_SYMBOL(console_mode);
 EXPORT_SYMBOL(console_device);
 EXPORT_SYMBOL_NOVERS(do_call_softirq);
-
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/s390x/kernel/setup.c linux/arch/s390x/kernel/setup.c
--- linux.orig/arch/s390x/kernel/setup.c	Mon Jan  7 23:41:08 2002
+++ linux/arch/s390x/kernel/setup.c	Fri Dec 21 16:25:30 2001
@@ -425,9 +425,10 @@
 	lowcore->io_new_psw.mask = _IO_PSW_MASK;
 	lowcore->io_new_psw.addr = (addr_t) &io_int_handler;
 	lowcore->ipl_device = S390_lowcore.ipl_device;
-	lowcore->kernel_stack = ((__u32) &init_task_union) + 16384;
+	lowcore->kernel_stack = ((__u64) &init_task_union) + 16384;
 	lowcore->async_stack = (__u64)
 		__alloc_bootmem(4*PAGE_SIZE, 4*PAGE_SIZE, 0) + 16384;
+	lowcore->jiffy_timer = -1LL;
 	set_prefix((__u32)(__u64) lowcore);
         cpu_init();
         boot_cpu_addr = S390_lowcore.cpu_data.cpu_addr;
@@ -474,15 +475,16 @@
 static int show_cpuinfo(struct seq_file *m, void *v)
 {
         struct cpuinfo_S390 *cpuinfo;
-	unsigned n = v;
+	unsigned long n = (unsigned long) v - 1;
 
-	if (!n--) {
+	if (!n) {
 		seq_printf(m, "vendor_id       : IBM/S390\n"
 				"# processors    : %i\n"
 				"bogomips per cpu: %lu.%02lu\n",
 				smp_num_cpus, loops_per_jiffy/(500000/HZ),
 				(loops_per_jiffy/(5000/HZ))%100);
-	} else if (cpu_online_map & (1 << n)) {
+	}
+	if (cpu_online_map & (1 << n)) {
 		cpuinfo = &safe_get_cpu_lowcore(n).cpu_data;
 		seq_printf(m, "processor %i: "
 				"version = %02X,  "
@@ -497,7 +499,7 @@
 
 static void *c_start(struct seq_file *m, loff_t *pos)
 {
-	return *pos <= NR_CPUS ? (void)(*pos+1) : NULL;
+	return *pos <= NR_CPUS ? (void *)((unsigned long) *pos + 1) : NULL;
 }
 static void *c_next(struct seq_file *m, void *v, loff_t *pos)
 {
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/s390x/kernel/smp.c linux/arch/s390x/kernel/smp.c
--- linux.orig/arch/s390x/kernel/smp.c	Mon Jan  7 23:41:08 2002
+++ linux/arch/s390x/kernel/smp.c	Fri Dec 21 16:25:30 2001
@@ -49,9 +49,6 @@
 static int       max_cpus = NR_CPUS;	  /* Setup configured maximum number of CPUs to activate	*/
 int              smp_num_cpus;
 struct _lowcore *lowcore_ptr[NR_CPUS];
-unsigned int     prof_multiplier[NR_CPUS];
-unsigned int     prof_old_multiplier[NR_CPUS];
-unsigned int     prof_counter[NR_CPUS];
 cycles_t         cacheflush_time=0;
 int              smp_threads_ready=0;      /* Set when the idlers are all forked. */
 static atomic_t  smp_commenced = ATOMIC_INIT(0);
@@ -453,7 +450,7 @@
 /*
  *      Activate a secondary processor.
  */
-extern void init_100hz_timer(void);
+extern void init_cpu_timer(void);
 extern int pfault_init(void);
 
 int __init start_secondary(void *cpuvoid)
@@ -465,8 +462,8 @@
         /* Wait for completion of smp startup */
         while (!atomic_read(&smp_commenced))
                 /* nothing */ ;
-        /* init per CPU 100 hz timer */
-        init_100hz_timer();
+        /* init per CPU timer */
+        init_cpu_timer();
 #ifdef CONFIG_PFAULT
 	/* Enable pfault pseudo page faults on this cpu. */
 	pfault_init();
@@ -519,7 +516,7 @@
 
         cpu_lowcore=&get_cpu_lowcore(cpu);
 	cpu_lowcore->save_area[15] = idle->thread.ksp;
-	cpu_lowcore->kernel_stack = (idle->thread.ksp | 16383) + 1;
+	cpu_lowcore->kernel_stack = (__u64) idle + 16384;
         __asm__ __volatile__("la    1,%0\n\t"
 			     "stctg 0,15,0(1)\n\t"
 			     "la    1,%1\n\t"
@@ -571,15 +568,7 @@
         
         /*
          *      Initialize the logical to physical CPU number mapping
-         *      and the per-CPU profiling counter/multiplier
          */
-        
-        for (i = 0; i < NR_CPUS; i++) {
-                prof_counter[i] = 1;
-                prof_old_multiplier[i] = 1;
-                prof_multiplier[i] = 1;
-        }
-
         print_cpu_info(&safe_get_cpu_lowcore(0).cpu_data);
 
         for(i = 0; i < smp_num_cpus; i++)
@@ -631,56 +620,6 @@
 int setup_profiling_timer(unsigned int multiplier)
 {
         return 0;
-}
-
-/*
- * Local timer interrupt handler. It does both profiling and
- * process statistics/rescheduling.
- *
- * We do profiling in every local tick, statistics/rescheduling
- * happen only every 'profiling multiplier' ticks. The default
- * multiplier is 1 and it can be changed by writing the new multiplier
- * value into /proc/profile.
- */
-
-void smp_local_timer_interrupt(struct pt_regs * regs)
-{
-	int user = (user_mode(regs) != 0);
-        int cpu = smp_processor_id();
-
-        /*
-         * The profiling function is SMP safe. (nothing can mess
-         * around with "current", and the profiling counters are
-         * updated with atomic operations). This is especially
-         * useful with a profiling multiplier != 1
-         */
-        if (!user_mode(regs))
-                s390_do_profile(regs->psw.addr);
-
-        if (!--prof_counter[cpu]) {
-
-                /*
-                 * The multiplier may have changed since the last time we got
-                 * to this point as a result of the user writing to
-                 * /proc/profile.  In this case we need to adjust the APIC
-                 * timer accordingly.
-                 *
-                 * Interrupts are already masked off at this point.
-                 */
-                prof_counter[cpu] = prof_multiplier[cpu];
-                if (prof_counter[cpu] != prof_old_multiplier[cpu]) {
-                  prof_old_multiplier[cpu] = prof_counter[cpu];
-                }
-
-                /*
-                 * 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.
-                 */
-
-		update_process_times(user);
-        }
 }
 
 EXPORT_SYMBOL(lowcore_ptr);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/s390x/kernel/time.c linux/arch/s390x/kernel/time.c
--- linux.orig/arch/s390x/kernel/time.c	Mon Jan  7 23:41:08 2002
+++ linux/arch/s390x/kernel/time.c	Fri Dec 21 16:25:30 2001
@@ -33,65 +33,33 @@
 #include <asm/irq.h>
 #include <asm/s390_ext.h>
 
-
 /* change this if you have some constant time drift */
-#define USECS_PER_JIFFY ((signed long)1000000/HZ)
-#define CLK_TICKS_PER_JIFFY ((signed long)USECS_PER_JIFFY<<12)
+#define USECS_PER_JIFFY     ((unsigned long) 1000000/HZ)
+#define CLK_TICKS_PER_JIFFY ((unsigned long) USECS_PER_JIFFY << 12)
 
 #define TICK_SIZE tick
 
-static uint64_t init_timer_cc, last_timer_cc;
+static uint64_t init_timer_cc;
 
 extern rwlock_t xtime_lock;
 extern unsigned long wall_jiffies;
 
-void tod_to_timeval(uint64_t todval, struct timeval *xtime)
+void tod_to_timeval(__u64 todval, struct timeval *xtime)
 {
-#if 0
-        const int high_bit = 0x80000000L;
-        const int c_f4240 = 0xf4240L;
-        const int c_7a120 = 0x7a120;
-	/* We have to divide the 64 bit value todval by 4096
-	 * (because the 2^12 bit is the one that changes every 
-         * microsecond) and then split it into seconds and
-         * microseconds. A value of max (2^52-1) divided by
-         * the value 0xF4240 can yield a max result of approx
-         * (2^32.068). Thats to big to fit into a signed int
-	 *   ... hacking time!
-         */
-	asm volatile ("L     2,%1\n\t"
-		      "LR    3,2\n\t"
-		      "SRL   2,12\n\t"
-		      "SLL   3,20\n\t"
-		      "L     4,%O1+4(%R1)\n\t"
-		      "SRL   4,12\n\t"
-		      "OR    3,4\n\t"  /* now R2/R3 contain (todval >> 12) */
-		      "SR    4,4\n\t"
-		      "CL    2,%2\n\t"
-		      "JL    .+12\n\t"
-		      "S     2,%2\n\t"
-		      "L     4,%3\n\t"
-                      "D     2,%4\n\t"
-		      "OR    3,4\n\t"
-		      "ST    2,%O0+4(%R0)\n\t"
-		      "ST    3,%0"
-		      : "=m" (*xtime) : "m" (todval),
-		        "m" (c_7a120), "m" (high_bit), "m" (c_f4240)
-		      : "cc", "memory", "2", "3", "4" );
-#else
-    todval >>= 12;
-    xtime->tv_sec = todval / 1000000;
-    xtime->tv_usec = todval % 1000000;
-#endif
+        todval >>= 12;
+        xtime->tv_sec = todval / 1000000;
+        xtime->tv_usec = todval % 1000000;
 }
 
-unsigned long do_gettimeoffset(void) 
+static inline unsigned long do_gettimeoffset(void) 
 {
-	__u64 timer_cc;
+	__u64 now;
 
-	asm volatile ("STCK %0" : "=m" (timer_cc));
-        /* We require the offset from the previous interrupt */
-        return ((unsigned long)((timer_cc - last_timer_cc)>>12));
+	asm ("STCK %0" : "=m" (now));
+        now = (now - init_timer_cc) >> 12;
+	/* We require the offset from the latest update of xtime */
+	now -= (__u64) wall_jiffies*USECS_PER_JIFFY;
+	return (unsigned long) now;
 }
 
 /*
@@ -101,15 +69,10 @@
 {
 	unsigned long flags;
 	unsigned long usec, sec;
-	unsigned long lost_ticks;
 
 	read_lock_irqsave(&xtime_lock, flags);
-	lost_ticks = jiffies - wall_jiffies;
-	usec = do_gettimeoffset();
-	if (lost_ticks)
-		usec +=(USECS_PER_JIFFY*lost_ticks);
 	sec = xtime.tv_sec;
-	usec += xtime.tv_usec;
+	usec = xtime.tv_usec + do_gettimeoffset();
 	read_unlock_irqrestore(&xtime_lock, flags);
 
 	while (usec >= 1000000) {
@@ -155,51 +118,31 @@
 extern __u16 boot_cpu_addr;
 #endif
 
-void do_timer_interrupt(struct pt_regs *regs, __u16 error_code)
+static void do_comparator_interrupt(struct pt_regs *regs, __u16 error_code)
 {
 	int cpu = smp_processor_id();
 
 	irq_enter(cpu, 0);
 
-        /*
-         * reset timer to 10ms minus time already elapsed
-         * since timer-interrupt pending
-         */
+	/*
+	 * set clock comparator for next tick
+	 */
+        S390_lowcore.jiffy_timer += CLK_TICKS_PER_JIFFY;
+        asm volatile ("SCKC %0" : : "m" (S390_lowcore.jiffy_timer));
+
 #ifdef CONFIG_SMP
-	if(S390_lowcore.cpu_data.cpu_addr==boot_cpu_addr) {
+	if (S390_lowcore.cpu_data.cpu_addr == boot_cpu_addr)
 		write_lock(&xtime_lock);
-		last_timer_cc = S390_lowcore.jiffy_timer_cc;
-	}
-#else
-        last_timer_cc = S390_lowcore.jiffy_timer_cc;
-#endif
-        /* set clock comparator */
-        S390_lowcore.jiffy_timer_cc += CLK_TICKS_PER_JIFFY;
-        asm volatile ("SCKC %0" : : "m" (S390_lowcore.jiffy_timer_cc));
 
-/*
- * In the SMP case we use the local timer interrupt to do the
- * profiling, except when we simulate SMP mode on a uniprocessor
- * system, in that case we have to call the local interrupt handler.
- */
-#ifdef CONFIG_SMP
-        /* when SMP, do smp_local_timer_interrupt for *all* CPUs,
-           but only do the rest for the boot CPU */
-        smp_local_timer_interrupt(regs);
-#else
-        if (!user_mode(regs))
-                s390_do_profile(regs->psw.addr);
-#endif
+	update_process_times(user_mode(regs));
 
-#ifdef CONFIG_SMP
-	if(S390_lowcore.cpu_data.cpu_addr==boot_cpu_addr)
-#endif
-	{
+	if (S390_lowcore.cpu_data.cpu_addr == boot_cpu_addr) {
 		do_timer(regs);
-#ifdef CONFIG_SMP
 		write_unlock(&xtime_lock);
-#endif
 	}
+#else
+	do_timer(regs);
+#endif
 
 	irq_exit(cpu, 0);
 }
@@ -207,19 +150,17 @@
 /*
  * Start the clock comparator on the current CPU
  */
-static unsigned long cr0 __attribute__ ((aligned (8)));
-
-void init_100hz_timer(void)
+void init_cpu_timer(void)
 {
+	unsigned long cr0;
+
         /* allow clock comparator timer interrupt */
         asm volatile ("STCTG 0,0,%0" : "=m" (cr0) : : "memory");
         cr0 |= 0x800;
         asm volatile ("LCTLG 0,0,%0" : : "m" (cr0) : "memory");
-        /* set clock comparator */
-        /* read the TOD clock */
-        asm volatile ("STCK %0" : "=m" (S390_lowcore.jiffy_timer_cc));
-        S390_lowcore.jiffy_timer_cc += CLK_TICKS_PER_JIFFY;
-        asm volatile ("SCKC %0" : : "m" (S390_lowcore.jiffy_timer_cc));
+	S390_lowcore.jiffy_timer = (__u64) jiffies * CLK_TICKS_PER_JIFFY;
+	S390_lowcore.jiffy_timer += init_timer_cc + CLK_TICKS_PER_JIFFY;
+	asm volatile ("SCKC %0" : : "m" (S390_lowcore.jiffy_timer));
 }
 
 /*
@@ -228,6 +169,7 @@
  */
 void __init time_init(void)
 {
+        __u64 set_time_cc;
 	int cc;
 
         /* kick the TOD clock */
@@ -247,14 +189,18 @@
                 printk("time_init: TOD clock stopped/non-operational\n");
                 break;
         }
+
+	/* set xtime */
+        set_time_cc = init_timer_cc - 0x8126d60e46000000LL +
+                      (0x3c26700LL*1000000*4096);
+        tod_to_timeval(set_time_cc, &xtime);
+
         /* request the 0x1004 external interrupt */
-        if (register_external_interrupt(0x1004, do_timer_interrupt) != 0)
-                panic("Couldn't request external interrupts 0x1004");
-        init_100hz_timer();
-        init_timer_cc = S390_lowcore.jiffy_timer_cc;
-        init_timer_cc -= 0x8126d60e46000000LL -
-                         (0x3c26700LL*1000000*4096);
-        tod_to_timeval(init_timer_cc, &xtime);
+        if (register_external_interrupt(0x1004, do_comparator_interrupt) != 0)
+                panic("Couldn't request external interrupt 0x1004");
+
+        /* init CPU timer */
+        init_cpu_timer();
 
 	/* Set do_get_fast_time function pointer.  */
 	do_get_fast_time = do_gettimeofday;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/s390x/kernel/traps.c linux/arch/s390x/kernel/traps.c
--- linux.orig/arch/s390x/kernel/traps.c	Mon Jan  7 23:41:08 2002
+++ linux/arch/s390x/kernel/traps.c	Fri Dec 21 16:25:30 2001
@@ -26,6 +26,7 @@
 #include <linux/smp_lock.h>
 #include <linux/init.h>
 #include <linux/delay.h>
+#include <linux/module.h>
 
 #include <asm/system.h>
 #include <asm/uaccess.h>
@@ -58,6 +59,203 @@
 extern void pfault_interrupt(struct pt_regs *regs, __u16 error_code);
 #endif
 
+int kstack_depth_to_print = 20;
+
+/*
+ * If the address is either in the .text section of the
+ * kernel, or in the vmalloc'ed module regions, it *may* 
+ * be the address of a calling routine
+ */
+extern char _stext, _etext;
+
+#ifdef CONFIG_MODULES
+
+extern struct module *module_list;
+extern struct module kernel_module;
+
+static inline int kernel_text_address(unsigned long addr)
+{
+	int retval = 0;
+	struct module *mod;
+
+	if (addr >= (unsigned long) &_stext &&
+	    addr <= (unsigned long) &_etext)
+		return 1;
+
+	for (mod = module_list; mod != &kernel_module; mod = mod->next) {
+		/* mod_bound tests for addr being inside the vmalloc'ed
+		 * module area. Of course it'd be better to test only
+		 * for the .text subset... */
+		if (mod_bound(addr, 0, mod)) {
+			retval = 1;
+			break;
+		}
+	}
+
+	return retval;
+}
+
+#else
+
+static inline int kernel_text_address(unsigned long addr)
+{
+	return (addr >= (unsigned long) &_stext &&
+		addr <= (unsigned long) &_etext);
+}
+
+#endif
+
+void show_trace(unsigned long * stack)
+{
+	unsigned long backchain, low_addr, high_addr, ret_addr;
+	int i;
+
+	if (!stack)
+		stack = (unsigned long*)&stack;
+
+	printk("Call Trace: ");
+	low_addr = ((unsigned long) stack) & PSW_ADDR_MASK;
+	high_addr = (low_addr & (-THREAD_SIZE)) + THREAD_SIZE;
+	/* Skip the first frame (biased stack) */
+	backchain = *((unsigned long *) low_addr) & PSW_ADDR_MASK;
+	/* Print up to 8 lines */
+	for (i = 0; i < 8; i++) {
+		if (backchain < low_addr || backchain >= high_addr)
+			break;
+		ret_addr = *((unsigned long *) (backchain+112)) & PSW_ADDR_MASK;
+		if (!kernel_text_address(ret_addr))
+			break;
+		if (i && ((i % 3) == 0))
+			printk("\n   ");
+		printk("[<%016lx>] ", ret_addr);
+		low_addr = backchain;
+		backchain = *((unsigned long *) backchain) & PSW_ADDR_MASK;
+	}
+	printk("\n");
+}
+
+void show_trace_task(struct task_struct *tsk)
+{
+	/*
+	 * We can't print the backtrace of a running process. It is
+	 * unreliable at best and can cause kernel oopses.
+	 */
+	if (task_has_cpu(tsk))
+		return;
+	show_trace((unsigned long *) tsk->thread.ksp);
+}
+
+void show_stack(unsigned long *sp)
+{
+	unsigned long *stack;
+	int i;
+
+	// debugging aid: "show_stack(NULL);" prints the
+	// back trace for this cpu.
+
+	if (sp == NULL)
+		sp = (unsigned long*) &sp;
+
+	stack = sp;
+	for (i = 0; i < kstack_depth_to_print; i++) {
+		if (((addr_t) stack & (THREAD_SIZE-1)) == 0)
+			break;
+		if (i && ((i % 4) == 0))
+			printk("\n       ");
+		printk("%016lx ", *stack++);
+	}
+	printk("\n");
+	show_trace(sp);
+}
+
+void show_registers(struct pt_regs *regs)
+{
+	mm_segment_t old_fs;
+	char *mode;
+	int i;
+
+	mode = (regs->psw.mask & PSW_PROBLEM_STATE) ? "User" : "Krnl";
+	printk("%s PSW : %016lx %016lx\n",
+	       mode, (unsigned long) regs->psw.mask,
+	       (unsigned long) regs->psw.addr);
+	printk("%s GPRS: %016lx %016lx %016lx %016lx\n", mode,
+	       regs->gprs[0], regs->gprs[1], regs->gprs[2], regs->gprs[3]);
+	printk("           %016lx %016lx %016lx %016lx\n",
+	       regs->gprs[4], regs->gprs[5], regs->gprs[6], regs->gprs[7]);
+	printk("           %016lx %016lx %016lx %016lx\n",
+	       regs->gprs[8], regs->gprs[9], regs->gprs[10], regs->gprs[11]);
+	printk("           %016lx %016lx %016lx %016lx\n",
+	       regs->gprs[12], regs->gprs[13], regs->gprs[14], regs->gprs[15]);
+	printk("%s ACRS: %08x %08x %08x %08x\n", mode,
+	       regs->acrs[0], regs->acrs[1], regs->acrs[2], regs->acrs[3]);
+	printk("           %08x %08x %08x %08x\n",
+	       regs->acrs[4], regs->acrs[5], regs->acrs[6], regs->acrs[7]);
+	printk("           %08x %08x %08x %08x\n",
+	       regs->acrs[8], regs->acrs[9], regs->acrs[10], regs->acrs[11]);
+	printk("           %08x %08x %08x %08x\n",
+	       regs->acrs[12], regs->acrs[13], regs->acrs[14], regs->acrs[15]);
+
+	/*
+	 * Print the first 20 byte of the instruction stream at the
+	 * time of the fault.
+	 */
+	old_fs = get_fs();
+	if (regs->psw.mask & PSW_PROBLEM_STATE)
+		set_fs(USER_DS);
+	else
+		set_fs(KERNEL_DS);
+	printk("%s Code: ", mode);
+	for (i = 0; i < 20; i++) {
+		unsigned char c;
+		if (__get_user(c, (char *)(regs->psw.addr + i))) {
+			printk(" Bad PSW.");
+			break;
+		}
+		printk("%02x ", c);
+	}
+	set_fs(old_fs);
+
+	printk("\n");
+}	
+
+/* This is called from fs/proc/array.c */
+char *task_show_regs(struct task_struct *task, char *buf)
+{
+	struct pt_regs *regs;
+
+	regs = __KSTK_PTREGS(task);
+	buf += sprintf(buf, "task: %016lx, ksp: %016lx\n",
+		       (unsigned long) task, task->thread.ksp);
+	buf += sprintf(buf, "User PSW : %016lx %016lx\n",
+		       (unsigned long) regs->psw.mask, 
+		       (unsigned long) regs->psw.addr);
+	buf += sprintf(buf, "User GPRS: %016lx %016lx %016lx %016lx\n",
+		       regs->gprs[0], regs->gprs[1],
+		       regs->gprs[2], regs->gprs[3]);
+	buf += sprintf(buf, "           %016lx %016lx %016lx %016lx\n",
+		       regs->gprs[4], regs->gprs[5], 
+		       regs->gprs[6], regs->gprs[7]);
+	buf += sprintf(buf, "           %016lx %016lx %016lx %016lx\n",
+		       regs->gprs[8], regs->gprs[9],
+		       regs->gprs[10], regs->gprs[11]);
+	buf += sprintf(buf, "           %016lx %016lx %016lx %016lx\n",
+		       regs->gprs[12], regs->gprs[13],
+		       regs->gprs[14], regs->gprs[15]);
+	buf += sprintf(buf, "User ACRS: %08x %08x %08x %08x\n",
+		       regs->acrs[0], regs->acrs[1],
+		       regs->acrs[2], regs->acrs[3]);
+	buf += sprintf(buf, "           %08x %08x %08x %08x\n",
+		       regs->acrs[4], regs->acrs[5],
+		       regs->acrs[6], regs->acrs[7]);
+	buf += sprintf(buf, "           %08x %08x %08x %08x\n",
+		       regs->acrs[8], regs->acrs[9],
+		       regs->acrs[10], regs->acrs[11]);
+	buf += sprintf(buf, "           %08x %08x %08x %08x\n",
+		       regs->acrs[12], regs->acrs[13],
+		       regs->acrs[14], regs->acrs[15]);
+	return buf;
+}
+
 spinlock_t die_lock = SPIN_LOCK_UNLOCKED;
 
 void die(const char * str, struct pt_regs * regs, long err)
@@ -142,7 +340,7 @@
 #if CONFIG_REMOTE_DEBUG
 		if(gdb_stub_initialised)
 		{
-			gdb_stub_handle_exception((gdb_pt_regs *)regs,signal);
+			gdb_stub_handle_exception(regs, signal);
 			return 0;
 		}
 #endif
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/s390x/kernel/wrapper32.S linux/arch/s390x/kernel/wrapper32.S
--- linux.orig/arch/s390x/kernel/wrapper32.S	Mon Jan  7 23:41:08 2002
+++ linux/arch/s390x/kernel/wrapper32.S	Fri Dec 21 16:25:30 2001
@@ -120,8 +120,8 @@
 	lgfr	%r2,%r2			# long
 	lgfr	%r3,%r3			# long
 	llgtr	%r4,%r4			# long
-	lgfr	%r5,%r5			# long
-	jg	sys32_ptrace		# branch to system call
+	llgfr	%r5,%r5			# long
+	jg	sys_ptrace		# branch to system call
 
 	.globl  sys32_alarm_wrapper 
 sys32_alarm_wrapper:
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/s390x/lib/uaccess.S linux/arch/s390x/lib/uaccess.S
--- linux.orig/arch/s390x/lib/uaccess.S	Mon Jan  7 23:41:08 2002
+++ linux/arch/s390x/lib/uaccess.S	Fri Dec 21 16:25:30 2001
@@ -6,40 +6,82 @@
  *    Copyright (C) 2000 IBM Deutschland Entwicklung GmbH, IBM Corporation
  *    Authors(s): Martin Schwidefsky (schwidefsky@de.ibm.com)
  *
- *  These functions have a non-standard call interface
+ *  These functions have standard call interface
  */
 
 #include <asm/lowcore.h>
 
         .text
         .align 4
-        .globl __copy_from_user_fixup
-__copy_from_user_fixup:
-        lg    1,__LC_PGM_OLD_PSW+8
-0:      lghi  3,-4096
-        ng    3,__LC_TRANS_EXC_ADDR
-        sgr   3,4
-        bm    4(1)
-1:      mvcle 2,4,0
-        b     4(1)
+        .globl __copy_from_user_asm
+__copy_from_user_asm:
+	lgr	%r5,%r3
+	sacf	512
+0:	mvcle	%r2,%r4,0
+	jo	0b
+	sacf	0
+	lgr	%r2,%r5
+	br	%r14
+1:	lg	%r1,__LC_PGM_OLD_PSW+8
+2:	lghi	%r3,-4096
+        ng	%r3,__LC_TRANS_EXC_ADDR
+        sgr	%r3,%r4
+        bm	4(%r1)
+3:      mvcle	%r2,%r4,0
+        b	4(%r1)
         .section __ex_table,"a"
-        .align 8
-	.quad 1b,0b
+        .align	8
+	.quad	0b,1b
+	.quad	3b,2b
         .previous
 
         .align 4
         .text
-        .globl __copy_to_user_fixup
-__copy_to_user_fixup:
-        lg    1,__LC_PGM_OLD_PSW+8
-0:      lghi  5,-4096
-        ng    5,__LC_TRANS_EXC_ADDR
-        sgr   5,4
-        bm    4(1)
-1:      mvcle 4,2,0
-        b     4(1)
+        .globl __copy_to_user_asm
+__copy_to_user_asm:
+	lgr	%r5,%r3
+	sacf	512
+0:	mvcle	%r4,%r2,0
+	jo	0b
+	sacf	0
+	lgr	%r2,%r3
+	br	%r14
+1:	lg	%r1,__LC_PGM_OLD_PSW+8
+2:      lghi	%r5,-4096
+        ng	%r5,__LC_TRANS_EXC_ADDR
+        sgr	%r5,%r4
+        bm	4(%r1)
+3:      mvcle	%r4,%r2,0
+        b	4(%r1)
         .section __ex_table,"a"
-        .align 8
-        .quad 1b,0b
+        .align	8
+        .quad	0b,1b
+        .quad	3b,2b
         .previous
 
+        .align 4
+        .text
+        .globl __clear_user_asm
+__clear_user_asm:
+	lgr	%r4,%r2
+	lgr	%r5,%r3
+	sgr	%r2,%r2
+	sgr	%r3,%r3
+	sacf	512
+0:	mvcle	%r4,%r2,0
+	jo	0b
+	sacf	0
+	lgr	%r2,%r5
+	br	%r14
+1:	lg	%r1,__LC_PGM_OLD_PSW+8
+2:      lghi	%r5,-4096
+        ng	%r5,__LC_TRANS_EXC_ADDR
+        sgr	%r5,%r4
+        bm	4(%r1)
+3:      mvcle	%r4,%r2,0
+        b	4(%r1)
+        .section __ex_table,"a"
+        .align	8
+        .quad	0b,1b
+        .quad	3b,2b
+        .previous
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/sh/config.in linux/arch/sh/config.in
--- linux.orig/arch/sh/config.in	Mon Jan  7 23:41:08 2002
+++ linux/arch/sh/config.in	Fri Dec 21 18:00:38 2001
@@ -189,7 +189,7 @@
    if [ "$CONFIG_PCI_GODIRECT" = "y" -o "$CONFIG_PCI_GOANY" = "y" ]; then
       define_bool CONFIG_PCI_DIRECT y
    fi
-   define_bool CONFIG_SH_PCIDMA_NONCOHERENT n
+   bool 'Cache and PCI noncoherent' CONFIG_SH_PCIDMA_NONCOHERENT n
 fi
 
 source drivers/pci/Config.in
@@ -280,9 +280,9 @@
 #
 source drivers/input/Config.in
 
-if [ "$CONFIG_SH_DREAMCAST" = "y" ]; then
-   source drivers/maple/Config.in
-fi
+# if [ "$CONFIG_SH_DREAMCAST" = "y" ]; then
+#    source drivers/maple/Config.in
+# fi
 
 mainmenu_option next_comment
 comment 'Character devices'
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/sh/kernel/io_7751se.c linux/arch/sh/kernel/io_7751se.c
--- linux.orig/arch/sh/kernel/io_7751se.c	Mon Jan  7 23:41:08 2002
+++ linux/arch/sh/kernel/io_7751se.c	Fri Dec 21 18:00:38 2001
@@ -17,7 +17,7 @@
 #include <asm/hitachi_7751se.h>
 #include <asm/addrspace.h>
 
-#include <asm/pci.h>
+#include <linux/pci.h>
 #include <asm/pci-sh7751.h>
 
 #if 0
@@ -70,7 +70,7 @@
 	else
 		return (volatile __u16 *) (PA_SUPERIO + (port << 1));
 #endif
-	maybebadio(name,port);
+	maybebadio(name,(unsigned long)port);
 	return (volatile __u16*)port;
 }
 
@@ -276,6 +276,7 @@
 /* ISA page descriptor.  */
 static __u32 sh_isa_memmap[256];
 
+#if 0
 static int
 sh_isa_mmap(__u32 start, __u32 length, __u32 offset)
 {
@@ -286,12 +287,11 @@
 
 	idx = start >> 12;
 	sh_isa_memmap[idx] = 0xb8000000 + (offset &~ 0xfff);
-#if 0
 	printk("sh_isa_mmap: start %x len %x offset %x (idx %x paddr %x)\n",
 	       start, length, offset, idx, sh_isa_memmap[idx]);
-#endif
 	return 0;
 }
+#endif
 
 unsigned long
 sh7751se_isa_port2addr(unsigned long offset)
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/sh/kernel/pci-7751se.c linux/arch/sh/kernel/pci-7751se.c
--- linux.orig/arch/sh/kernel/pci-7751se.c	Mon Jan  7 23:41:08 2002
+++ linux/arch/sh/kernel/pci-7751se.c	Fri Dec 21 18:00:38 2001
@@ -37,7 +37,6 @@
  */
 int __init pcibios_init_platform(void)
 {
-   unsigned long data;
    unsigned long bcr1, wcr1, wcr2, wcr3, mcr;
    unsigned short bcr2;
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/sh/kernel/rtc.c linux/arch/sh/kernel/rtc.c
--- linux.orig/arch/sh/kernel/rtc.c	Mon Jan  7 23:41:08 2002
+++ linux/arch/sh/kernel/rtc.c	Fri Dec 21 18:00:38 2001
@@ -46,7 +46,7 @@
 	} while ((ctrl_inb(RCR1) & RCR1_CF) != 0);
 
 #if RTC_BIT_INVERTED != 0
-	/* Work around to avoid reading correct value. */
+	/* Work around to avoid reading incorrect value. */
 	if (sec128 == RTC_BIT_INVERTED) {
 		schedule_timeout(1);
 		goto again;
@@ -81,12 +81,18 @@
 		goto again;
 	}
 
+#if RTC_BIT_INVERTED != 0
+	if ((sec128 & RTC_BIT_INVERTED))
+		sec--;
+#endif
+
 	tv->tv_sec = mktime(yr100 * 100 + yr, mon, day, hr, min, sec);
-	tv->tv_usec = ((sec128 ^ RTC_BIT_INVERTED) * 1000000) / 128;
+	tv->tv_usec = (sec128 * 1000000) / 128;
 }
 
-static int set_rtc_time(unsigned long nowtime)
+int sh_rtc_settimeofday(const struct timeval *tv)
 {
+	unsigned long nowtime = tv->tv_sec;
 	int retval = 0;
 	int real_seconds, real_minutes, cmos_minutes;
 
@@ -122,13 +128,4 @@
 	ctrl_outb(RCR2_RTCEN|RCR2_START, RCR2);  /* Start RTC */
 
 	return retval;
-}
-
-int sh_rtc_settimeofday(const struct timeval *tv)
-{
-#if RTC_BIT_INVERTED != 0
-	/* This is not accurate, but better than nothing. */
-	schedule_timeout(HZ/2);
-#endif
-	return set_rtc_time(tv->tv_sec);
 }
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/sh/kernel/traps.c linux/arch/sh/kernel/traps.c
--- linux.orig/arch/sh/kernel/traps.c	Mon Jan  7 23:41:08 2002
+++ linux/arch/sh/kernel/traps.c	Fri Dec 21 18:00:38 2001
@@ -560,3 +560,8 @@
 		}
 	}
 }
+
+void show_trace_task(struct task_struct *tsk)
+{
+	printk("Backtrace not yet implemented for SH.\n");
+}
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/block/ll_rw_blk.c linux/drivers/block/ll_rw_blk.c
--- linux.orig/drivers/block/ll_rw_blk.c	Mon Oct 29 20:11:17 2001
+++ linux/drivers/block/ll_rw_blk.c	Mon Jan  7 17:36:23 2002
@@ -694,8 +694,10 @@
 	switch (el_ret) {
 
 		case ELEVATOR_BACK_MERGE:
-			if (!q->back_merge_fn(q, req, bh, max_segments))
+			if (!q->back_merge_fn(q, req, bh, max_segments)) {
+				insert_here = &req->queue;
 				break;
+			}
 			elevator->elevator_merge_cleanup_fn(q, req, count);
 			req->bhtail->b_reqnext = bh;
 			req->bhtail = bh;
@@ -706,8 +708,10 @@
 			goto out;
 
 		case ELEVATOR_FRONT_MERGE:
-			if (!q->front_merge_fn(q, req, bh, max_segments))
+			if (!q->front_merge_fn(q, req, bh, max_segments)) {
+				insert_here = req->queue.prev;
 				break;
+			}
 			elevator->elevator_merge_cleanup_fn(q, req, count);
 			bh->b_reqnext = req->bh;
 			req->bh = bh;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/block/swim3.c linux/drivers/block/swim3.c
--- linux.orig/drivers/block/swim3.c	Mon Jan  7 23:41:08 2002
+++ linux/drivers/block/swim3.c	Wed Dec 26 16:29:22 2001
@@ -29,14 +29,15 @@
 #include <asm/prom.h>
 #include <asm/uaccess.h>
 #include <asm/mediabay.h>
-#include <asm/feature.h>
+#include <asm/machdep.h>
+#include <asm/pmac_feature.h>
 
 #define MAJOR_NR	FLOPPY_MAJOR
 #include <linux/blk.h>
 #include <linux/devfs_fs_kernel.h>
 
 static int floppy_blocksizes[2] = {512,512};
-static int floppy_sizes[2] = {2880,2880};
+static int floppy_sizes[2] = {1440,1440};
 
 #define MAX_FLOPPIES	2
 
@@ -444,9 +445,9 @@
 		++cp;
 		init_dma(cp, OUTPUT_MORE, CURRENT->buffer, 512);
 		++cp;
-		init_dma(cp, OUTPUT_MORE, write_postamble, sizeof(write_postamble));
+		init_dma(cp, OUTPUT_LAST, write_postamble, sizeof(write_postamble));
 	} else {
-		init_dma(cp, INPUT_MORE, CURRENT->buffer, n * 512);
+		init_dma(cp, INPUT_LAST, CURRENT->buffer, n * 512);
 	}
 	++cp;
 	out_le16(&cp->command, DBDMA_STOP);
@@ -678,7 +679,10 @@
 			break;
 		dr = fs->dma;
 		cp = fs->dma_cmd;
-		st_le32(&dr->control, RUN << 16);
+		/* We must wait a bit for dbdma to complete */
+		for (n=0; (in_le32(&dr->status) & ACTIVE) && n < 1000; n++)
+			udelay(10);
+		DBDMA_DO_STOP(dr);
 		out_8(&sw->intr_enable, 0);
 		out_8(&sw->control_bic, WRITE_SECTORS | DO_ACTION);
 		out_8(&sw->select, RELAX);
@@ -1070,9 +1074,14 @@
 		return -EINVAL;
 	}
 
+	if (!request_OF_resource(swim, 0, NULL)) {
+		printk(KERN_INFO "swim3: can't request IO resource !\n");
+		return -EINVAL;
+	}
+
 	mediabay = (strcasecmp(swim->parent->type, "media-bay") == 0) ? swim->parent : NULL;
 	if (mediabay == NULL)
-		feature_set(swim, FEATURE_SWIM3_enable);
+		pmac_call_feature(PMAC_FTR_SWIM3_ENABLE, swim, 0, 1);
 	
 	memset(fs, 0, sizeof(*fs));
 	fs->state = idle;
@@ -1094,14 +1103,14 @@
 
 	if (request_irq(fs->swim3_intr, swim3_interrupt, 0, "SWIM3", fs)) {
 		printk(KERN_ERR "Couldn't get irq %d for SWIM3\n", fs->swim3_intr);
-		feature_clear(swim, FEATURE_SWIM3_enable);
+		pmac_call_feature(PMAC_FTR_SWIM3_ENABLE, swim, 0, 0);
 		return -EBUSY;
 	}
 /*
 	if (request_irq(fs->dma_intr, fd_dma_interrupt, 0, "SWIM3-dma", fs)) {
 		printk(KERN_ERR "Couldn't get irq %d for SWIM3 DMA",
 		       fs->dma_intr);
-		feature_clear(swim, FEATURE_SWIM3_enable);
+		pmac_call_feature(PMAC_FTR_SWIM3_ENABLE, swim, 0, 0);
 		return -EBUSY;
 	}
 */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/agp/agp.h linux/drivers/char/agp/agp.h
--- linux.orig/drivers/char/agp/agp.h	Fri Nov  9 22:01:21 2001
+++ linux/drivers/char/agp/agp.h	Mon Jan  7 14:18:35 2002
@@ -179,6 +179,9 @@
 #ifndef PCI_DEVICE_ID_INTEL_820_0
 #define PCI_DEVICE_ID_INTEL_820_0       0x2500
 #endif
+#ifndef PCI_DEVICE_ID_INTEL_820_UP_0
+#define PCI_DEVICE_ID_INTEL_820_UP_0    0x2501
+#endif
 #ifndef PCI_DEVICE_ID_INTEL_840_0
 #define PCI_DEVICE_ID_INTEL_840_0		0x1a21
 #endif
@@ -189,7 +192,7 @@
 #define PCI_DEVICE_ID_INTEL_850_0     0x2530
 #endif
 #ifndef PCI_DEVICE_ID_INTEL_860_0
-#define PCI_DEVICE_ID_INTEL_860_0	0x2532
+#define PCI_DEVICE_ID_INTEL_860_0     0x2531
 #endif
 #ifndef PCI_DEVICE_ID_INTEL_810_DC100_0
 #define PCI_DEVICE_ID_INTEL_810_DC100_0 0x7122
@@ -275,6 +278,9 @@
 #define I830_RDRAM_CHANNEL_TYPE    0x03010
 #define I830_RDRAM_ND(x)           (((x) & 0x20) >> 5)
 #define I830_RDRAM_DDT(x)          (((x) & 0x18) >> 3)
+
+/* This one is for I830MP w. an external graphic card */
+#define INTEL_I830_ERRSTS          0x92
 
 /* intel i820 registers */
 #define INTEL_I820_RDCR     0x51
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/agp/agpgart_be.c linux/drivers/char/agp/agpgart_be.c
--- linux.orig/drivers/char/agp/agpgart_be.c	Fri Dec 21 17:41:53 2001
+++ linux/drivers/char/agp/agpgart_be.c	Mon Jan  7 14:18:35 2002
@@ -1587,6 +1587,40 @@
 	return 0;
 }
 
+static int intel_830mp_configure(void)
+{
+       u32 temp;
+       u16 temp2;
+       aper_size_info_8 *current_size;
+
+       current_size = A_SIZE_8(agp_bridge.current_size);
+
+       /* aperture size */
+       pci_write_config_byte(agp_bridge.dev, INTEL_APSIZE,
+                             current_size->size_value);
+
+       /* address to map to */
+       pci_read_config_dword(agp_bridge.dev, INTEL_APBASE, &temp);
+       agp_bridge.gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK);
+
+       /* attbase - aperture base */
+       pci_write_config_dword(agp_bridge.dev, INTEL_ATTBASE,
+                              agp_bridge.gatt_bus_addr);
+
+       /* agpctrl */
+       pci_write_config_dword(agp_bridge.dev, INTEL_AGPCTRL, 0x0000);
+
+       /* gmch */
+       pci_read_config_word(agp_bridge.dev, INTEL_NBXCFG, &temp2);
+       pci_write_config_word(agp_bridge.dev, INTEL_NBXCFG,
+                             temp2 | (1 << 9));
+       /* clear any possible AGP-related error conditions */
+       pci_write_config_word(agp_bridge.dev, INTEL_I830_ERRSTS, 0x1c);
+       return 0;
+}
+       
+
+
 static int intel_840_configure(void)
 {
 	u32 temp;
@@ -1715,6 +1749,7 @@
 	return 0;
 }
 
+
 static unsigned long intel_mask_memory(unsigned long addr, int type)
 {
 	/* Memory type is ignored */
@@ -1755,6 +1790,14 @@
 	{4, 1024, 0, 63}
 };
 
+static aper_size_info_8 intel_830mp_sizes[4] = 
+{
+  {256, 65536, 6, 0},
+  {128, 32768, 5, 32},
+  {64, 16384, 4, 48},
+  {32, 8192, 3, 56}
+};
+
 static int __init intel_generic_setup (struct pci_dev *pdev)
 {
 	agp_bridge.masks = intel_generic_masks;
@@ -1789,6 +1832,7 @@
 }
 
 
+
 static int __init intel_820_setup (struct pci_dev *pdev)
 {
        agp_bridge.masks = intel_generic_masks;
@@ -1800,7 +1844,7 @@
        agp_bridge.needs_scratch_page = FALSE;
        agp_bridge.configure = intel_820_configure;
        agp_bridge.fetch_size = intel_8xx_fetch_size;
-       agp_bridge.cleanup = intel_cleanup;
+       agp_bridge.cleanup = intel_820_cleanup;
        agp_bridge.tlb_flush = intel_820_tlbflush;
        agp_bridge.mask_memory = intel_mask_memory;
        agp_bridge.agp_enable = agp_generic_agp_enable;
@@ -1819,6 +1863,35 @@
        (void) pdev; /* unused */
 }
 
+static int __init intel_830mp_setup (struct pci_dev *pdev)
+{
+       agp_bridge.masks = intel_generic_masks;
+       agp_bridge.num_of_masks = 1;
+       agp_bridge.aperture_sizes = (void *) intel_830mp_sizes;
+       agp_bridge.size_type = U8_APER_SIZE;
+       agp_bridge.num_aperture_sizes = 4;
+       agp_bridge.dev_private_data = NULL;
+       agp_bridge.needs_scratch_page = FALSE;
+       agp_bridge.configure = intel_830mp_configure;
+       agp_bridge.fetch_size = intel_8xx_fetch_size;
+       agp_bridge.cleanup = intel_8xx_cleanup;
+       agp_bridge.tlb_flush = intel_8xx_tlbflush;
+       agp_bridge.mask_memory = intel_mask_memory;
+       agp_bridge.agp_enable = agp_generic_agp_enable;
+       agp_bridge.cache_flush = global_cache_flush;
+       agp_bridge.create_gatt_table = agp_generic_create_gatt_table;
+       agp_bridge.free_gatt_table = agp_generic_free_gatt_table;
+       agp_bridge.insert_memory = agp_generic_insert_memory;
+       agp_bridge.remove_memory = agp_generic_remove_memory;
+       agp_bridge.alloc_by_type = agp_generic_alloc_by_type;
+       agp_bridge.free_by_type = agp_generic_free_by_type;
+       agp_bridge.agp_alloc_page = agp_generic_alloc_page;
+       agp_bridge.agp_destroy_page = agp_generic_destroy_page;
+
+       return 0;
+
+       (void) pdev; /* unused */
+}
 
 static int __init intel_840_setup (struct pci_dev *pdev)
 {
@@ -3572,12 +3645,18 @@
 		"Intel",
 		"i820",
 		intel_820_setup },
+        { PCI_DEVICE_ID_INTEL_820_UP_0,
+                PCI_VENDOR_ID_INTEL,
+                INTEL_I820,
+                "Intel",
+                 "i820",
+                 intel_820_setup },
 	{ PCI_DEVICE_ID_INTEL_830_M_0,
 		PCI_VENDOR_ID_INTEL,
 		INTEL_I830_M,
 		"Intel",
 		"i830M",
-		intel_generic_setup },
+		intel_830mp_setup },
 	{ PCI_DEVICE_ID_INTEL_840_0,
 		PCI_VENDOR_ID_INTEL,
 		INTEL_I840,
@@ -3904,11 +3983,13 @@
 			}
 
 			if (i810_dev == NULL) {
-				printk(KERN_ERR PFX "Detected an "
-					   "Intel 830M, but could not find the"
-					   " secondary device.\n");
-				agp_bridge.type = NOT_SUPPORTED;
-				return -ENODEV;
+                                /* 
+                                 * We probably have a I830MP chipset
+                                 * with an external graphics
+                                 * card. It will be initialized later 
+                                 */
+				agp_bridge.type = INTEL_I830_M;
+				break;
 			}
 			printk(KERN_INFO PFX "Detected an Intel "
 				   "830M Chipset.\n");
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/lp.c linux/drivers/char/lp.c
--- linux.orig/drivers/char/lp.c	Thu Oct 25 07:07:39 2001
+++ linux/drivers/char/lp.c	Mon Jan  7 13:38:04 2002
@@ -650,7 +650,7 @@
 	do {
 		/* Write the data, converting LF->CRLF as we go. */
 		ssize_t canwrite = count;
-		char *lf = strchr (s, '\n');
+		char *lf = memchr (s, '\n', count);
 		if (lf)
 			canwrite = lf - s;
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/n_hdlc.c linux/drivers/char/n_hdlc.c
--- linux.orig/drivers/char/n_hdlc.c	Mon Jan  7 23:41:08 2002
+++ linux/drivers/char/n_hdlc.c	Fri Dec 21 19:42:08 2001
@@ -9,7 +9,7 @@
  *	Al Longyear <longyear@netcom.com>, Paul Mackerras <Paul.Mackerras@cs.anu.edu.au>
  *
  * Original release 01/11/99
- * $Id: n_hdlc.c,v 3.2 2000/11/06 22:34:38 paul Exp $
+ * $Id: n_hdlc.c,v 3.3 2001/11/08 16:16:03 paulkf Exp $
  *
  * This code is released under the GNU General Public License (GPL)
  *
@@ -78,7 +78,7 @@
  */
 
 #define HDLC_MAGIC 0x239e
-#define HDLC_VERSION "3.2"
+#define HDLC_VERSION "$Revision: 3.3 $"
 
 #include <linux/version.h>
 #include <linux/config.h>
@@ -160,11 +160,6 @@
 	struct tty_struct *tty;		/* ptr to TTY structure	*/
 	struct tty_struct *backup_tty;	/* TTY to use if tty gets closed */
 	
-	/* Queues for select() functionality */
-	wait_queue_head_t read_wait;
-	wait_queue_head_t write_wait;
-	wait_queue_head_t poll_wait;
-
 	int		tbusy;		/* reentrancy flag for tx wakeup code */
 	int		woke_up;
 	N_HDLC_BUF	*tbuf;		/* currently transmitting tx buffer */
@@ -235,9 +230,8 @@
 		printk("%s(%d)n_hdlc_release() called\n",__FILE__,__LINE__);
 		
 	/* Ensure that the n_hdlcd process is not hanging on select()/poll() */
-	wake_up_interruptible (&n_hdlc->read_wait);
-	wake_up_interruptible (&n_hdlc->poll_wait);
-	wake_up_interruptible (&n_hdlc->write_wait);
+	wake_up_interruptible (&tty->read_wait);
+	wake_up_interruptible (&tty->write_wait);
 
 	if (tty != NULL && tty->disc_data == n_hdlc)
 		tty->disc_data = NULL;	/* Break the tty->n_hdlc link */
@@ -432,8 +426,7 @@
 			n_hdlc->tbuf = NULL;
 			
 			/* wait up sleeping writers */
-			wake_up_interruptible(&n_hdlc->write_wait);
-			wake_up_interruptible(&n_hdlc->poll_wait);
+			wake_up_interruptible(&tty->write_wait);
 	
 			/* get next pending transmit buffer */
 			tbuf = n_hdlc_buf_get(&n_hdlc->tx_buf_list);
@@ -575,8 +568,7 @@
 	n_hdlc_buf_put(&n_hdlc->rx_buf_list,buf);
 	
 	/* wake up any blocked reads and perform async signalling */
-	wake_up_interruptible (&n_hdlc->read_wait);
-	wake_up_interruptible (&n_hdlc->poll_wait);
+	wake_up_interruptible (&tty->read_wait);
 	if (n_hdlc->tty->fasync != NULL)
 		kill_fasync (&n_hdlc->tty->fasync, SIGIO, POLL_IN);
 
@@ -621,6 +613,9 @@
 	}
 
 	for (;;) {
+		if (test_bit(TTY_OTHER_CLOSED, &tty->flags))
+			return -EIO;
+
 		n_hdlc = tty2n_hdlc (tty);
 		if (!n_hdlc || n_hdlc->magic != HDLC_MAGIC ||
 			 tty != n_hdlc->tty)
@@ -634,7 +629,7 @@
 		if (file->f_flags & O_NONBLOCK)
 			return -EAGAIN;
 			
-		interruptible_sleep_on (&n_hdlc->read_wait);
+		interruptible_sleep_on (&tty->read_wait);
 		if (signal_pending(current))
 			return -EINTR;
 	}
@@ -703,7 +698,7 @@
 		count = maxframe;
 	}
 	
-	add_wait_queue(&n_hdlc->write_wait, &wait);
+	add_wait_queue(&tty->write_wait, &wait);
 	set_current_state(TASK_INTERRUPTIBLE);
 	
 	/* Allocate transmit buffer */
@@ -726,7 +721,7 @@
 	}
 
 	set_current_state(TASK_RUNNING);
-	remove_wait_queue(&n_hdlc->write_wait, &wait);
+	remove_wait_queue(&tty->write_wait, &wait);
 
 	if (!error) {		
 		/* Retrieve the user's buffer */
@@ -836,12 +831,14 @@
 	if (n_hdlc && n_hdlc->magic == HDLC_MAGIC && tty == n_hdlc->tty) {
 		/* queue current process into any wait queue that */
 		/* may awaken in the future (read and write) */
-		poll_wait(filp, &n_hdlc->poll_wait, wait);
+
+		poll_wait(filp, &tty->read_wait, wait);
+		poll_wait(filp, &tty->write_wait, wait);
 
 		/* set bits for operations that wont block */
 		if(n_hdlc->rx_buf_list.head)
 			mask |= POLLIN | POLLRDNORM;	/* readable */
-		if(tty->flags & (1 << TTY_OTHER_CLOSED))
+		if (test_bit(TTY_OTHER_CLOSED, &tty->flags))
 			mask |= POLLHUP;
 		if(tty_hung_up_p(filp))
 			mask |= POLLHUP;
@@ -895,11 +892,7 @@
 	
 	/* Initialize the control block */
 	n_hdlc->magic  = HDLC_MAGIC;
-
 	n_hdlc->flags  = 0;
-	init_waitqueue_head(&n_hdlc->read_wait);
-	init_waitqueue_head(&n_hdlc->poll_wait);
-	init_waitqueue_head(&n_hdlc->write_wait);
 	
 	return n_hdlc;
 	
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/ppdev.c linux/drivers/char/ppdev.c
--- linux.orig/drivers/char/ppdev.c	Thu Sep 13 22:21:32 2001
+++ linux/drivers/char/ppdev.c	Mon Jan  7 13:38:04 2002
@@ -324,6 +324,7 @@
 	case PPCLAIM:
 	    {
 		struct ieee1284_info *info;
+		int ret;
 
 		if (pp->flags & PP_CLAIMED) {
 			printk (KERN_DEBUG CHRDEV
@@ -339,7 +340,10 @@
 			}
 		}
 
-		parport_claim_or_block (pp->pdev);
+		ret = parport_claim_or_block (pp->pdev);
+		if (ret < 0)
+			return ret;
+
 		pp->flags |= PP_CLAIMED;
 
 		/* For interrupt-reporting to work, we need to be
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/serial.c linux/drivers/char/serial.c
--- linux.orig/drivers/char/serial.c	Mon Jan  7 23:41:08 2002
+++ linux/drivers/char/serial.c	Wed Dec 26 16:47:47 2001
@@ -231,8 +231,8 @@
 #include <asm/irq.h>
 #include <asm/bitops.h>
 
-#ifdef CONFIG_MAC_SERIAL
-#define SERIAL_DEV_OFFSET	2
+#if defined(CONFIG_MAC_SERIAL)
+#define SERIAL_DEV_OFFSET	((_machine == _MACH_prep || _machine == _MACH_chrp) ? 0 : 2)
 #else
 #define SERIAL_DEV_OFFSET	0
 #endif
@@ -5393,6 +5393,7 @@
 #endif
 	serial_driver.major = TTY_MAJOR;
 	serial_driver.minor_start = 64 + SERIAL_DEV_OFFSET;
+	serial_driver.name_base = SERIAL_DEV_OFFSET;
 	serial_driver.num = NR_PORTS;
 	serial_driver.type = TTY_DRIVER_TYPE_SERIAL;
 	serial_driver.subtype = SERIAL_TYPE_NORMAL;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/shwdt.c linux/drivers/char/shwdt.c
--- linux.orig/drivers/char/shwdt.c	Mon Jan  7 23:41:08 2002
+++ linux/drivers/char/shwdt.c	Fri Dec 21 18:00:38 2001
@@ -10,7 +10,6 @@
  * Free Software Foundation; either version 2 of the License, or (at your
  * option) any later version.
  */
-
 #include <linux/config.h>
 #include <linux/module.h>
 #include <linux/init.h>
@@ -177,7 +176,7 @@
  * 	sh_wdt_read - Read from Device
  *
  * 	@file: file handle of device
- * 	@char: buffer to write to
+ * 	@buf: buffer to write to
  * 	@count: length of buffer
  * 	@ppos: offset
  *
@@ -193,7 +192,7 @@
  * 	sh_wdt_write - Write to Device
  *
  * 	@file: file handle of device
- * 	@char: buffer to write
+ * 	@buf: buffer to write
  * 	@count: length of buffer
  * 	@ppos: offset
  *
@@ -269,7 +268,7 @@
 static int sh_wdt_notify_sys(struct notifier_block *this,
 			     unsigned long code, void *unused)
 {
-	if (code == SYS_DOWN || SYS_HALT) {
+	if (code == SYS_DOWN || code == SYS_HALT) {
 		sh_wdt_stop();
 	}
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/sonypi.c linux/drivers/char/sonypi.c
--- linux.orig/drivers/char/sonypi.c	Fri Dec 21 17:41:54 2001
+++ linux/drivers/char/sonypi.c	Mon Jan  7 13:03:04 2002
@@ -109,25 +109,29 @@
         return result;
 }
 
-static void sonypi_ecrset(u16 addr, u16 value) {
+static void sonypi_ecrset(u8 addr, u8 value) {
 
-	wait_on_command(1, inw_p(SONYPI_CST_IOPORT) & 3);
-	outw_p(0x81, SONYPI_CST_IOPORT);
-	wait_on_command(0, inw_p(SONYPI_CST_IOPORT) & 2);
-	outw_p(addr, SONYPI_DATA_IOPORT);
-	wait_on_command(0, inw_p(SONYPI_CST_IOPORT) & 2);
-	outw_p(value, SONYPI_DATA_IOPORT);
-	wait_on_command(0, inw_p(SONYPI_CST_IOPORT) & 2);
-}
-
-static u16 sonypi_ecrget(u16 addr) {
-
-	wait_on_command(1, inw_p(SONYPI_CST_IOPORT) & 3);
-	outw_p(0x80, SONYPI_CST_IOPORT);
-	wait_on_command(0, inw_p(SONYPI_CST_IOPORT) & 2);
-	outw_p(addr, SONYPI_DATA_IOPORT);
-	wait_on_command(0, inw_p(SONYPI_CST_IOPORT) & 2);
-	return inw_p(SONYPI_DATA_IOPORT);
+	wait_on_command(1, inb_p(SONYPI_CST_IOPORT) & 3);
+	outb_p(0x81, SONYPI_CST_IOPORT);
+	wait_on_command(0, inb_p(SONYPI_CST_IOPORT) & 2);
+	outb_p(addr, SONYPI_DATA_IOPORT);
+	wait_on_command(0, inb_p(SONYPI_CST_IOPORT) & 2);
+	outb_p(value, SONYPI_DATA_IOPORT);
+	wait_on_command(0, inb_p(SONYPI_CST_IOPORT) & 2);
+}
+
+static u8 sonypi_ecrget(u8 addr) {
+
+	wait_on_command(1, inb_p(SONYPI_CST_IOPORT) & 3);
+	outb_p(0x80, SONYPI_CST_IOPORT);
+	wait_on_command(0, inb_p(SONYPI_CST_IOPORT) & 2);
+	outb_p(addr, SONYPI_DATA_IOPORT);
+	wait_on_command(0, inb_p(SONYPI_CST_IOPORT) & 2);
+	return inb_p(SONYPI_DATA_IOPORT);
+}
+
+static u16 sonypi_ecrget16(u8 addr) {
+	return sonypi_ecrget(addr) | (sonypi_ecrget(addr + 1) << 8);
 }
 
 /* Initializes the device - this comes from the AML code in the ACPI bios */
@@ -510,24 +514,60 @@
 static int sonypi_misc_ioctl(struct inode *ip, struct file *fp, 
 			     unsigned int cmd, unsigned long arg) {
 	int ret = 0;
-	u8 val;
+	u8 val8;
+	u16 val16;
 
 	down(&sonypi_device.lock);
 	switch (cmd) {
-		case SONYPI_IOCGBRT:
-			val = sonypi_ecrget(0x96) & 0xff;
-			if (copy_to_user((u8 *)arg, &val, sizeof(val))) {
-				ret = -EFAULT;
-				goto out;
-			}
-			break;
-		case SONYPI_IOCSBRT:
-			if (copy_from_user(&val, (u8 *)arg, sizeof(val))) {
-				ret = -EFAULT;
-				goto out;
-			}
-			sonypi_ecrset(0x96, val);
-			break;
+	case SONYPI_IOCGBRT:
+		val8 = sonypi_ecrget(0x96);
+		if (copy_to_user((u8 *)arg, &val8, sizeof(val8))) {
+			ret = -EFAULT;
+			goto out;
+		}
+		break;
+	case SONYPI_IOCSBRT:
+		if (copy_from_user(&val8, (u8 *)arg, sizeof(val8))) {
+			ret = -EFAULT;
+			goto out;
+		}
+		sonypi_ecrset(0x96, val8);
+		break;
+	case SONYPI_IOCGBAT1CAP:
+		val16 = sonypi_ecrget16(0xb2);
+		if (copy_to_user((u16 *)arg, &val16, sizeof(val16))) {
+			ret = -EFAULT;
+			goto out;
+		}
+		break;
+	case SONYPI_IOCGBAT1REM:
+		val16 = sonypi_ecrget16(0xa2);
+		if (copy_to_user((u16 *)arg, &val16, sizeof(val16))) {
+			ret = -EFAULT;
+			goto out;
+		}
+		break;
+	case SONYPI_IOCGBAT2CAP:
+		val16 = sonypi_ecrget16(0xba);
+		if (copy_to_user((u16 *)arg, &val16, sizeof(val16))) {
+			ret = -EFAULT;
+			goto out;
+		}
+		break;
+	case SONYPI_IOCGBAT2REM:
+		val16 = sonypi_ecrget16(0xaa);
+		if (copy_to_user((u16 *)arg, &val16, sizeof(val16))) {
+			ret = -EFAULT;
+			goto out;
+		}
+		break;
+	case SONYPI_IOCGBATFLAGS:
+		val8 = sonypi_ecrget(0x81) & 0x07;
+		if (copy_to_user((u8 *)arg, &val8, sizeof(val8))) {
+			ret = -EFAULT;
+			goto out;
+		}
+		break;
 	default:
 		ret = -EINVAL;
 	}
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/sonypi.h linux/drivers/char/sonypi.h
--- linux.orig/drivers/char/sonypi.h	Fri Dec 21 17:41:54 2001
+++ linux/drivers/char/sonypi.h	Mon Jan  7 13:03:04 2002
@@ -35,7 +35,7 @@
 #ifdef __KERNEL__
 
 #define SONYPI_DRIVER_MAJORVERSION	1
-#define SONYPI_DRIVER_MINORVERSION	8
+#define SONYPI_DRIVER_MINORVERSION	9
 
 #include <linux/types.h>
 #include <linux/pci.h>
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/sx.c linux/drivers/char/sx.c
--- linux.orig/drivers/char/sx.c	Fri Nov  9 22:01:21 2001
+++ linux/drivers/char/sx.c	Mon Jan  7 13:50:35 2002
@@ -1160,7 +1160,8 @@
 				/* DCD went UP */
 				if( (~(port->gs.flags & ASYNC_NORMAL_ACTIVE) || 
 						 ~(port->gs.flags & ASYNC_CALLOUT_ACTIVE)) &&
-						(sx_read_channel_byte(port, hi_hstat) != HS_IDLE_CLOSED)) {
+						(sx_read_channel_byte(port, hi_hstat) != HS_IDLE_CLOSED) &&
+						!(port->gs.tty->termios->c_cflag & CLOCAL) ) {
 					/* Are we blocking in open?*/
 					sx_dprintk (SX_DEBUG_MODEMSIGNALS, "DCD active, unblocking open\n");
 					wake_up_interruptible(&port->gs.open_wait);
@@ -1170,7 +1171,8 @@
 			} else {
 				/* DCD went down! */
 				if (!((port->gs.flags & ASYNC_CALLOUT_ACTIVE) &&
-				      (port->gs.flags & ASYNC_CALLOUT_NOHUP))) {
+				      (port->gs.flags & ASYNC_CALLOUT_NOHUP)) &&
+				    !(port->gs.tty->termios->c_cflag & CLOCAL) ) {
 					sx_dprintk (SX_DEBUG_MODEMSIGNALS, "DCD dropped. hanging up....\n");
 					tty_hangup (port->gs.tty);
 				} else {
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/tty_io.c linux/drivers/char/tty_io.c
--- linux.orig/drivers/char/tty_io.c	Mon Jan  7 23:41:08 2002
+++ linux/drivers/char/tty_io.c	Wed Dec 26 16:47:47 2001
@@ -2192,6 +2192,11 @@
 #ifdef CONFIG_SERIAL_CONSOLE
 #if (defined(CONFIG_8xx) || defined(CONFIG_8260))
 	console_8xx_init();
+#elif defined(CONFIG_MAC_SERIAL) && defined(CONFIG_SERIAL)
+	if (_machine == _MACH_Pmac)
+ 		mac_scc_console_init();
+	else
+		serial_console_init();
 #elif defined(CONFIG_MAC_SERIAL)
  	mac_scc_console_init();
 #elif defined(CONFIG_PARISC)
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/i2c/Config.in linux/drivers/i2c/Config.in
--- linux.orig/drivers/i2c/Config.in	Mon Jan  7 23:41:08 2002
+++ linux/drivers/i2c/Config.in	Wed Dec 26 16:31:13 2001
@@ -39,6 +39,10 @@
       fi
    fi
 
+   if [ "$CONFIG_ALL_PPC" = "y" ] ; then
+      dep_tristate 'Keywest I2C interface in Apple Core99 machines' CONFIG_I2C_KEYWEST $CONFIG_I2C
+   fi
+
 # This is needed for automatic patch generation: sensors code starts here
 # This is needed for automatic patch generation: sensors code ends here
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/i2c/Makefile linux/drivers/i2c/Makefile
--- linux.orig/drivers/i2c/Makefile	Mon Jan  7 23:41:08 2002
+++ linux/drivers/i2c/Makefile	Wed Dec 26 16:31:13 2001
@@ -18,6 +18,7 @@
 obj-$(CONFIG_ITE_I2C_ALGO)	+= i2c-algo-ite.o
 obj-$(CONFIG_ITE_I2C_ADAP)	+= i2c-adap-ite.o
 obj-$(CONFIG_I2C_PROC)		+= i2c-proc.o
+obj-$(CONFIG_I2C_KEYWEST)	+= i2c-keywest.o
 
 # This is needed for automatic patch generation: sensors code starts here
 # This is needed for automatic patch generation: sensors code ends here
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/i2c/i2c-keywest.c linux/drivers/i2c/i2c-keywest.c
--- linux.orig/drivers/i2c/i2c-keywest.c	Thu Jan  1 00:00:00 1970
+++ linux/drivers/i2c/i2c-keywest.c	Wed Dec 26 16:31:13 2001
@@ -0,0 +1,671 @@
+/*
+    i2c Support for Apple Keywest I2C Bus Controller
+
+    Copyright (c) 2001 Benjamin Herrenschmidt <benh@kernel.crashing.org>
+
+    Original work by
+    
+    Copyright (c) 2000 Philip Edelbrock <phil@stimpy.netroedge.com>
+
+    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.
+
+    Changes:
+
+    2001/12/13 BenH	New implementation
+    2001/12/15 BenH	Add support for "byte" and "quick"
+                        transfers. Add i2c_xfer routine.
+
+    ToDo:
+
+    Figure out how to use combined mode on the chip      
+
+*/
+
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/timer.h>
+#include <linux/spinlock.h>
+#include <linux/completion.h>
+
+#include <asm/io.h>
+#include <asm/prom.h>
+#include <asm/machdep.h>
+#include <asm/pmac_feature.h>
+
+#include "i2c-keywest.h"
+
+#undef POLLED_MODE
+
+#define DBG(x...) do {\
+	if (debug > 0) \
+		printk(KERN_DEBUG "KW:" x); \
+	} while(0)
+
+
+MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
+MODULE_DESCRIPTION("I2C driver for Apple's Keywest");
+MODULE_LICENSE("GPL");
+MODULE_PARM(probe, "i");
+MODULE_PARM(debug, "i");
+EXPORT_NO_SYMBOLS;
+
+int probe = 0;
+int debug = 0;
+
+static struct keywest_iface *ifaces = NULL;
+
+#ifdef POLLED_MODE
+/* This isn't fast, but will go once I implement interrupt with
+ * proper timeout
+ */
+static u8
+wait_interrupt(struct keywest_iface* iface)
+{
+	int i;
+	u8 isr;
+	
+	for (i = 0; i < POLL_TIMEOUT; i++) {
+		isr = read_reg(reg_isr) & KW_I2C_IRQ_MASK;
+		if (isr != 0)
+			return isr;
+		current->state = TASK_UNINTERRUPTIBLE;
+		schedule_timeout(1);
+	}
+	return isr;
+}
+#endif /* POLLED_MODE */
+
+
+static void
+do_stop(struct keywest_iface* iface, int result)
+{
+	write_reg(reg_control, read_reg(reg_control) | KW_I2C_CTL_STOP);
+	iface->state = state_stop;
+	iface->result = result;
+}
+
+/* Main state machine for standard & standard sub mode */
+static void
+handle_interrupt(struct keywest_iface *iface, u8 isr)
+{
+	int ack;
+	
+	DBG("handle_interrupt(), got: %x, status: %x, state: %d\n",
+		isr, read_reg(reg_status), iface->state);
+	if (isr == 0 && iface->state != state_stop) {
+		do_stop(iface, -1);
+		return;
+	}
+	if (isr & KW_I2C_IRQ_STOP && iface->state != state_stop) {
+		iface->result = -1;
+		iface->state = state_stop;
+	}
+	switch(iface->state) {
+	case state_addr:
+		if (!(isr & KW_I2C_IRQ_ADDR)) {
+			do_stop(iface, -1);
+			break;
+		}
+		ack = read_reg(reg_status);
+		DBG("ack on set address: %x\n", ack);
+		if ((ack & KW_I2C_STAT_LAST_AAK) == 0) {
+			do_stop(iface, -1);
+			break;
+		}
+		/* Handle rw "quick" mode */
+		if (iface->datalen == 0)
+			do_stop(iface, 0);
+		else if (iface->read_write == I2C_SMBUS_READ) {
+			iface->state = state_read;
+			if (iface->datalen > 1)
+				write_reg(reg_control, read_reg(reg_control)
+					| KW_I2C_CTL_AAK);
+		} else {
+			iface->state = state_write;
+			DBG("write byte: %x\n", *(iface->data));
+			write_reg(reg_data, *(iface->data++));
+			iface->datalen--;
+		}
+		
+		break;
+	case state_read:
+		if (!(isr & KW_I2C_IRQ_DATA)) {
+			do_stop(iface, -1);
+			break;
+		}
+		*(iface->data++) = read_reg(reg_data);
+		DBG("read byte: %x\n", *(iface->data-1));
+		iface->datalen--;
+		if (iface->datalen == 0)
+			iface->state = state_stop;
+		else
+			write_reg(reg_control, 0);
+		break;
+	case state_write:
+		if (!(isr & KW_I2C_IRQ_DATA)) {
+			do_stop(iface, -1);
+			break;
+		}
+		/* Check ack status */
+		ack = read_reg(reg_status);
+		DBG("ack on data write: %x\n", ack);
+		if ((ack & KW_I2C_STAT_LAST_AAK) == 0) {
+			do_stop(iface, -1);
+			break;
+		}
+		if (iface->datalen) {
+			DBG("write byte: %x\n", *(iface->data));
+			write_reg(reg_data, *(iface->data++));
+			iface->datalen--;
+		} else
+			do_stop(iface, 0);
+		break;
+		
+	case state_stop:
+		if (!(isr & KW_I2C_IRQ_STOP) && (++iface->stopretry) < 10)
+			do_stop(iface, -1);
+		else {
+			iface->state = state_idle;
+			write_reg(reg_control, 0x00);
+			write_reg(reg_ier, 0x00);
+#ifndef POLLED_MODE
+			complete(&iface->complete);
+#endif /* POLLED_MODE */			
+		}
+		break;
+	}
+	
+	write_reg(reg_isr, isr);
+}
+
+#ifndef POLLED_MODE
+
+/* Interrupt handler */
+static void
+keywest_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct keywest_iface *iface = (struct keywest_iface *)dev_id;
+
+	spin_lock(&iface->lock);
+	del_timer(&iface->timeout_timer);
+	handle_interrupt(iface, read_reg(reg_isr));
+	if (iface->state != state_idle) {
+		iface->timeout_timer.expires = jiffies + POLL_TIMEOUT;
+		add_timer(&iface->timeout_timer);
+	}
+	spin_unlock(&iface->lock);
+}
+
+static void
+keywest_timeout(unsigned long data)
+{
+	struct keywest_iface *iface = (struct keywest_iface *)data;
+
+	DBG("timeout !\n");
+	spin_lock_irq(&iface->lock);
+	handle_interrupt(iface, read_reg(reg_isr));
+	if (iface->state != state_idle) {
+		iface->timeout_timer.expires = jiffies + POLL_TIMEOUT;
+		add_timer(&iface->timeout_timer);
+	}
+	spin_unlock(&iface->lock);
+}
+
+#endif /* POLLED_MODE */
+
+/*
+ * SMBUS-type transfer entrypoint
+ */
+static s32
+keywest_smbus_xfer(	struct i2c_adapter*	adap,
+			u16			addr,
+			unsigned short		flags,
+			char			read_write,
+			u8			command,
+			int			size,
+			union i2c_smbus_data*	data)
+{
+	struct keywest_chan* chan = (struct keywest_chan*)adap->data;
+	struct keywest_iface* iface = chan->iface;
+	int len;
+	u8* buffer;
+	u16 cur_word;
+	int rc = 0;
+
+	if (iface->state == state_dead)
+		return -1;
+		
+	/* Prepare datas & select mode */
+	iface->cur_mode &= ~KW_I2C_MODE_MODE_MASK;
+	switch (size) {
+	    case I2C_SMBUS_QUICK:
+	    	len = 0;
+	    	buffer = NULL;
+	    	iface->cur_mode |= KW_I2C_MODE_STANDARD;
+	    	break;
+	    case I2C_SMBUS_BYTE:
+	    	len = 1;
+	    	buffer = &data->byte;
+	    	iface->cur_mode |= KW_I2C_MODE_STANDARD;
+	    	break;
+	    case I2C_SMBUS_BYTE_DATA:
+	    	len = 1;
+	    	buffer = &data->byte;
+	    	iface->cur_mode |= KW_I2C_MODE_STANDARDSUB;
+	    	break;
+	    case I2C_SMBUS_WORD_DATA:
+	    	len = 2;
+	    	cur_word = cpu_to_le16(data->word);
+	    	buffer = (u8 *)&cur_word;
+	    	iface->cur_mode |= KW_I2C_MODE_STANDARDSUB;
+		break;
+	    case I2C_SMBUS_BLOCK_DATA:
+	    	len = data->block[0];
+	    	buffer = &data->block[1];
+	    	iface->cur_mode |= KW_I2C_MODE_STANDARDSUB;
+		break;
+	    default:
+	    	return -1;
+	}
+
+	/* Original driver had this limitation */
+	if (len > 32)
+		len = 32;
+
+	down(&iface->sem);
+
+	DBG("chan: %d, addr: 0x%x, transfer len: %d, read: %d\n",
+		chan->chan_no, addr, len, read_write == I2C_SMBUS_READ);
+
+	iface->data = buffer;
+	iface->datalen = len;
+	iface->state = state_addr;
+	iface->result = 0;
+	iface->stopretry = 0;
+	iface->read_write = read_write;
+	
+	/* Setup channel & clear pending irqs */
+	write_reg(reg_mode, iface->cur_mode | (chan->chan_no << 4));
+	write_reg(reg_isr, read_reg(reg_isr));
+	write_reg(reg_status, 0);
+
+	/* Set up address and r/w bit */
+	write_reg(reg_addr,
+		(addr << 1) | ((read_write == I2C_SMBUS_READ) ? 0x01 : 0x00));
+
+	/* Set up the sub address */
+	if ((iface->cur_mode & KW_I2C_MODE_MODE_MASK) == KW_I2C_MODE_STANDARDSUB
+	    || (iface->cur_mode & KW_I2C_MODE_MODE_MASK) == KW_I2C_MODE_COMBINED)
+		write_reg(reg_subaddr, command);
+
+	/* Arm timeout */
+	iface->timeout_timer.expires = jiffies + POLL_TIMEOUT;
+	add_timer(&iface->timeout_timer);
+
+	/* Start sending address & enable interrupt*/
+	write_reg(reg_control, read_reg(reg_control) | KW_I2C_CTL_XADDR);
+	write_reg(reg_ier, KW_I2C_IRQ_MASK);
+
+#ifdef POLLED_MODE
+	DBG("using polled mode...\n");
+	/* State machine, to turn into an interrupt handler */
+	while(iface->state != state_idle) {
+		u8 isr = wait_interrupt(iface);
+		handle_interrupt(iface, isr);
+	}
+#else /* POLLED_MODE */
+	DBG("using interrupt mode...\n");
+	wait_for_completion(&iface->complete);	
+#endif /* POLLED_MODE */	
+
+	rc = iface->result;	
+	DBG("transfer done, result: %d\n", rc);
+
+	if (rc == 0 && size == I2C_SMBUS_WORD_DATA && read_write == I2C_SMBUS_READ)
+	    	data->word = le16_to_cpu(cur_word);
+	
+	/* Release sem */
+	up(&iface->sem);
+	
+	return rc;
+}
+
+/*
+ * Generic i2c master transfer entrypoint
+ */
+static int
+keywest_xfer(	struct i2c_adapter *adap,
+		struct i2c_msg msgs[], 
+		int num)
+{
+	struct keywest_chan* chan = (struct keywest_chan*)adap->data;
+	struct keywest_iface* iface = chan->iface;
+	struct i2c_msg *pmsg;
+	int i, completed;
+	int rc = 0;
+
+	down(&iface->sem);
+
+	/* Set adapter to standard mode */
+	iface->cur_mode &= ~KW_I2C_MODE_MODE_MASK;
+	iface->cur_mode |= KW_I2C_MODE_STANDARD;
+
+	completed = 0;
+	for (i = 0; rc >= 0 && i < num;) {
+		u8 addr;
+		
+		pmsg = &msgs[i++];
+		addr = pmsg->addr;
+		if (pmsg->flags & I2C_M_TEN) {
+			printk(KERN_ERR "i2c-keywest: 10 bits addr not supported !\n");
+			rc = -EINVAL;
+			break;
+		}
+		DBG("xfer: chan: %d, doing %s %d bytes to 0x%02x - %d of %d messages\n",
+		     chan->chan_no,
+		     pmsg->flags & I2C_M_RD ? "read" : "write",
+                     pmsg->len, addr, i, num);
+    
+		/* Setup channel & clear pending irqs */
+		write_reg(reg_mode, iface->cur_mode | (chan->chan_no << 4));
+		write_reg(reg_isr, read_reg(reg_isr));
+		write_reg(reg_status, 0);
+		
+		iface->data = pmsg->buf;
+		iface->datalen = pmsg->len;
+		iface->state = state_addr;
+		iface->result = 0;
+		iface->stopretry = 0;
+		if (pmsg->flags & I2C_M_RD)
+			iface->read_write = I2C_SMBUS_READ;
+		else
+			iface->read_write = I2C_SMBUS_WRITE;
+
+		/* Set up address and r/w bit */
+		if (pmsg->flags & I2C_M_REV_DIR_ADDR)
+			addr ^= 1;		
+		write_reg(reg_addr,
+			(addr << 1) |
+			((iface->read_write == I2C_SMBUS_READ) ? 0x01 : 0x00));
+
+		/* Arm timeout */
+		iface->timeout_timer.expires = jiffies + POLL_TIMEOUT;
+		add_timer(&iface->timeout_timer);
+
+		/* Start sending address & enable interrupt*/
+		write_reg(reg_control, read_reg(reg_control) | KW_I2C_CTL_XADDR);
+		write_reg(reg_ier, KW_I2C_IRQ_MASK);
+
+#ifdef POLLED_MODE
+		DBG("using polled mode...\n");
+		/* State machine, to turn into an interrupt handler */
+		while(iface->state != state_idle) {
+			u8 isr = wait_interrupt(iface);
+			handle_interrupt(iface, isr);
+		}
+#else /* POLLED_MODE */88
+		DBG("using interrupt mode...\n");
+		wait_for_completion(&iface->complete);	
+#endif /* POLLED_MODE */	
+
+		rc = iface->result;
+		if (rc == 0)
+			completed++;
+		DBG("transfer done, result: %d\n", rc);
+	}
+
+	/* Release sem */
+	up(&iface->sem);
+
+	return completed;
+}
+
+static u32
+keywest_func(struct i2c_adapter * adapter)
+{
+	return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
+	       I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
+	       I2C_FUNC_SMBUS_BLOCK_DATA;
+}
+
+static void
+keywest_inc(struct i2c_adapter *adapter)
+{
+	MOD_INC_USE_COUNT;
+}
+
+static void
+keywest_dec(struct i2c_adapter *adapter)
+{
+	MOD_DEC_USE_COUNT;
+}
+
+/* For now, we only handle combined mode (smbus) */
+static struct i2c_algorithm keywest_algorithm = {
+	name:		"Keywest i2c",
+	id:		I2C_ALGO_SMBUS,
+	smbus_xfer:	keywest_smbus_xfer,
+	master_xfer:	keywest_xfer,
+	functionality:	keywest_func,
+};
+
+
+static int
+create_iface(struct device_node* np)
+{
+	unsigned long steps, *psteps, *prate;
+	unsigned bsteps, tsize, i, nchan, addroffset;
+	struct keywest_iface* iface;
+	int rc;
+
+	psteps = (unsigned long *)get_property(np, "AAPL,address-step", NULL);
+	steps = psteps ? (*psteps) : 0x10;
+
+	/* Hrm... maybe we can be smarter here */
+	for (bsteps = 0; (steps & 0x01) == 0; bsteps++)
+		steps >>= 1;
+
+	/* For now, we declare 4 channels on uninorth and 1 on keylargo */
+	if (!strcmp(np->parent->name, "uni-n")) {
+		nchan = 4;
+		addroffset = 3;
+	} else {
+		nchan = 1;
+		addroffset = 0;
+	}
+
+	tsize = sizeof(struct keywest_iface) +
+		(sizeof(struct keywest_chan) + 4) * nchan;
+	iface = (struct keywest_iface *) kmalloc(tsize, GFP_KERNEL);
+	if (iface == NULL) {
+		printk(KERN_ERR "i2c-keywest: can't allocate inteface !\n");
+		return -ENOMEM;
+	}
+	memset(iface, 0, tsize);
+	init_MUTEX(&iface->sem);
+	spin_lock_init(&iface->lock);
+	init_completion(&iface->complete);
+	iface->bsteps = bsteps;
+	iface->chan_count = nchan;
+	iface->state = state_idle;
+	iface->irq = np->intrs[0].line;
+	iface->channels = (struct keywest_chan *)
+		(((unsigned long)(iface + 1) + 3UL) & ~3UL);
+	iface->base = (unsigned long)ioremap(np->addrs[0].address + addroffset,
+						np->addrs[0].size);
+	if (iface->base == 0) {
+		printk(KERN_ERR "i2c-keywest: can't map inteface !\n");
+		kfree(iface);
+		return -ENOMEM;
+	}
+
+	init_timer(&iface->timeout_timer);
+	iface->timeout_timer.function = keywest_timeout;
+	iface->timeout_timer.data = (unsigned long)iface;
+
+	/* Select interface rate */
+	iface->cur_mode = KW_I2C_MODE_100KHZ;
+	prate = (unsigned long *)get_property(np, "AAPL,i2c-rate", NULL);
+	if (prate) switch(*prate) {
+	case 100:
+		iface->cur_mode = KW_I2C_MODE_100KHZ;
+		break;
+	case 50:
+		iface->cur_mode = KW_I2C_MODE_50KHZ;
+		break;
+	case 25:
+		iface->cur_mode = KW_I2C_MODE_25KHZ;
+		break;
+	default:
+		printk(KERN_WARNING "i2c-keywest: unknown rate %ldKhz, using 100KHz\n",
+			*prate);
+	}
+	
+	/* Select standard sub mode */
+	iface->cur_mode |= KW_I2C_MODE_STANDARDSUB;
+	
+	/* Write mode */
+	write_reg(reg_mode, iface->cur_mode);
+	
+	/* Switch interrupts off & clear them*/
+	write_reg(reg_ier, 0x00);
+	write_reg(reg_isr, KW_I2C_IRQ_MASK);
+
+#ifndef POLLED_MODE
+	/* Request chip interrupt */	
+	rc = request_irq(iface->irq, keywest_irq, 0, "keywest i2c", iface);
+	if (rc) {
+		printk(KERN_ERR "i2c-keywest: can't get IRQ %d !\n", iface->irq);
+		iounmap((void *)iface->base);
+		kfree(iface);
+		return -ENODEV;
+	}
+#endif /* POLLED_MODE */
+
+	for (i=0; i<nchan; i++) {
+		struct keywest_chan* chan = &iface->channels[i];
+		u8 addr;
+		
+		sprintf(chan->adapter.name, "%s %d", np->parent->name, i);
+		chan->iface = iface;
+		chan->chan_no = i;
+		chan->adapter.id = I2C_ALGO_SMBUS;
+		chan->adapter.algo = &keywest_algorithm;
+		chan->adapter.algo_data = NULL;
+		chan->adapter.inc_use = keywest_inc;
+		chan->adapter.dec_use = keywest_dec;
+		chan->adapter.client_register = NULL;
+		chan->adapter.client_unregister = NULL;
+		chan->adapter.data = chan;
+
+		rc = i2c_add_adapter(&chan->adapter);
+		if (rc) {
+			printk("i2c-keywest.c: Adapter %s registration failed\n",
+				chan->adapter.name);
+			chan->adapter.data = NULL;
+		}
+		if (probe) {
+			printk("Probe: ");
+			for (addr = 0x00; addr <= 0x7f; addr++) {
+				if (i2c_smbus_xfer(&chan->adapter,addr,
+				    0,0,0,I2C_SMBUS_QUICK,NULL) >= 0)
+					printk("%02x ", addr);
+			}
+			printk("\n");
+		}
+	}
+
+	printk(KERN_INFO "Found KeyWest i2c on \"%s\", %d channel%s, stepping: %d bits\n",
+		np->parent->name, nchan, nchan > 1 ? "s" : "", bsteps);
+		
+	iface->next = ifaces;
+	ifaces = iface;
+	return 0;
+}
+
+static void
+dispose_iface(struct keywest_iface *iface)
+{
+	int i, rc;
+	
+	ifaces = iface->next;
+
+	/* Make sure we stop all activity */
+	down(&iface->sem);
+#ifndef POLLED_MODE
+	spin_lock_irq(&iface->lock);
+	while (iface->state != state_idle) {
+		spin_unlock_irq(&iface->lock);
+		schedule();
+		spin_lock_irq(&iface->lock);
+	}
+#endif /* POLLED_MODE */
+	iface->state = state_dead;
+#ifndef POLLED_MODE
+	spin_unlock_irq(&iface->lock);
+	free_irq(iface->irq, iface);
+#endif /* POLLED_MODE */
+	up(&iface->sem);
+
+	/* Release all channels */
+	for (i=0; i<iface->chan_count; i++) {
+		struct keywest_chan* chan = &iface->channels[i];
+		if (!chan->adapter.data)
+			continue;
+		rc = i2c_del_adapter(&chan->adapter);
+		chan->adapter.data = NULL;
+		/* We aren't that prepared to deal with this... */
+		if (rc)
+			printk("i2c-keywest.c: i2c_del_adapter failed, that's bad !\n");
+	}
+	iounmap((void *)iface->base);
+	kfree(iface);
+}
+
+static int __init
+i2c_keywest_init(void)
+{
+	struct device_node *np;
+	int rc = -ENODEV;
+	
+	np = find_compatible_devices("i2c", "keywest");
+	while (np != 0) {
+		if (np->n_addrs >= 1 && np->n_intrs >= 1)
+			rc = create_iface(np);
+		np = np->next;
+	}
+	if (ifaces)
+		rc = 0;
+	return rc;
+}
+
+static void __exit
+i2c_keywest_cleanup(void)
+{
+	while(ifaces)
+		dispose_iface(ifaces);
+}
+
+module_init(i2c_keywest_init);
+module_exit(i2c_keywest_cleanup);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/i2c/i2c-keywest.h linux/drivers/i2c/i2c-keywest.h
--- linux.orig/drivers/i2c/i2c-keywest.h	Thu Jan  1 00:00:00 1970
+++ linux/drivers/i2c/i2c-keywest.h	Wed Dec 26 16:31:13 2001
@@ -0,0 +1,111 @@
+#ifndef __I2C_KEYWEST_H__
+#define __I2C_KEYWEST_H__
+
+/* The Tumbler audio equalizer can be really slow sometimes */
+#define POLL_TIMEOUT		(2*HZ)
+
+/* Register indices */
+typedef enum {
+	reg_mode = 0,
+	reg_control,
+	reg_status,
+	reg_isr,
+	reg_ier,
+	reg_addr,
+	reg_subaddr,
+	reg_data
+} reg_t;
+
+
+/* Mode register */
+#define KW_I2C_MODE_100KHZ	0x00
+#define KW_I2C_MODE_50KHZ	0x01
+#define KW_I2C_MODE_25KHZ	0x02
+#define KW_I2C_MODE_DUMB	0x00
+#define KW_I2C_MODE_STANDARD	0x04
+#define KW_I2C_MODE_STANDARDSUB	0x08
+#define KW_I2C_MODE_COMBINED	0x0C
+#define KW_I2C_MODE_MODE_MASK	0x0C
+#define KW_I2C_MODE_CHAN_MASK	0xF0
+
+/* Control register */
+#define KW_I2C_CTL_AAK		0x01
+#define KW_I2C_CTL_XADDR	0x02
+#define KW_I2C_CTL_STOP		0x04
+#define KW_I2C_CTL_START	0x08
+
+/* Status register */
+#define KW_I2C_STAT_BUSY	0x01
+#define KW_I2C_STAT_LAST_AAK	0x02
+#define KW_I2C_STAT_LAST_RW	0x04
+#define KW_I2C_STAT_SDA		0x08
+#define KW_I2C_STAT_SCL		0x10
+
+/* IER & ISR registers */
+#define KW_I2C_IRQ_DATA		0x01
+#define KW_I2C_IRQ_ADDR		0x02
+#define KW_I2C_IRQ_STOP		0x04
+#define KW_I2C_IRQ_START	0x08
+#define KW_I2C_IRQ_MASK		0x0F
+
+/* Physical interface */
+struct keywest_iface
+{
+	unsigned long		base;
+	unsigned		bsteps;
+	int			irq;
+	struct semaphore	sem;
+	spinlock_t		lock;
+	struct keywest_chan*	channels;
+	unsigned		chan_count;
+	u8			cur_mode;
+	char			read_write;
+	u8*			data;
+	unsigned		datalen;
+	int			state;
+	int			result;
+	int			stopretry;
+	struct timer_list	timeout_timer;
+	struct completion	complete;
+	struct keywest_iface*	next;
+};
+
+enum {
+	state_idle,
+	state_addr,
+	state_read,
+	state_write,
+	state_stop,
+	state_dead
+};
+
+/* Channel on an interface */
+struct keywest_chan
+{
+	struct i2c_adapter	adapter;
+	struct keywest_iface*	iface;
+	unsigned		chan_no;
+};
+
+/* Register access */
+
+static inline u8 __read_reg(struct keywest_iface *iface, reg_t reg)
+{
+	return in_8(((volatile u8 *)iface->base)
+		+ (((unsigned)reg) << iface->bsteps));
+}
+
+static inline void __write_reg(struct keywest_iface *iface, reg_t reg, u8 val)
+{
+	out_8(((volatile u8 *)iface->base)
+		+ (((unsigned)reg) << iface->bsteps), val);
+	(void)__read_reg(iface, reg);
+	udelay(10);
+}
+
+#define write_reg(reg, val)	__write_reg(iface, reg, val) 
+#define read_reg(reg)		__read_reg(iface, reg) 
+
+
+
+#endif /* __I2C_KEYWEST_H__ */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/ide/ide-cd.c linux/drivers/ide/ide-cd.c
--- linux.orig/drivers/ide/ide-cd.c	Mon Jan  7 23:41:09 2002
+++ linux/drivers/ide/ide-cd.c	Wed Dec 26 16:47:47 2001
@@ -2642,6 +2642,13 @@
 	if (cap.mechtype == mechtype_caddy || cap.mechtype == mechtype_popup)
 		CDROM_CONFIG_FLAGS (drive)->close_tray = 0;
 
+	/* Some drives used by Apple don't advertise audio play
+	 * but they do support reading TOC & audio datas
+	 */
+	if (strcmp (drive->id->model, "MATSHITADVD-ROM SR-8187") == 0 ||
+	    strcmp (drive->id->model, "MATSHITADVD-ROM SR-8186") == 0)
+		CDROM_CONFIG_FLAGS (drive)->audio_play = 1;
+
 #if ! STANDARD_ATAPI
 	if (cdi->sanyo_slot > 0) {
 		CDROM_CONFIG_FLAGS (drive)->is_changer = 1;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/ide/ide-m8xx.c linux/drivers/ide/ide-m8xx.c
--- linux.orig/drivers/ide/ide-m8xx.c	Mon Jan  7 23:41:09 2002
+++ linux/drivers/ide/ide-m8xx.c	Wed Dec 26 16:47:47 2001
@@ -1,8 +1,14 @@
 /*
- *
- *
  *  linux/drivers/ide/ide-m8xx.c
  *
+ *  Copyright (C) 2000, 2001 Wolfgang Denk, wd@denx.de
+ *  Modified for direct IDE interface
+ *	by Thomas Lange, thomas@corelatus.com
+ *  Modified for direct IDE interface on 8xx without using the PCMCIA
+ *  controller
+ *	by Steven.Scholz@imc-berlin.de
+ *  Moved out of arch/ppc/kernel/m8xx_setup.c, other minor cleanups
+ *	by Mathew Locke <mattl@mvista.com>
  */
 
 #include <linux/config.h>
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/ide/ide-pmac.c linux/drivers/ide/ide-pmac.c
--- linux.orig/drivers/ide/ide-pmac.c	Mon Jan  7 23:41:09 2002
+++ linux/drivers/ide/ide-pmac.c	Wed Dec 26 16:47:47 2001
@@ -1,5 +1,5 @@
 /*
- * linux/drivers/ide/ide-pmac.c		Version ?.??	Mar. 18, 2000
+ * linux/drivers/ide/ide-pmac.c
  *
  * Support for IDE interfaces on PowerMacs.
  * These IDE interfaces are memory-mapped and have a DBDMA channel
@@ -24,26 +24,29 @@
 #include <linux/init.h>
 #include <linux/delay.h>
 #include <linux/ide.h>
-
+#include <linux/notifier.h>
+#include <linux/reboot.h>
 #include <asm/prom.h>
 #include <asm/io.h>
 #include <asm/dbdma.h>
 #include <asm/ide.h>
 #include <asm/mediabay.h>
-#include <asm/feature.h>
+#include <asm/machdep.h>
+#include <asm/pmac_feature.h>
+#include <asm/sections.h>
+#include <asm/irq.h>
 #ifdef CONFIG_PMAC_PBOOK
 #include <linux/adb.h>
 #include <linux/pmu.h>
-#include <asm/irq.h>
 #endif
 #include "ide_modes.h"
 
 extern char *ide_dmafunc_verbose(ide_dma_action_t dmafunc);
+extern void ide_do_request(ide_hwgroup_t *hwgroup, int masked_irq);
 
-#undef IDE_PMAC_DEBUG
+#define IDE_PMAC_DEBUG
 
-#define IDE_SYSCLK_NS		30
-#define IDE_SYSCLK_ULTRA_PS	0x1d4c /* (15 * 1000 / 2)*/
+#define DMA_WAIT_TIMEOUT	500
 
 struct pmac_ide_hwif {
 	ide_ioreg_t			regbase;
@@ -52,12 +55,14 @@
 	int				aapl_bus_id;
 	struct device_node*		node;
 	u32				timings[2];
+	struct resource*		reg_resource;
 #ifdef CONFIG_BLK_DEV_IDEDMA_PMAC
 	volatile struct dbdma_regs*	dma_regs;
 	struct dbdma_cmd*		dma_table;
+	struct resource*		dma_resource;
 #endif
 	
-} pmac_ide[MAX_HWIFS];
+} pmac_ide[MAX_HWIFS] __pmacdata;
 
 static int pmac_ide_count;
 
@@ -65,33 +70,145 @@
 	controller_ohare,	/* OHare based */
 	controller_heathrow,	/* Heathrow/Paddington */
 	controller_kl_ata3,	/* KeyLargo ATA-3 */
-	controller_kl_ata4	/* KeyLargo ATA-4 */
+	controller_kl_ata4,	/* KeyLargo ATA-4 */
+	controller_kl_ata4_80	/* KeyLargo ATA-4 with 80 conductor cable */
 };
 
+/*
+ * Timing register definitions
+ */
+
+/* Number of IDE_SYSCLK_NS ticks, argument is in nanoseconds */
+#define SYSCLK_TICKS(t)		(((t) + IDE_SYSCLK_NS - 1) / IDE_SYSCLK_NS)
+#define SYSCLK_TICKS_66(t)	(((t) + IDE_SYSCLK_66_NS - 1) / IDE_SYSCLK_66_NS)
+#define IDE_SYSCLK_NS		30	/* 33Mhz cell */
+#define IDE_SYSCLK_66_NS	15	/* 66Mhz cell */
+
+/* 66Mhz cell, found in KeyLargo. Can do ultra mode 0 to 2 on
+ * 40 connector cable and to 4 on 80 connector one.
+ * Clock unit is 15ns (66Mhz)
+ * 
+ * 3 Values can be programmed:
+ *  - Write data setup, which appears to match the cycle time. They
+ *    also call it DIOW setup.
+ *  - Ready to pause time (from spec)
+ *  - Address setup. That one is weird. I don't see where exactly
+ *    it fits in UDMA cycles, I got it's name from an obscure piece
+ *    of commented out code in Darwin. They leave it to 0, we do as
+ *    well, despite a comment that would lead to think it has a
+ *    min value of 45ns.
+ * Apple also add 60ns to the write data setup (or cycle time ?) on
+ * reads. I can't explain that, I tried it and it broke everything
+ * here.
+ */
+#define TR_66_UDMA_MASK			0xfff00000
+#define TR_66_UDMA_EN			0x00100000 /* Enable Ultra mode for DMA */
+#define TR_66_UDMA_ADDRSETUP_MASK	0xe0000000 /* Address setup */
+#define TR_66_UDMA_ADDRSETUP_SHIFT	29
+#define TR_66_UDMA_RDY2PAUS_MASK	0x1e000000 /* Ready 2 pause time */
+#define TR_66_UDMA_RDY2PAUS_SHIFT	25
+#define TR_66_UDMA_WRDATASETUP_MASK	0x01e00000 /* Write data setup time */
+#define TR_66_UDMA_WRDATASETUP_SHIFT	21
+#define TR_66_MDMA_MASK			0x000ffc00
+#define TR_66_MDMA_RECOVERY_MASK	0x000f8000
+#define TR_66_MDMA_RECOVERY_SHIFT	15
+#define TR_66_MDMA_ACCESS_MASK		0x00007c00
+#define TR_66_MDMA_ACCESS_SHIFT		10
+#define TR_66_PIO_MASK			0x000003ff
+#define TR_66_PIO_RECOVERY_MASK		0x000003e0
+#define TR_66_PIO_RECOVERY_SHIFT	5
+#define TR_66_PIO_ACCESS_MASK		0x0000001f
+#define TR_66_PIO_ACCESS_SHIFT		0
+
+/* 33Mhz cell, found in OHare, Heathrow (& Paddington) and KeyLargo
+ * Can do pio & mdma modes, clock unit is 30ns (33Mhz)
+ * 
+ * The access time and recovery time can be programmed. Some older
+ * Darwin code base limit OHare to 150ns cycle time. I decided to do
+ * the same here fore safety against broken old hardware ;)
+ * The HalfTick bit, when set, adds half a clock (15ns) to the access
+ * time and removes one from recovery. It's not supported on KeyLargo
+ * implementation afaik. The E bit appears to be set for PIO mode 0 and
+ * is used to reach long timings used in this mode.
+ */
+#define TR_33_MDMA_MASK			0x003ff800
+#define TR_33_MDMA_RECOVERY_MASK	0x001f0000
+#define TR_33_MDMA_RECOVERY_SHIFT	16
+#define TR_33_MDMA_ACCESS_MASK		0x0000f800
+#define TR_33_MDMA_ACCESS_SHIFT		11
+#define TR_33_MDMA_HALFTICK		0x00200000
+#define TR_33_PIO_MASK			0x000007ff
+#define TR_33_PIO_E			0x00000400
+#define TR_33_PIO_RECOVERY_MASK		0x000003e0
+#define TR_33_PIO_RECOVERY_SHIFT	5
+#define TR_33_PIO_ACCESS_MASK		0x0000001f
+#define TR_33_PIO_ACCESS_SHIFT		0
 
 #ifdef CONFIG_BLK_DEV_IDEDMA_PMAC
 
-typedef struct {
+/* Rounded Multiword DMA timings
+ * 
+ * I gave up finding a generic formula for all controller
+ * types and instead, built tables based on timing values
+ * used by Apple in Darwin's implementation.
+ */
+struct mdma_timings_t {
 	int	accessTime;
+	int	recoveryTime;
 	int	cycleTime;
-} pmac_ide_timing;
+};
+
+struct mdma_timings_t mdma_timings_33[] __pmacdata =
+{
+    { 240, 240, 480 },
+    { 180, 180, 360 },
+    { 135, 135, 270 },
+    { 120, 120, 240 },
+    { 105, 105, 210 },
+    {  90,  90, 180 },
+    {  75,  75, 150 },
+    {  75,  45, 120 },
+    {   0,   0,   0 }
+};
 
-/* Multiword DMA timings */
-static pmac_ide_timing mdma_timings[] =
+struct mdma_timings_t mdma_timings_33k[] __pmacdata =
 {
-    { 215,    480 },	/* Mode 0 */
-    {  80,    150 },	/*      1 */
-    {  70,    120 }	/*      2 */
+    { 240, 240, 480 },
+    { 180, 180, 360 },
+    { 150, 150, 300 },
+    { 120, 120, 240 },
+    {  90, 120, 210 },
+    {  90,  90, 180 },
+    {  90,  60, 150 },
+    {  90,  30, 120 },
+    {   0,   0,   0 }
 };
 
-/* Ultra DMA timings (for use when I know how to calculate them */
-static pmac_ide_timing udma_timings[] =
+struct mdma_timings_t mdma_timings_66[] __pmacdata =
 {
-    {   0,    114 },	/* Mode 0 */
-    {   0,     75 },	/*      1 */
-    {   0,     55 },	/*      2 */
-    {   100,   45 },	/*      3 */
-    {   100,   25 }	/*      4 */
+    { 240, 240, 480 },
+    { 180, 180, 360 },
+    { 135, 135, 270 },
+    { 120, 120, 240 },
+    { 105, 105, 210 },
+    {  90,  90, 180 },
+    {  90,  75, 165 },
+    {  75,  45, 120 },
+    {   0,   0,   0 }
+};
+
+/* Ultra DMA timings (rounded) */
+struct {
+	int	addrSetup; /* ??? */
+	int	rdy2pause;
+	int	wrDataSetup;
+} udma_timings[] __pmacdata =
+{
+    {   0, 180,  120 },	/* Mode 0 */
+    {   0, 150,  90 },	/*      1 */
+    {   0, 120,  60 },	/*      2 */
+    {   0, 90,   45 },	/*      3 */
+    {   0, 90,   30 }	/*      4 */
 };
 
 /* allow up to 256 DBDMA commands per xfer */
@@ -121,7 +238,14 @@
 };
 #endif /* CONFIG_PMAC_PBOOK */
 
-static int
+static int pmac_ide_notify_reboot(struct notifier_block *, unsigned long, void *);
+static struct notifier_block pmac_ide_reboot_notifier = {
+	pmac_ide_notify_reboot,
+	NULL,
+	0
+};
+
+static int __pmac
 pmac_ide_find(ide_drive_t *drive)
 {
 	ide_hwif_t *hwif = HWIF(drive);
@@ -140,7 +264,8 @@
  * N.B. this can't be an initfunc, because the media-bay task can
  * call ide_[un]register at any time.
  */
-void pmac_ide_init_hwif_ports(hw_regs_t *hw,
+void __pmac
+pmac_ide_init_hwif_ports(hw_regs_t *hw,
 			      ide_ioreg_t data_port, ide_ioreg_t ctrl_port,
 			      int *irq)
 {
@@ -198,24 +323,31 @@
 /* Setup timings for the selected drive (master/slave). I still need to verify if this
  * is enough, I beleive selectproc will be called whenever an IDE command is started,
  * but... */
-static void
+static void __pmac
 pmac_ide_selectproc(ide_drive_t *drive)
 {
 	int i = pmac_ide_find(drive);
 	if (i < 0)
 		return;
-			
-	if (drive->select.all & 0x10)
+
+	if (drive->select.b.unit & 0x01)
 		out_le32((unsigned *)(IDE_DATA_REG + 0x200 + _IO_BASE), pmac_ide[i].timings[1]);
 	else
 		out_le32((unsigned *)(IDE_DATA_REG + 0x200 + _IO_BASE), pmac_ide[i].timings[0]);
+	(void)in_le32((unsigned *)(IDE_DATA_REG + 0x200 + _IO_BASE));
 }
 
-/* Number of IDE_SYSCLK_NS ticks, argument is in nanoseconds */
-#define SYSCLK_TICKS(t)		(((t) + IDE_SYSCLK_NS - 1) / IDE_SYSCLK_NS)
-#define SYSCLK_TICKS_UDMA(t)	(((t) + IDE_SYSCLK_ULTRA_PS - 1) / IDE_SYSCLK_ULTRA_PS)
 
-static __inline__ int
+/* Note: We don't use the generic routine here because for some
+ * yet unexplained reasons, it cause some media-bay CD-ROMs to
+ * lockup the bus. Strangely, this new version of the code is
+ * almost identical to the generic one and works, I've not yet
+ * managed to figure out what bit is causing the lockup in the
+ * generic code, possibly a timing issue...
+ * 
+ * --BenH
+ */
+static int __pmac
 wait_for_ready(ide_drive_t *drive)
 {
 	/* Timeout bumped for some powerbooks */
@@ -241,22 +373,14 @@
 	return 0;
 }
 
-/* Note: We don't use the generic routine here because some of Apple's
- * controller seem to be very sensitive about how things are done.
- * We should probably set the NIEN bit, but that's an example of thing
- * that can cause the controller to hang under some circumstances when
- * done on the media-bay CD-ROM during boot. We do get occasional
- * spurrious interrupts because of that.
- * --BenH
- */
-static int
+static int __pmac
 pmac_ide_do_setfeature(ide_drive_t *drive, byte command)
 {
-	unsigned long flags;
 	int result = 1;
-
-	save_flags(flags);
-	cli();
+	unsigned long flags;
+	ide_hwif_t *hwif = HWIF(drive);
+	
+	disable_irq(hwif->irq);	/* disable_irq_nosync ?? */
 	udelay(1);
 	SELECT_DRIVE(HWIF(drive), drive);
 	SELECT_MASK(HWIF(drive), drive, 0);
@@ -265,27 +389,34 @@
 		printk(KERN_ERR "pmac_ide_do_setfeature disk not ready before SET_FEATURE!\n");
 		goto out;
 	}
-	OUT_BYTE(SETFEATURES_XFER, IDE_FEATURE_REG);
+	udelay(10);
+	OUT_BYTE(drive->ctl | 2, IDE_CONTROL_REG);
 	OUT_BYTE(command, IDE_NSECTOR_REG);
+	OUT_BYTE(SETFEATURES_XFER, IDE_FEATURE_REG);
 	OUT_BYTE(WIN_SETFEATURES, IDE_COMMAND_REG);
 	udelay(1);
+	__save_flags(flags);	/* local CPU only */
+	ide__sti();		/* local CPU only -- for jiffies */
 	result = wait_for_ready(drive);
+	__restore_flags(flags); /* local CPU only */
+	OUT_BYTE(drive->ctl, IDE_CONTROL_REG);
 	if (result)
 		printk(KERN_ERR "pmac_ide_do_setfeature disk not ready after SET_FEATURE !\n");
 out:
-	restore_flags(flags);
-	
+	SELECT_MASK(HWIF(drive), drive, 0);
+	enable_irq(hwif->irq);
 	return result;
 }
 
 /* Calculate PIO timings */
-static void
+static void __pmac
 pmac_ide_tuneproc(ide_drive_t *drive, byte pio)
 {
 	ide_pio_data_t d;
 	int i;
 	u32 *timings;
-	int accessTicks, recTicks;
+	unsigned accessTicks, recTicks;
+	unsigned accessTime, recTime;
 	
 	i = pmac_ide_find(drive);
 	if (i < 0)
@@ -293,27 +424,44 @@
 		
 	pio = ide_get_best_pio_mode(drive, pio, 4, &d);
 	accessTicks = SYSCLK_TICKS(ide_pio_timings[pio].active_time);
-	if (drive->select.all & 0x10)
+	if (drive->select.b.unit & 0x01)
 		timings = &pmac_ide[i].timings[1];
 	else
 		timings = &pmac_ide[i].timings[0];
-	
-	if (pmac_ide[i].kind == controller_kl_ata4) {
-		/* The "ata-4" IDE controller of Core99 machines */
-		accessTicks = SYSCLK_TICKS_UDMA(ide_pio_timings[pio].active_time * 1000);
-		recTicks = SYSCLK_TICKS_UDMA(d.cycle_time * 1000) - accessTicks;
 
-		*timings = ((*timings) & 0x1FFFFFC00) | accessTicks | (recTicks << 5);
+	recTime = d.cycle_time - ide_pio_timings[pio].active_time
+			- ide_pio_timings[pio].setup_time;
+	recTime = max(recTime, 150U);
+	accessTime = ide_pio_timings[pio].active_time;
+	accessTime = max(accessTime, 150U);
+	if (pmac_ide[i].kind == controller_kl_ata4 ||
+		pmac_ide[i].kind == controller_kl_ata4_80) {
+		/* 66Mhz cell */
+		accessTicks = SYSCLK_TICKS_66(accessTime);
+		accessTicks = min(accessTicks, 0x1fU);
+		recTicks = SYSCLK_TICKS_66(recTime);
+		recTicks = min(recTicks, 0x1fU);
+		*timings = ((*timings) & ~TR_66_PIO_MASK) |
+				(accessTicks << TR_66_PIO_ACCESS_SHIFT) |
+				(recTicks << TR_66_PIO_RECOVERY_SHIFT);
 	} else {
-		/* The old "ata-3" IDE controller */
-		accessTicks = SYSCLK_TICKS(ide_pio_timings[pio].active_time);
-		if (accessTicks < 4)
-			accessTicks = 4;
-		recTicks = SYSCLK_TICKS(d.cycle_time) - accessTicks - 4;
-		if (recTicks < 1)
-			recTicks = 1;
-	
-		*timings = ((*timings) & 0xFFFFFF800) | accessTicks | (recTicks << 5);
+		/* 33Mhz cell */
+		int ebit = 0;
+		accessTicks = SYSCLK_TICKS(accessTime);
+		accessTicks = min(accessTicks, 0x1fU);
+		accessTicks = max(accessTicks, 4U);
+		recTicks = SYSCLK_TICKS(recTime);
+		recTicks = min(recTicks, 0x1fU);
+		recTicks = max(recTicks, 5U) - 4;
+		if (recTicks > 9) {
+			recTicks--; /* guess, but it's only for PIO0, so... */
+			ebit = 1;
+		}
+		*timings = ((*timings) & ~TR_33_PIO_MASK) |
+				(accessTicks << TR_33_PIO_ACCESS_SHIFT) |
+				(recTicks << TR_33_PIO_RECOVERY_SHIFT);
+		if (ebit)
+			*timings |= TR_33_PIO_E;
 	}
 
 #ifdef IDE_PMAC_DEBUG
@@ -326,70 +474,134 @@
 }
 
 #ifdef CONFIG_BLK_DEV_IDEDMA_PMAC
-static int
-set_timings_udma(int intf, u32 *timings, byte speed)
+static int __pmac
+set_timings_udma(u32 *timings, byte speed)
 {
-	int cycleTime, accessTime;
-	int rdyToPauseTicks, cycleTicks;
-
-	if (pmac_ide[intf].kind != controller_kl_ata4)
-		return 1;
-		
-	cycleTime = udma_timings[speed & 0xf].cycleTime;
-	accessTime = udma_timings[speed & 0xf].accessTime;
-
-	rdyToPauseTicks = SYSCLK_TICKS_UDMA(accessTime * 1000);
-	cycleTicks = SYSCLK_TICKS_UDMA(cycleTime * 1000);
+	unsigned rdyToPauseTicks, wrDataSetupTicks, addrTicks;
 
-	*timings = ((*timings) & 0xe00fffff) |
-			((cycleTicks << 1) | (rdyToPauseTicks << 5) | 1) << 20;
+	rdyToPauseTicks = SYSCLK_TICKS_66(udma_timings[speed & 0xf].rdy2pause);
+	wrDataSetupTicks = SYSCLK_TICKS_66(udma_timings[speed & 0xf].wrDataSetup);
+	addrTicks = SYSCLK_TICKS_66(udma_timings[speed & 0xf].addrSetup);
+
+	*timings = ((*timings) & ~(TR_66_UDMA_MASK | TR_66_MDMA_MASK)) |
+			(wrDataSetupTicks << TR_66_UDMA_WRDATASETUP_SHIFT) | 
+			(rdyToPauseTicks << TR_66_UDMA_RDY2PAUS_SHIFT) |
+			(addrTicks <<TR_66_UDMA_ADDRSETUP_SHIFT) |
+			TR_66_UDMA_EN;
+#ifdef IDE_PMAC_DEBUG
+	printk(KERN_ERR "ide_pmac: Set UDMA timing for mode %d, reg: 0x%08x\n",
+		speed & 0xf,  *timings);
+#endif	
 
 	return 0;
 }
 
-static int
-set_timings_mdma(int intf, u32 *timings, byte speed)
+static int __pmac
+set_timings_mdma(int intf_type, u32 *timings, byte speed, int drive_cycle_time)
 {
-	int cycleTime, accessTime;
-	int accessTicks, recTicks;
+	int cycleTime, accessTime, recTime;
+	unsigned accessTicks, recTicks;
+	struct mdma_timings_t* tm;
+	int i;
 
-	/* Calculate accesstime and cycle time */
-	cycleTime = mdma_timings[speed & 0xf].cycleTime;
-	accessTime = mdma_timings[speed & 0xf].accessTime;
-	if ((pmac_ide[intf].kind == controller_ohare) && (cycleTime < 150))
+	/* Get default cycle time for mode */
+	switch(speed & 0xf) {
+		case 0: cycleTime = 480; break;
+		case 1: cycleTime = 150; break;
+		case 2: cycleTime = 120; break;
+		default:
+			return -1;
+	}
+	/* Adjust for drive */
+	if (drive_cycle_time && drive_cycle_time > cycleTime)
+		cycleTime = drive_cycle_time;
+	/* OHare limits according to some old Apple sources */	
+	if ((intf_type == controller_ohare) && (cycleTime < 150))
 		cycleTime = 150;
+	/* Get the proper timing array for this controller */
+	switch(intf_type) {
+		case controller_kl_ata4:
+		case controller_kl_ata4_80:
+			tm = mdma_timings_66;
+			break;
+		case controller_kl_ata3:
+			tm = mdma_timings_33k;
+			break;
+		default:
+			tm = mdma_timings_33;
+			break;
+	}
+	/* Lookup matching access & recovery times */
+	i = -1;
+	for (;;) {
+		if (tm[i+1].cycleTime < cycleTime)
+			break;
+		i++;
+	}
+	if (i < 0)
+		return -1;
+	cycleTime = tm[i].cycleTime;
+	accessTime = tm[i].accessTime;
+	recTime = tm[i].recoveryTime;
 
-	/* For ata-4 controller */
-	if (pmac_ide[intf].kind == controller_kl_ata4) {
-		accessTicks = SYSCLK_TICKS_UDMA(accessTime * 1000);
-		recTicks = SYSCLK_TICKS_UDMA(cycleTime * 1000) - accessTicks;
-		*timings = ((*timings) & 0xffe003ff) |
-			(accessTicks | (recTicks << 5)) << 10;
+#ifdef IDE_PMAC_DEBUG
+	printk(KERN_ERR "ide_pmac: MDMA, cycleTime: %d, accessTime: %d, recTime: %d\n",
+		cycleTime, accessTime, recTime);
+#endif	
+	if (intf_type == controller_kl_ata4 || intf_type == controller_kl_ata4_80) {
+		/* 66Mhz cell */
+		accessTicks = SYSCLK_TICKS_66(accessTime);
+		accessTicks = min(accessTicks, 0x1fU);
+		accessTicks = max(accessTicks, 0x1U);
+		recTicks = SYSCLK_TICKS_66(recTime);
+		recTicks = min(recTicks, 0x1fU);
+		recTicks = max(recTicks, 0x3U);
+		/* Clear out mdma bits and disable udma */
+		*timings = ((*timings) & ~(TR_66_MDMA_MASK | TR_66_UDMA_MASK)) |
+			(accessTicks << TR_66_MDMA_ACCESS_SHIFT) |
+			(recTicks << TR_66_MDMA_RECOVERY_SHIFT);
+	} else if (intf_type == controller_kl_ata3) {
+		/* 33Mhz cell on KeyLargo */
+		accessTicks = SYSCLK_TICKS(accessTime);
+		accessTicks = max(accessTicks, 1U);
+		accessTicks = min(accessTicks, 0x1fU);
+		accessTime = accessTicks * IDE_SYSCLK_NS;
+		recTicks = SYSCLK_TICKS(recTime);
+		recTicks = max(recTicks, 1U);
+		recTicks = min(recTicks, 0x1fU);
+		*timings = ((*timings) & ~TR_33_MDMA_MASK) |
+				(accessTicks << TR_33_MDMA_ACCESS_SHIFT) |
+				(recTicks << TR_33_MDMA_RECOVERY_SHIFT);
 	} else {
+		/* 33Mhz cell on others */
 		int halfTick = 0;
 		int origAccessTime = accessTime;
-		int origCycleTime = cycleTime;
+		int origRecTime = recTime;
 		
 		accessTicks = SYSCLK_TICKS(accessTime);
-		if (accessTicks < 1)
-			accessTicks = 1;
+		accessTicks = max(accessTicks, 1U);
+		accessTicks = min(accessTicks, 0x1fU);
 		accessTime = accessTicks * IDE_SYSCLK_NS;
-		recTicks = SYSCLK_TICKS(cycleTime - accessTime) - 1;
-		if (recTicks < 1)
-			recTicks = 1;
-		cycleTime = (recTicks + 1 + accessTicks) * IDE_SYSCLK_NS;
-
-		/* KeyLargo ata-3 don't support the half-tick stuff */
-		if ((pmac_ide[intf].kind != controller_kl_ata3) &&
-			(accessTicks > 1) &&
-			((accessTime - IDE_SYSCLK_NS/2) >= origAccessTime) &&
-			((cycleTime - IDE_SYSCLK_NS) >= origCycleTime)) {
-            			halfTick    = 1;
-				accessTicks--;
-		}
-		*timings = ((*timings) & 0x7FF) |
-			(accessTicks | (recTicks << 5) | (halfTick << 10)) << 11;
+		recTicks = SYSCLK_TICKS(recTime);
+		recTicks = max(recTicks, 2U) - 1;
+		recTicks = min(recTicks, 0x1fU);
+		recTime = (recTicks + 1) * IDE_SYSCLK_NS;
+		if ((accessTicks > 1) &&
+		    ((accessTime - IDE_SYSCLK_NS/2) >= origAccessTime) &&
+		    ((recTime - IDE_SYSCLK_NS/2) >= origRecTime)) {
+            		halfTick = 1;
+			accessTicks--;
+		}
+		*timings = ((*timings) & ~TR_33_MDMA_MASK) |
+				(accessTicks << TR_33_MDMA_ACCESS_SHIFT) |
+				(recTicks << TR_33_MDMA_RECOVERY_SHIFT);
+		if (halfTick)
+			*timings |= TR_33_MDMA_HALFTICK;
 	}
+#ifdef IDE_PMAC_DEBUG
+	printk(KERN_ERR "ide_pmac: Set MDMA timing for mode %d, reg: 0x%08x\n",
+		speed & 0xf,  *timings);
+#endif	
 	return 0;
 }
 #endif /* #ifdef CONFIG_BLK_DEV_IDEDMA_PMAC */
@@ -397,11 +609,11 @@
 /* You may notice we don't use this function on normal operation,
  * our, normal mdma function is supposed to be more precise
  */
-static int
+static int __pmac
 pmac_ide_tune_chipset (ide_drive_t *drive, byte speed)
 {
 	int intf		= pmac_ide_find(drive);
-	int unit		= (drive->select.all & 0x10) ? 1:0;
+	int unit		= (drive->select.b.unit & 0x01);
 	int ret			= 0;
 	u32 *timings;
 
@@ -414,19 +626,25 @@
 #ifdef CONFIG_BLK_DEV_IDEDMA_PMAC
 		case XFER_UDMA_4:
 		case XFER_UDMA_3:
+			if (pmac_ide[intf].kind != controller_kl_ata4_80)
+				return 1;		
 		case XFER_UDMA_2:
 		case XFER_UDMA_1:
 		case XFER_UDMA_0:
-			ret = set_timings_udma(intf, timings, speed);
+			if (pmac_ide[intf].kind != controller_kl_ata4 &&
+				pmac_ide[intf].kind != controller_kl_ata4_80)
+				return 1;		
+			ret = set_timings_udma(timings, speed);
 			break;
 		case XFER_MW_DMA_2:
 		case XFER_MW_DMA_1:
 		case XFER_MW_DMA_0:
+			ret = set_timings_mdma(pmac_ide[intf].kind, timings, speed, 0);
+			break;
 		case XFER_SW_DMA_2:
 		case XFER_SW_DMA_1:
 		case XFER_SW_DMA_0:
-			ret = set_timings_mdma(intf, timings, speed);
-			break;
+			return 1;
 #endif /* CONFIG_BLK_DEV_IDEDMA_PMAC */
 		case XFER_PIO_4:
 		case XFER_PIO_3:
@@ -451,13 +669,46 @@
 	return 0;
 }
 
-ide_ioreg_t
+static void __pmac
+sanitize_timings(int i)
+{
+	unsigned value;
+	
+	switch(pmac_ide[i].kind) {
+		case controller_kl_ata4:
+		case controller_kl_ata4_80:
+			value = 0x0008438c;
+			break;
+		case controller_kl_ata3:
+			value = 0x00084526;
+			break;
+		case controller_heathrow:
+		case controller_ohare:
+		default:
+			value = 0x00074526;
+			break;
+	}
+	pmac_ide[i].timings[0] = pmac_ide[i].timings[1] = value;
+}
+
+ide_ioreg_t __pmac
 pmac_ide_get_base(int index)
 {
 	return pmac_ide[index].regbase;
 }
 
-int
+int __pmac
+pmac_ide_check_base(ide_ioreg_t base)
+{
+	int ix;
+	
+ 	for (ix = 0; ix < MAX_HWIFS; ++ix)
+		if (base == pmac_ide[ix].regbase)
+			return ix;
+	return -1;
+}
+
+int __pmac
 pmac_ide_get_irq(ide_ioreg_t base)
 {
 	int ix;
@@ -468,7 +719,7 @@
 	return 0;
 }
 
-static int ide_majors[] = { 3, 22, 33, 34, 56, 57 };
+static int ide_majors[]  __pmacdata = { 3, 22, 33, 34, 56, 57 };
 
 kdev_t __init
 pmac_find_ide_boot(char *bootdevice, int n)
@@ -532,6 +783,7 @@
 
 	for (i = 0, np = atas; i < MAX_HWIFS && np != NULL; np = np->next) {
 		struct device_node *tp;
+		struct pmac_ide_hwif* pmhw;
 		int *bidp;
 		int in_bay = 0;
 
@@ -560,6 +812,22 @@
 			++i;
 		if (i >= MAX_HWIFS)
 			break;
+		pmhw = &pmac_ide[i];
+
+		/*
+		 * Some older OFs have bogus sizes, causing request_OF_resource
+		 * to fail. We fix them up here
+		 */
+		if (np->addrs[0].size > 0x1000)
+			np->addrs[0].size = 0x1000;
+		if (np->n_addrs > 1 && np->addrs[1].size > 0x100)
+			np->addrs[1].size = 0x100;
+
+		pmhw->reg_resource = request_OF_resource(np, 0, "  (mac-io IDE IO)");
+		if (!pmhw->reg_resource) {
+			printk(KERN_ERR "ide-pmac(%s): can't request IO resource !\n", np->name);
+			continue;
+		}
 
 		base = (unsigned long) ioremap(np->addrs[0].address, 0x200) - _IO_BASE;
 
@@ -574,21 +842,30 @@
 		} else {
 			irq = np->intrs[0].line;
 		}
-		pmac_ide[i].regbase = base;
-		pmac_ide[i].irq = irq;
-		pmac_ide[i].node = np;
+		pmhw->regbase = base;
+		pmhw->irq = irq;
+		pmhw->node = np;
 		if (device_is_compatible(np, "keylargo-ata")) {
 			if (strcmp(np->name, "ata-4") == 0)
-				pmac_ide[i].kind = controller_kl_ata4;
+				pmhw->kind = controller_kl_ata4;
 			else
-				pmac_ide[i].kind = controller_kl_ata3;
+				pmhw->kind = controller_kl_ata3;
 		} else if (device_is_compatible(np, "heathrow-ata"))
-			pmac_ide[i].kind = controller_heathrow;
+			pmhw->kind = controller_heathrow;
 		else
-			pmac_ide[i].kind = controller_ohare;
+			pmhw->kind = controller_ohare;
 
 		bidp = (int *)get_property(np, "AAPL,bus-id", NULL);
-		pmac_ide[i].aapl_bus_id =  bidp ? *bidp : 0;
+		pmhw->aapl_bus_id =  bidp ? *bidp : 0;
+
+		if (pmhw->kind == controller_kl_ata4) {
+			char* cable = get_property(np, "cable-type", NULL);
+			if (cable && !strncmp(cable, "80-", 3))
+				pmhw->kind = controller_kl_ata4_80;
+		}
+
+		/* Make sure we have sane timings */
+		sanitize_timings(i);
 
 		if (np->parent && np->parent->name
 		    && strcasecmp(np->parent->name, "media-bay") == 0) {
@@ -596,39 +873,22 @@
 			media_bay_set_ide_infos(np->parent,base,irq,i);
 #endif /* CONFIG_PMAC_PBOOK */
 			in_bay = 1;
-		} else if (pmac_ide[i].kind == controller_ohare) {
+			if (!bidp)
+				pmhw->aapl_bus_id = 1;
+		} else if (pmhw->kind == controller_ohare) {
 			/* The code below is having trouble on some ohare machines
 			 * (timing related ?). Until I can put my hand on one of these
 			 * units, I keep the old way
 			 */
-			 feature_set(np, FEATURE_IDE0_enable);
+			ppc_md.feature_call(PMAC_FTR_IDE_ENABLE, np, 0, 1);
 		} else {
  			/* This is necessary to enable IDE when net-booting */
 			printk(KERN_INFO "pmac_ide: enabling IDE bus ID %d\n",
-				pmac_ide[i].aapl_bus_id);
-			switch(pmac_ide[i].aapl_bus_id) {
-			    case 0:
-				feature_set(np, FEATURE_IDE0_reset);
-				mdelay(10);
- 				feature_set(np, FEATURE_IDE0_enable);
-				mdelay(10);
-				feature_clear(np, FEATURE_IDE0_reset);
-				break;
-			    case 1:
-				feature_set(np, FEATURE_IDE1_reset);
-				mdelay(10);
- 				feature_set(np, FEATURE_IDE1_enable);
-				mdelay(10);
-				feature_clear(np, FEATURE_IDE1_reset);
-				break;
-			    case 2:
-			    	/* This one exists only for KL, I don't know
-				   about any enable bit */
-				feature_set(np, FEATURE_IDE2_reset);
-				mdelay(10);
-				feature_clear(np, FEATURE_IDE2_reset);
-				break;
-			}
+				pmhw->aapl_bus_id);
+			ppc_md.feature_call(PMAC_FTR_IDE_RESET, np, pmhw->aapl_bus_id, 1);
+			ppc_md.feature_call(PMAC_FTR_IDE_ENABLE, np, pmhw->aapl_bus_id, 1);
+			mdelay(10);
+			ppc_md.feature_call(PMAC_FTR_IDE_RESET, np, pmhw->aapl_bus_id, 0);
 			big_delay = 1;
 		}
 
@@ -658,6 +918,7 @@
 #ifdef CONFIG_PMAC_PBOOK
 	pmu_register_sleep_notifier(&idepmac_sleep_notifier);
 #endif /* CONFIG_PMAC_PBOOK */
+	register_reboot_notifier(&pmac_ide_reboot_notifier);
 }
 
 #ifdef CONFIG_BLK_DEV_IDEDMA_PMAC
@@ -665,6 +926,12 @@
 static void __init 
 pmac_ide_setup_dma(struct device_node *np, int ix)
 {
+	pmac_ide[ix].dma_resource = request_OF_resource(np, 1, " (mac-io IDE DMA)");
+	if (!pmac_ide[ix].dma_resource) {
+		printk(KERN_ERR "ide-pmac(%s): can't request DMA resource !\n", np->name);
+		return;
+	}
+
 	pmac_ide[ix].dma_regs =
 		(volatile struct dbdma_regs*)ioremap(np->addrs[1].address, 0x200);
 
@@ -692,7 +959,7 @@
  * pmac_ide_build_dmatable builds the DBDMA command list
  * for a transfer and sets the DBDMA channel to point to it.
  */
-static int
+static int __pmac
 pmac_ide_build_dmatable(ide_drive_t *drive, int ix, int wr)
 {
 	struct dbdma_cmd *table, *tstart;
@@ -703,6 +970,13 @@
 	volatile struct dbdma_regs *dma = pmac_ide[ix].dma_regs;
 
 	table = tstart = (struct dbdma_cmd *) DBDMA_ALIGN(pmac_ide[ix].dma_table);
+
+#ifdef IDE_PMAC_DEBUG
+	if (in_le32(&dma->status) & (RUN|ACTIVE))
+		printk("ide-pmac: channel status not stopped ! (%x)\n",
+			in_le32(&dma->status));
+#endif	
+	/* Make sure channel is stopped and all error conditions are clear */
 	out_le32(&dma->control, (RUN|PAUSE|FLUSH|WAKE|DEAD) << 16);
 	while (in_le32(&dma->status) & RUN)
 		udelay(1);
@@ -734,6 +1008,10 @@
 		 * Note that one DBDMA command can transfer
 		 * at most 65535 bytes.
 		 */
+#ifdef IDE_PMAC_DEBUG
+		if (size & 0x01)
+			printk("ide-pmac: odd size transfer ! (%d)\n", size);
+#endif			
 		while (size) {
 			unsigned int tc = (size < 0xfe00)? size: 0xfe00;
 
@@ -782,12 +1060,14 @@
 }
 
 static __inline__ unsigned char
-udma_bits_to_command(unsigned char bits)
+udma_bits_to_command(unsigned char bits, int high_speed)
 {
-	if(bits & 0x10)
-		return XFER_UDMA_4;
-	if(bits & 0x08)
-		return XFER_UDMA_3;
+	if (high_speed) {
+		if(bits & 0x10)
+			return XFER_UDMA_4;
+		if(bits & 0x08)
+			return XFER_UDMA_3;
+	}
 	if(bits & 0x04)
 		return XFER_UDMA_2;
 	if(bits & 0x02)
@@ -798,14 +1078,13 @@
 }
 
 /* Calculate MultiWord DMA timings */
-static int
+static int __pmac
 pmac_ide_mdma_enable(ide_drive_t *drive, int idx)
 {
 	byte bits = drive->id->dma_mword & 0x07;
 	byte feature = dma_bits_to_command(bits);
 	u32 *timings;
-	int cycleTime, accessTime;
-	int accessTicks, recTicks;
+	int drive_cycle_time;
 	struct hd_driveid *id = drive->id;
 	int ret;
 
@@ -821,66 +1100,30 @@
 		drive->init_speed = feature;
 	
 	/* which drive is it ? */
-	if (drive->select.all & 0x10)
+	if (drive->select.b.unit & 0x01)
 		timings = &pmac_ide[idx].timings[1];
 	else
 		timings = &pmac_ide[idx].timings[0];
 
-	/* Calculate accesstime and cycle time */
-	cycleTime = mdma_timings[feature & 0xf].cycleTime;
-	accessTime = mdma_timings[feature & 0xf].accessTime;
+	/* Check if drive provide explicit cycle time */
 	if ((id->field_valid & 2) && (id->eide_dma_time))
-		cycleTime = id->eide_dma_time;
-	if ((pmac_ide[idx].kind == controller_ohare) && (cycleTime < 150))
-		cycleTime = 150;
+		drive_cycle_time = id->eide_dma_time;
+	else
+		drive_cycle_time = 0;
+
+	/* Calculate controller timings */
+	set_timings_mdma(pmac_ide[idx].kind, timings, feature, drive_cycle_time);
 
-	/* For ata-4 controller */
-	if (pmac_ide[idx].kind == controller_kl_ata4) {
-		accessTicks = SYSCLK_TICKS_UDMA(accessTime * 1000);
-		recTicks = SYSCLK_TICKS_UDMA(cycleTime * 1000) - accessTicks;
-		*timings = ((*timings) & 0xffe003ff) |
-			(accessTicks | (recTicks << 5)) << 10;
-	} else {
-		int halfTick = 0;
-		int origAccessTime = accessTime;
-		int origCycleTime = cycleTime;
-		
-		accessTicks = SYSCLK_TICKS(accessTime);
-		if (accessTicks < 1)
-			accessTicks = 1;
-		accessTime = accessTicks * IDE_SYSCLK_NS;
-		recTicks = SYSCLK_TICKS(cycleTime - accessTime) - 1;
-		if (recTicks < 1)
-			recTicks = 1;
-		cycleTime = (recTicks + 1 + accessTicks) * IDE_SYSCLK_NS;
-
-		/* KeyLargo ata-3 don't support the half-tick stuff */
-		if ((pmac_ide[idx].kind != controller_kl_ata3) &&
-			(accessTicks > 1) &&
-			((accessTime - IDE_SYSCLK_NS/2) >= origAccessTime) &&
-			((cycleTime - IDE_SYSCLK_NS) >= origCycleTime)) {
-            			halfTick    = 1;
-				accessTicks--;
-		}
-		*timings = ((*timings) & 0x7FF) |
-			(accessTicks | (recTicks << 5) | (halfTick << 10)) << 11;
-	}
-#ifdef IDE_PMAC_DEBUG
-	printk(KERN_INFO "ide_pmac: Set MDMA timing for mode %d, reg: 0x%08x\n",
-		feature & 0xf, *timings);
-#endif
 	drive->current_speed = feature;	
 	return 1;
 }
 
 /* Calculate Ultra DMA timings */
-static int
-pmac_ide_udma_enable(ide_drive_t *drive, int idx)
+static int __pmac
+pmac_ide_udma_enable(ide_drive_t *drive, int idx, int high_speed)
 {
 	byte bits = drive->id->dma_ultra & 0x1f;
-	byte feature = udma_bits_to_command(bits);
-	int cycleTime, accessTime;
-	int rdyToPauseTicks, cycleTicks;
+	byte feature = udma_bits_to_command(bits, high_speed);
 	u32 *timings;
 	int ret;
 
@@ -896,25 +1139,18 @@
 		drive->init_speed = feature;
 
 	/* which drive is it ? */
-	if (drive->select.all & 0x10)
+	if (drive->select.b.unit & 0x01)
 		timings = &pmac_ide[idx].timings[1];
 	else
 		timings = &pmac_ide[idx].timings[0];
 
-	cycleTime = udma_timings[feature & 0xf].cycleTime;
-	accessTime = udma_timings[feature & 0xf].accessTime;
-
-	rdyToPauseTicks = SYSCLK_TICKS_UDMA(accessTime * 1000);
-	cycleTicks = SYSCLK_TICKS_UDMA(cycleTime * 1000);
-
-	*timings = ((*timings) & 0xe00fffff) |
-			((cycleTicks << 1) | (rdyToPauseTicks << 5) | 1) << 20;
+	set_timings_udma(timings, feature);
 
 	drive->current_speed = feature;	
 	return 1;
 }
 
-static int
+static int __pmac
 pmac_ide_check_dma(ide_drive_t *drive)
 {
 	int ata4, udma, idx;
@@ -935,21 +1171,20 @@
 		enable = 0;
 
 	udma = 0;
-	ata4 = (pmac_ide[idx].kind == controller_kl_ata4);
+	ata4 = (pmac_ide[idx].kind == controller_kl_ata4 ||
+		pmac_ide[idx].kind == controller_kl_ata4_80);
 			
 	if(enable) {
 		if (ata4 && (drive->media == ide_disk) &&
-		    (id->field_valid & 0x0004) && (id->dma_ultra & 0x17)) {
+		    (id->field_valid & 0x0004) && (id->dma_ultra & 0x1f)) {
 			/* UltraDMA modes. */
-			drive->using_dma = pmac_ide_udma_enable(drive, idx);
+			drive->using_dma = pmac_ide_udma_enable(drive, idx,
+				pmac_ide[idx].kind == controller_kl_ata4_80);
 		}
 		if (!drive->using_dma && (id->dma_mword & 0x0007)) {
 			/* Normal MultiWord DMA modes. */
 			drive->using_dma = pmac_ide_mdma_enable(drive, idx);
 		}
-		/* Without this, strange things will happen on Keylargo-based
-		 * machines
-		 */
 		OUT_BYTE(0, IDE_CONTROL_REG);
 		/* Apply settings to controller */
 		pmac_ide_selectproc(drive);
@@ -957,10 +1192,13 @@
 	return 0;
 }
 
-int pmac_ide_dmaproc(ide_dma_action_t func, ide_drive_t *drive)
+static int __pmac
+pmac_ide_dmaproc(ide_dma_action_t func, ide_drive_t *drive)
 {
-	int ix, dstat, i;
+	int ix, dstat;
 	volatile struct dbdma_regs *dma;
+	byte unit = (drive->select.b.unit & 0x01);
+	byte ata4;
 
 	/* Can we stuff a pointer to our intf structure in config_data
 	 * or select_data in hwif ?
@@ -969,7 +1207,9 @@
 	if (ix < 0)
 		return 0;		
 	dma = pmac_ide[ix].dma_regs;
-
+	ata4 = (pmac_ide[ix].kind == controller_kl_ata4 ||
+		pmac_ide[ix].kind == controller_kl_ata4_80);
+	
 	switch (func) {
 	case ide_dma_off:
 		printk(KERN_INFO "%s: DMA disabled\n", drive->name);
@@ -984,6 +1224,13 @@
 	case ide_dma_write:
 		if (!pmac_ide_build_dmatable(drive, ix, func==ide_dma_write))
 			return 1;
+		/* Apple adds 60ns to wrDataSetup on reads */
+		if (ata4 && (pmac_ide[ix].timings[unit] & TR_66_UDMA_EN)) {
+			out_le32((unsigned *)(IDE_DATA_REG + 0x200 + _IO_BASE),
+				pmac_ide[ix].timings[unit] + 
+				((func == ide_dma_read) ? 0x00800000UL : 0));
+			(void)in_le32((unsigned *)(IDE_DATA_REG + 0x200 + _IO_BASE));
+		}
 		drive->waiting_for_dma = 1;
 		if (drive->media != ide_disk)
 			return 0;
@@ -992,35 +1239,56 @@
 			 IDE_COMMAND_REG);
 	case ide_dma_begin:
 		out_le32(&dma->control, (RUN << 16) | RUN);
+		/* Make sure it gets to the controller right now */
+		(void)in_le32(&dma->control);
 		break;
-	case ide_dma_end:
+	case ide_dma_end: /* returns 1 on error, 0 otherwise */
 		drive->waiting_for_dma = 0;
 		dstat = in_le32(&dma->status);
 		out_le32(&dma->control, ((RUN|WAKE|DEAD) << 16));
 		/* verify good dma status */
 		return (dstat & (RUN|DEAD|ACTIVE)) != RUN;
-	case ide_dma_test_irq:
-		if ((in_le32(&dma->status) & (RUN|ACTIVE)) == RUN)
-			return 1;
-		/* That's a bit ugly and dangerous, but works in our case
-		 * to workaround a problem with the channel status staying
-		 * active if the drive returns an error
+	case ide_dma_test_irq: /* returns 1 if dma irq issued, 0 otherwise */
+		/* We have to things to deal with here:
+		 * 
+		 * - The dbdma won't stop if the command was started
+		 * but completed with an error without transfering all
+		 * datas. This happens when bad blocks are met during
+		 * a multi-block transfer.
+		 * 
+		 * - The dbdma fifo hasn't yet finished flushing to
+		 * to system memory when the disk interrupt occurs.
+		 * 
+		 * The trick here is to increment drive->waiting_for_dma,
+		 * and return as if no interrupt occured. If the counter
+		 * reach a certain timeout value, we then return 1. If
+		 * we really got the interrupt, it will happen right away
+		 * again.
+		 * Apple's solution here may be more elegant. They issue
+		 * a DMA channel interrupt (a separate irq line) via a DBDMA
+		 * NOP command just before the STOP, and wait for both the
+		 * disk and DBDMA interrupts to have completed.
 		 */
-		if (IDE_CONTROL_REG) {
-			byte stat;
-			stat = GET_ALTSTAT();
-			if (stat & ERR_STAT)
-				return 1;
-		}
-		/* In some edge cases, some datas may still be in the dbdma
-		 * engine fifo, we wait a bit for dbdma to complete
+		 
+		/* If ACTIVE is cleared, the STOP command have passed and
+		 * transfer is complete.
 		 */
-		while ((in_le32(&dma->status) & (RUN|ACTIVE)) != RUN) {
-			if (++i > 100)
-				return 0;
-			udelay(1);
+		if (!(in_le32(&dma->status) & ACTIVE))
+			return 1;
+		if (!drive->waiting_for_dma)
+			printk(KERN_WARNING "ide%d, ide_dma_test_irq \
+				called while not waiting\n", ix);
+
+		/* If dbdma didn't execute the STOP command yet, the
+		 * active bit is still set */
+		drive->waiting_for_dma++;
+		if (drive->waiting_for_dma >= DMA_WAIT_TIMEOUT) {
+			printk(KERN_WARNING "ide%d, timeout waiting \
+				for dbdma command stop\n", ix);
+			return 1;
 		}
-		return 1;
+		udelay(1);
+		return 0;
 
 		/* Let's implement tose just in case someone wants them */
 	case ide_dma_bad_drive:
@@ -1041,8 +1309,8 @@
 }
 #endif /* CONFIG_BLK_DEV_IDEDMA_PMAC */
 
-#ifdef CONFIG_PMAC_PBOOK
-static void idepmac_sleep_device(ide_drive_t *drive, int i, unsigned base)
+static void __pmac
+idepmac_sleep_device(ide_drive_t *drive, int i, unsigned base)
 {
 	int j;
 	
@@ -1052,7 +1320,9 @@
 	switch (drive->media) {
 	case ide_disk:
 		/* Spin down the drive */
-		outb(0xa0, base+0x60);
+		outb(drive->select.all, base+0x60);
+		(void)inb(base+0x60);
+		udelay(100);
 		outb(0x0, base+0x30);
 		outb(0x0, base+0x20);
 		outb(0x0, base+0x40);
@@ -1076,8 +1346,10 @@
 	}
 }
 
-static void idepmac_wake_device(ide_drive_t *drive, int used_dma)
- {
+#ifdef CONFIG_PMAC_PBOOK
+static void __pmac
+idepmac_wake_device(ide_drive_t *drive, int used_dma)
+{
 	/* We force the IDE subdriver to check for a media change
 	 * This must be done first or we may lost the condition
 	 *
@@ -1101,12 +1373,15 @@
 		HWGROUP(drive)->busy = 1;
 		pmac_ide_check_dma(drive);
 		HWGROUP(drive)->busy = 0;
+		if (!list_empty(&drive->queue.queue_head))
+			ide_do_request(HWGROUP(drive), 0);
 		spin_unlock_irq(&io_request_lock);
 	}
 #endif /* CONFIG_BLK_DEV_IDEDMA_PMAC */
 }
 
-static void idepmac_sleep_interface(int i, unsigned base, int mediabay)
+static void __pmac
+idepmac_sleep_interface(int i, unsigned base, int mediabay)
 {
 	struct device_node* np = pmac_ide[i].node;
 
@@ -1118,73 +1393,85 @@
 	if (mediabay)
 		return;
 	
-	/* Disable and reset the bus */
-	feature_set(np, FEATURE_IDE0_reset);
-	feature_clear(np, FEATURE_IDE0_enable);
-	switch(pmac_ide[i].aapl_bus_id) {
-	    case 0:
-		feature_set(np, FEATURE_IDE0_reset);
-		feature_clear(np, FEATURE_IDE0_enable);
-		break;
-	    case 1:
-		feature_set(np, FEATURE_IDE1_reset);
-		feature_clear(np, FEATURE_IDE1_enable);
-		break;
-	    case 2:
-		feature_set(np, FEATURE_IDE2_reset);
-		break;
-	}
+	/* Disable the bus */
+	ppc_md.feature_call(PMAC_FTR_IDE_ENABLE, np, pmac_ide[i].aapl_bus_id, 0);
 }
 
-static void idepmac_wake_interface(int i, unsigned long base, int mediabay)
+static void __pmac
+idepmac_wake_interface(int i, unsigned long base, int mediabay)
 {
 	struct device_node* np = pmac_ide[i].node;
 
 	if (!mediabay) {
 		/* Revive IDE disk and controller */
-		switch(pmac_ide[i].aapl_bus_id) {
-		    case 0:
-			feature_set(np, FEATURE_IDE0_reset);
-			feature_set(np, FEATURE_IOBUS_enable);
-			mdelay(10);
-	 		feature_set(np, FEATURE_IDE0_enable);
-			mdelay(10);
-			feature_clear(np, FEATURE_IDE0_reset);
-			break;
-		    case 1:
-			feature_set(np, FEATURE_IDE1_reset);
-			feature_set(np, FEATURE_IOBUS_enable);
-			mdelay(10);
-	 		feature_set(np, FEATURE_IDE1_enable);
-			mdelay(10);
-			feature_clear(np, FEATURE_IDE1_reset);
-			break;
-		    case 2:
-		    	/* This one exists only for KL, I don't know
-			   about any enable bit */
-			feature_set(np, FEATURE_IDE2_reset);
-			mdelay(10);
-			feature_clear(np, FEATURE_IDE2_reset);
-			break;
-		}
+		ppc_md.feature_call(PMAC_FTR_IDE_RESET, np, pmac_ide[i].aapl_bus_id, 1);
+		ppc_md.feature_call(PMAC_FTR_IDE_ENABLE, np, pmac_ide[i].aapl_bus_id, 1);
+		mdelay(10);
+		ppc_md.feature_call(PMAC_FTR_IDE_RESET, np, pmac_ide[i].aapl_bus_id, 0);
+	}
+}
+
+static void
+idepmac_sleep_drive(ide_drive_t *drive, int idx, unsigned long base)
+{
+	int unlock = 0;
+
+	/* Wait for HW group to complete operations */
+	if (ide_spin_wait_hwgroup(drive)) {
+		// What can we do here ? Wake drive we had already
+		// put to sleep and return an error ?
+	} else {
+		unlock = 1;
+		/* Lock HW group */
+		HWGROUP(drive)->busy = 1;
+		/* Stop the device */
+		idepmac_sleep_device(drive, idx, base);
 	}
+	if (unlock)
+		spin_unlock_irq(&io_request_lock);
+}
+
+static void
+idepmac_wake_drive(ide_drive_t *drive, unsigned long base)
+{
+	unsigned long flags;
+	int j;
 	
 	/* Reset timings */
-	pmac_ide_selectproc(&ide_hwifs[i].drives[0]);
+	pmac_ide_selectproc(drive);
 	mdelay(10);
+	
+	/* Wait up to 20 seconds for the drive to be ready */
+	for (j = 0; j < 200; j++) {
+		int status;
+		mdelay(100);
+		outb(drive->select.all, base + 0x60);
+		if (inb(base + 0x60) != drive->select.all)
+			continue;
+		status = inb(base + 0x70);
+		if (!(status & BUSY_STAT))
+			break;
+	}
+
+	/* We resume processing on the HW group */
+	spin_lock_irqsave(&io_request_lock, flags);
+	HWGROUP(drive)->busy = 0;
+	if (!list_empty(&drive->queue.queue_head))
+		ide_do_request(HWGROUP(drive), 0);
+	spin_unlock_irqrestore(&io_request_lock, flags);			
 }
 
 /* Note: We support only master drives for now. This will have to be
  * improved if we want to handle sleep on the iMacDV where the CD-ROM
  * is a slave
  */
-static int idepmac_notify_sleep(struct pmu_sleep_notifier *self, int when)
+static int __pmac
+idepmac_notify_sleep(struct pmu_sleep_notifier *self, int when)
 {
 	int i, ret;
 	unsigned long base;
-	unsigned long flags;
 	int big_delay;
-
+ 
 	switch (when) {
 	case PBOOK_SLEEP_REQUEST:
 		break;
@@ -1193,34 +1480,19 @@
 	case PBOOK_SLEEP_NOW:
 		for (i = 0; i < pmac_ide_count; ++i) {
 			ide_hwif_t *hwif;
-			ide_drive_t *drive;
-			int unlock = 0;
+			int dn;
 
 			if ((base = pmac_ide[i].regbase) == 0)
-				continue;	
+				continue;
 
 			hwif = &ide_hwifs[i];
-			drive = &hwif->drives[0];
-			
-			if (drive->present) {
-				/* Wait for HW group to complete operations */
-				if (ide_spin_wait_hwgroup(drive)) {
-					// What can we do here ? Wake drive we had already
-					// put to sleep and return an error ?
-				} else {
-					unlock = 1;
-					/* Lock HW group */
-					HWGROUP(drive)->busy = 1;
-
-					/* Stop the device */
-					idepmac_sleep_device(drive, i, base);
-				
-				}
+			for (dn=0; dn<MAX_DRIVES; dn++) {
+				if (!hwif->drives[dn].present)
+					continue;
+				idepmac_sleep_drive(&hwif->drives[dn], i, base);
 			}
 			/* Disable irq during sleep */
 			disable_irq(pmac_ide[i].irq);
-			if (unlock)
-				spin_unlock_irq(&io_request_lock);
 			
 			/* Check if this is a media bay with an IDE device or not
 			 * a media bay.
@@ -1237,6 +1509,9 @@
 			if ((base = pmac_ide[i].regbase) == 0)
 				continue;
 				
+			/* Make sure we have sane timings */		
+			sanitize_timings(i);
+
 			/* Check if this is a media bay with an IDE device or not
 			 * a media bay
 			 */
@@ -1253,46 +1528,79 @@
 	
 		for (i = 0; i < pmac_ide_count; ++i) {
 			ide_hwif_t *hwif;
-			ide_drive_t *drive;			
-			int j, used_dma;
+			int used_dma, dn;
+			int irq_on = 0;
 			
 			if ((base = pmac_ide[i].regbase) == 0)
 				continue;
 				
 			hwif = &ide_hwifs[i];
-			drive = &hwif->drives[0];
-
-			/* Wait for the drive to come up and set it's DMA */
-			if (drive->present) {
-				/* Wait up to 20 seconds */
-				for (j = 0; j < 200; j++) {
-					int status;
-					mdelay(100);
-					status = inb(base + 0x70);
-					if (!(status & BUSY_STAT))
-						break;
+			for (dn=0; dn<MAX_DRIVES; dn++) {
+				ide_drive_t *drive = &hwif->drives[dn];
+				if (!drive->present)
+					continue;
+				/* We don't have re-configured DMA yet */
+				used_dma = drive->using_dma;
+				drive->using_dma = 0;
+				idepmac_wake_drive(drive, base);
+				if (!irq_on) {
+					enable_irq(pmac_ide[i].irq);
+					irq_on = 1;
 				}
-			}
-			
-			/* We don't have re-configured DMA yet */
-			used_dma = drive->using_dma;
-			drive->using_dma = 0;
-
-			/* We resume processing on the HW group */
-			spin_lock_irqsave(&io_request_lock, flags);
-			enable_irq(pmac_ide[i].irq);
-			if (drive->present)
-				HWGROUP(drive)->busy = 0;
-			spin_unlock_irqrestore(&io_request_lock, flags);
-			
-			/* Wake the device
-			 * We could handle the slave here
-			 */
-			if (drive->present)
 				idepmac_wake_device(drive, used_dma);
+			}
+			if (!irq_on)
+				enable_irq(pmac_ide[i].irq);
 		}
 		break;
 	}
 	return PBOOK_SLEEP_OK;
 }
 #endif /* CONFIG_PMAC_PBOOK */
+
+static int __pmac
+pmac_ide_notify_reboot(struct notifier_block *this, unsigned long code, void *x)
+{
+	int i, gotone;
+	unsigned long base;
+
+	if (code != SYS_HALT && code != SYS_POWER_OFF)
+		return 0;
+
+	gotone = 0;
+	for (i = 0; i < pmac_ide_count; ++i) {
+		ide_hwif_t *hwif;
+		ide_drive_t *drive;
+		int unlock = 0;
+		int dn;
+
+		if ((base = pmac_ide[i].regbase) == 0)
+				continue;	
+
+		hwif = &ide_hwifs[i];
+		for (dn=0; dn<MAX_DRIVES; dn++) {
+			drive = &hwif->drives[dn];
+			if (drive->present) {
+				gotone = 1;
+				/* Wait for HW group to complete operations */
+				if (ide_spin_wait_hwgroup(drive)) {
+					// What can we do here ? Wake drive we had already
+					// put to sleep and return an error ?
+				} else {
+					unlock = 1;
+					/* Lock HW group */
+					HWGROUP(drive)->busy = 1;
+
+					/* Stop the device */
+					idepmac_sleep_device(drive, i, base);
+				}
+			}
+			if (unlock)
+				spin_unlock_irq(&io_request_lock);
+		}
+	}
+	if (gotone)
+		mdelay(1000);
+		
+	return NOTIFY_DONE;
+}
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/ide/ide.c linux/drivers/ide/ide.c
--- linux.orig/drivers/ide/ide.c	Mon Jan  7 23:41:09 2002
+++ linux/drivers/ide/ide.c	Wed Dec 26 16:47:47 2001
@@ -1367,7 +1367,10 @@
  * the driver.  This makes the driver much more friendlier to shared IRQs
  * than previous designs, while remaining 100% (?) SMP safe and capable.
  */
-static void ide_do_request(ide_hwgroup_t *hwgroup, int masked_irq)
+/* --BenH: made non-static as ide-pmac.c uses it to kick the hwgroup back
+ *         into life on wakeup from machine sleep.
+ */ 
+void ide_do_request(ide_hwgroup_t *hwgroup, int masked_irq)
 {
 	ide_drive_t	*drive;
 	ide_hwif_t	*hwif;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/ieee1394/sbp2.c linux/drivers/ieee1394/sbp2.c
--- linux.orig/drivers/ieee1394/sbp2.c	Fri Dec 21 17:41:54 2001
+++ linux/drivers/ieee1394/sbp2.c	Mon Jan  7 14:06:24 2002
@@ -2773,7 +2773,9 @@
 	/*
 	 * Tell scsi stack that we're done with this command
 	 */
+	spin_lock_irq(&io_request_lock);
 	done (SCpnt);
+	spin_unlock_irq(&io_request_lock);
 
 	return;
 }
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/isdn/avmb1/capi.c linux/drivers/isdn/avmb1/capi.c
--- linux.orig/drivers/isdn/avmb1/capi.c	Fri Dec 21 17:41:54 2001
+++ linux/drivers/isdn/avmb1/capi.c	Mon Jan  7 23:29:49 2002
@@ -87,10 +87,10 @@
 	struct capincci  *nccip;
 	unsigned int      minor;
 
-	__u16		 applid;
-	__u32		 ncci;
-	__u16		 datahandle;
-	__u16		 msgid;
+	u16  		 applid;
+	u32		 ncci;
+	u16		 datahandle;
+	u16		 msgid;
 
 	struct file      *file;
 	struct tty_struct *tty;
@@ -112,7 +112,7 @@
 	/* transmit path */
 	struct datahandle_queue {
 		    struct datahandle_queue *next;
-		    __u16                    datahandle;
+		    u16                      datahandle;
 	} *ackqueue;
 	int nack;
 
@@ -121,7 +121,7 @@
 
 struct capincci {
 	struct capincci *next;
-	__u32		 ncci;
+	u32		 ncci;
 	struct capidev	*cdev;
 #ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
 	struct capiminor *minorp;
@@ -131,8 +131,8 @@
 struct capidev {
 	struct capidev *next;
 	struct file    *file;
-	__u16		applid;
-	__u16		errcode;
+	u16		applid;
+	u16		errcode;
 	unsigned int    minor;
 	unsigned        userflags;
 
@@ -166,7 +166,7 @@
 #ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
 /* -------- datahandles --------------------------------------------- */
 
-int capincci_add_ack(struct capiminor *mp, __u16 datahandle)
+static int capincci_add_ack(struct capiminor *mp, u16 datahandle)
 {
 	struct datahandle_queue *n, **pp;
 
@@ -184,7 +184,7 @@
 	return 0;
 }
 
-int capiminor_del_ack(struct capiminor *mp, __u16 datahandle)
+static int capiminor_del_ack(struct capiminor *mp, u16 datahandle)
 {
 	struct datahandle_queue **pp, *p;
 
@@ -200,7 +200,7 @@
 	return -1;
 }
 
-void capiminor_del_all_ack(struct capiminor *mp)
+static void capiminor_del_all_ack(struct capiminor *mp)
 {
 	struct datahandle_queue **pp, *p;
 
@@ -216,7 +216,7 @@
 
 /* -------- struct capiminor ---------------------------------------- */
 
-struct capiminor *capiminor_alloc(__u16 applid, __u32 ncci)
+static struct capiminor *capiminor_alloc(u16 applid, u32 ncci)
 {
 	struct capiminor *mp, **pp;
         unsigned int minor = 0;
@@ -257,7 +257,7 @@
 	return mp;
 }
 
-void capiminor_free(struct capiminor *mp)
+static void capiminor_free(struct capiminor *mp)
 {
 	struct capiminor **pp;
 
@@ -283,7 +283,7 @@
 	}
 }
 
-struct capiminor *capiminor_find(unsigned int minor)
+static struct capiminor *capiminor_find(unsigned int minor)
 {
 	struct capiminor *p;
 	for (p = minors; p && p->minor != minor; p = p->next)
@@ -294,7 +294,7 @@
 
 /* -------- struct capincci ----------------------------------------- */
 
-static struct capincci *capincci_alloc(struct capidev *cdev, __u32 ncci)
+static struct capincci *capincci_alloc(struct capidev *cdev, u32 ncci)
 {
 	struct capincci *np, **pp;
 #ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
@@ -331,7 +331,7 @@
         return np;
 }
 
-static void capincci_free(struct capidev *cdev, __u32 ncci)
+static void capincci_free(struct capidev *cdev, u32 ncci)
 {
 	struct capincci *np, **pp;
 #ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
@@ -375,7 +375,7 @@
 	}
 }
 
-struct capincci *capincci_find(struct capidev *cdev, __u32 ncci)
+static struct capincci *capincci_find(struct capidev *cdev, u32 ncci)
 {
 	struct capincci *p;
 
@@ -426,7 +426,7 @@
 	kmem_cache_free(capidev_cachep, cdev);
 }
 
-static struct capidev *capidev_find(__u16 applid)
+static struct capidev *capidev_find(u16 applid)
 {
 	struct capidev *p;
 	for (p=capidev_openlist; p; p = p->next) {
@@ -439,13 +439,13 @@
 #ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
 /* -------- handle data queue --------------------------------------- */
 
-struct sk_buff *
+static struct sk_buff *
 gen_data_b3_resp_for(struct capiminor *mp, struct sk_buff *skb)
 {
 	struct sk_buff *nskb;
 	nskb = alloc_skb(CAPI_DATA_B3_RESP_LEN, GFP_ATOMIC);
 	if (nskb) {
-		__u16 datahandle = CAPIMSG_U16(skb->data,CAPIMSG_BASELEN+4+4+2);
+		u16 datahandle = CAPIMSG_U16(skb->data,CAPIMSG_BASELEN+4+4+2);
 		unsigned char *s = skb_put(nskb, CAPI_DATA_B3_RESP_LEN);
 		capimsg_setu16(s, 0, CAPI_DATA_B3_RESP_LEN);
 		capimsg_setu16(s, 2, mp->applid);
@@ -458,11 +458,11 @@
 	return nskb;
 }
 
-int handle_recv_skb(struct capiminor *mp, struct sk_buff *skb)
+static int handle_recv_skb(struct capiminor *mp, struct sk_buff *skb)
 {
 	struct sk_buff *nskb;
 	unsigned int datalen;
-	__u16 errcode, datahandle;
+	u16 errcode, datahandle;
 
 	datalen = skb->len - CAPIMSG_LEN(skb->data);
 	if (mp->tty) {
@@ -538,7 +538,7 @@
 	return -1;
 }
 
-void handle_minor_recv(struct capiminor *mp)
+static void handle_minor_recv(struct capiminor *mp)
 {
 	struct sk_buff *skb;
 	while ((skb = skb_dequeue(&mp->inqueue)) != 0) {
@@ -552,13 +552,13 @@
 	}
 }
 
-int handle_minor_send(struct capiminor *mp)
+static int handle_minor_send(struct capiminor *mp)
 {
 	struct sk_buff *skb;
-	__u16 len;
+	u16 len;
 	int count = 0;
-	__u16 errcode;
-	__u16 datahandle;
+	u16 errcode;
+	u16 datahandle;
 
 	if (mp->tty && mp->ttyoutstop) {
 #if defined(_DEBUG_DATAFLOW) || defined(_DEBUG_TTYFUNCS)
@@ -569,7 +569,7 @@
 
 	while ((skb = skb_dequeue(&mp->outqueue)) != 0) {
 		datahandle = mp->datahandle;
-		len = (__u16)skb->len;
+		len = (u16)skb->len;
 		skb_push(skb, CAPI_DATA_B3_REQ_LEN);
 		memset(skb->data, 0, CAPI_DATA_B3_REQ_LEN);
 		capimsg_setu16(skb->data, 0, CAPI_DATA_B3_REQ_LEN);
@@ -578,7 +578,7 @@
 		capimsg_setu8 (skb->data, 5, CAPI_REQ);
 		capimsg_setu16(skb->data, 6, mp->msgid++);
 		capimsg_setu32(skb->data, 8, mp->ncci);	/* NCCI */
-		capimsg_setu32(skb->data, 12, (__u32) skb->data); /* Data32 */
+		capimsg_setu32(skb->data, 12, (u32) skb->data); /* Data32 */
 		capimsg_setu16(skb->data, 16, len);	/* Data length */
 		capimsg_setu16(skb->data, 18, datahandle);
 		capimsg_setu16(skb->data, 20, 0);	/* Flags */
@@ -620,16 +620,16 @@
 #endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
 /* -------- function called by lower level -------------------------- */
 
-static void capi_signal(__u16 applid, void *param)
+static void capi_signal(u16 applid, void *param)
 {
 	struct capidev *cdev = (struct capidev *)param;
 #ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
 	struct capiminor *mp;
-	__u16 datahandle;
+	u16 datahandle;
 #endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
 	struct capincci *np;
 	struct sk_buff *skb = 0;
-	__u32 ncci;
+	u32 ncci;
 
 	(void) (*capifuncs->capi_get_message) (applid, &skb);
 	if (!skb) {
@@ -758,7 +758,7 @@
 	struct capidev *cdev = (struct capidev *)file->private_data;
 	struct sk_buff *skb;
 	int retval;
-	__u16 mlen;
+	u16 mlen;
 
         if (ppos != &file->f_pos)
 		return -ESPIPE;
@@ -998,7 +998,7 @@
 						sizeof(ncci));
 			if (retval)
 				return -EFAULT;
-			nccip = capincci_find(cdev, (__u32) ncci);
+			nccip = capincci_find(cdev, (u32) ncci);
 			if (!nccip)
 				return 0;
 #ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
@@ -1023,7 +1023,7 @@
 						sizeof(ncci));
 			if (retval)
 				return -EFAULT;
-			nccip = capincci_find(cdev, (__u32) ncci);
+			nccip = capincci_find(cdev, (u32) ncci);
 			if (!nccip || (mp = nccip->minorp) == 0)
 				return -ESRCH;
 			return mp->minor;
@@ -1272,7 +1272,7 @@
 
 /* -------- tty_operations for capincci ----------------------------- */
 
-int capinc_tty_open(struct tty_struct * tty, struct file * file)
+static int capinc_tty_open(struct tty_struct * tty, struct file * file)
 {
 	struct capiminor *mp;
 
@@ -1300,7 +1300,7 @@
 	return 0;
 }
 
-void capinc_tty_close(struct tty_struct * tty, struct file * file)
+static void capinc_tty_close(struct tty_struct * tty, struct file * file)
 {
 	struct capiminor *mp;
 
@@ -1325,8 +1325,8 @@
 #endif
 }
 
-int capinc_tty_write(struct tty_struct * tty, int from_user,
-		      const unsigned char *buf, int count)
+static int capinc_tty_write(struct tty_struct * tty, int from_user,
+			    const unsigned char *buf, int count)
 {
 	struct capiminor *mp = (struct capiminor *)tty->driver_data;
 	struct sk_buff *skb;
@@ -1377,7 +1377,7 @@
 	return count;
 }
 
-void capinc_tty_put_char(struct tty_struct *tty, unsigned char ch)
+static void capinc_tty_put_char(struct tty_struct *tty, unsigned char ch)
 {
 	struct capiminor *mp = (struct capiminor *)tty->driver_data;
 	struct sk_buff *skb;
@@ -1414,7 +1414,7 @@
 	}
 }
 
-void capinc_tty_flush_chars(struct tty_struct *tty)
+static void capinc_tty_flush_chars(struct tty_struct *tty)
 {
 	struct capiminor *mp = (struct capiminor *)tty->driver_data;
 	struct sk_buff *skb;
@@ -1440,7 +1440,7 @@
 	(void)handle_minor_recv(mp);
 }
 
-int capinc_tty_write_room(struct tty_struct *tty)
+static int capinc_tty_write_room(struct tty_struct *tty)
 {
 	struct capiminor *mp = (struct capiminor *)tty->driver_data;
 	int room;
@@ -1458,7 +1458,7 @@
 	return room;
 }
 
-int capinc_tty_chars_in_buffer(struct tty_struct *tty)
+static int capinc_tty_chars_in_buffer(struct tty_struct *tty)
 {
 	struct capiminor *mp = (struct capiminor *)tty->driver_data;
 	if (!mp || !mp->nccip) {
@@ -1476,7 +1476,7 @@
 	return mp->outbytes;
 }
 
-int capinc_tty_ioctl(struct tty_struct *tty, struct file * file,
+static int capinc_tty_ioctl(struct tty_struct *tty, struct file * file,
 		    unsigned int cmd, unsigned long arg)
 {
 	int error = 0;
@@ -1488,14 +1488,14 @@
 	return error;
 }
 
-void capinc_tty_set_termios(struct tty_struct *tty, struct termios * old)
+static void capinc_tty_set_termios(struct tty_struct *tty, struct termios * old)
 {
 #ifdef _DEBUG_TTYFUNCS
 	printk(KERN_DEBUG "capinc_tty_set_termios\n");
 #endif
 }
 
-void capinc_tty_throttle(struct tty_struct * tty)
+static void capinc_tty_throttle(struct tty_struct * tty)
 {
 	struct capiminor *mp = (struct capiminor *)tty->driver_data;
 #ifdef _DEBUG_TTYFUNCS
@@ -1505,7 +1505,7 @@
 		mp->ttyinstop = 1;
 }
 
-void capinc_tty_unthrottle(struct tty_struct * tty)
+static void capinc_tty_unthrottle(struct tty_struct * tty)
 {
 	struct capiminor *mp = (struct capiminor *)tty->driver_data;
 #ifdef _DEBUG_TTYFUNCS
@@ -1517,7 +1517,7 @@
 	}
 }
 
-void capinc_tty_stop(struct tty_struct *tty)
+static void capinc_tty_stop(struct tty_struct *tty)
 {
 	struct capiminor *mp = (struct capiminor *)tty->driver_data;
 #ifdef _DEBUG_TTYFUNCS
@@ -1528,7 +1528,7 @@
 	}
 }
 
-void capinc_tty_start(struct tty_struct *tty)
+static void capinc_tty_start(struct tty_struct *tty)
 {
 	struct capiminor *mp = (struct capiminor *)tty->driver_data;
 #ifdef _DEBUG_TTYFUNCS
@@ -1540,49 +1540,43 @@
 	}
 }
 
-void capinc_tty_hangup(struct tty_struct *tty)
+static void capinc_tty_hangup(struct tty_struct *tty)
 {
 #ifdef _DEBUG_TTYFUNCS
 	printk(KERN_DEBUG "capinc_tty_hangup\n");
 #endif
 }
 
-void capinc_tty_break_ctl(struct tty_struct *tty, int state)
+static void capinc_tty_break_ctl(struct tty_struct *tty, int state)
 {
 #ifdef _DEBUG_TTYFUNCS
 	printk(KERN_DEBUG "capinc_tty_break_ctl(%d)\n", state);
 #endif
 }
 
-void capinc_tty_flush_buffer(struct tty_struct *tty)
+static void capinc_tty_flush_buffer(struct tty_struct *tty)
 {
 #ifdef _DEBUG_TTYFUNCS
 	printk(KERN_DEBUG "capinc_tty_flush_buffer\n");
 #endif
 }
 
-void capinc_tty_set_ldisc(struct tty_struct *tty)
+static void capinc_tty_set_ldisc(struct tty_struct *tty)
 {
 #ifdef _DEBUG_TTYFUNCS
 	printk(KERN_DEBUG "capinc_tty_set_ldisc\n");
 #endif
 }
 
-void capinc_tty_send_xchar(struct tty_struct *tty, char ch)
+static void capinc_tty_send_xchar(struct tty_struct *tty, char ch)
 {
 #ifdef _DEBUG_TTYFUNCS
 	printk(KERN_DEBUG "capinc_tty_send_xchar(%d)\n", ch);
 #endif
 }
 
-int capinc_tty_read_proc(char *page, char **start, off_t off,
-			  int count, int *eof, void *data)
-{
-	return 0;
-}
-
-int capinc_write_proc(struct file *file, const char *buffer,
-			  unsigned long count, void *data)
+static int capinc_tty_read_proc(char *page, char **start, off_t off,
+				int count, int *eof, void *data)
 {
 	return 0;
 }
@@ -1594,7 +1588,7 @@
 static struct termios *capinc_tty_termios[CAPINC_NR_PORTS];
 static struct termios *capinc_tty_termios_locked[CAPINC_NR_PORTS];
 
-int capinc_tty_init(void)
+static int capinc_tty_init(void)
 {
 	struct tty_driver *drv = &capinc_tty_driver;
 
@@ -1652,7 +1646,7 @@
 	return 0;
 }
 
-void capinc_tty_exit(void)
+static void capinc_tty_exit(void)
 {
 	struct tty_driver *drv = &capinc_tty_driver;
 	int retval;
@@ -1844,7 +1838,7 @@
 	return 0;
 }
 
-static void lower_callback(unsigned int cmd, __u32 contr, void *data)
+static void lower_callback(unsigned int cmd, u32 contr, void *data)
 {
 	struct capi_ncciinfo *np;
 	struct capidev *cdev;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/macintosh/Makefile linux/drivers/macintosh/Makefile
--- linux.orig/drivers/macintosh/Makefile	Mon Jan  7 23:41:09 2002
+++ linux/drivers/macintosh/Makefile	Wed Dec 26 16:32:31 2001
@@ -32,9 +32,11 @@
 obj-$(CONFIG_MAC_HID)		+= mac_hid.o
 obj-$(CONFIG_INPUT_ADBHID)	+= adbhid.o
 obj-$(CONFIG_PPC_RTC)		+= rtc.o
+obj-$(CONFIG_ANSLCD)		+= ans-lcd.o
 
 obj-$(CONFIG_ADB_PMU)		+= via-pmu.o
 obj-$(CONFIG_ADB_CUDA)		+= via-cuda.o
+obj-$(CONFIG_PMAC_APM_EMU)	+= apm_emu.o
 
 obj-$(CONFIG_ADB)		+= adb.o
 obj-$(CONFIG_ADB_KEYBOARD)	+= mac_keyb.o
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/macintosh/adb.c linux/drivers/macintosh/adb.c
--- linux.orig/drivers/macintosh/adb.c	Mon Jan  7 23:41:09 2002
+++ linux/drivers/macintosh/adb.c	Wed Dec 26 16:32:31 2001
@@ -34,6 +34,7 @@
 #include <linux/wait.h>
 #include <linux/init.h>
 #include <linux/delay.h>
+#include <linux/completion.h>
 #include <asm/uaccess.h>
 #ifdef CONFIG_PPC
 #include <asm/prom.h>
@@ -78,7 +79,7 @@
 static int adb_inited = 0;
 static pid_t adb_probe_task_pid;
 static int adb_probe_task_flag;
-static wait_queue_head_t adb_probe_task_wq;
+static struct completion adb_probe_task_comp;
 static int sleepy_trackpad;
 int __adb_probe_sync;
 
@@ -318,7 +319,7 @@
 		if (machine_is_compatible("AAPL,PowerBook1998") ||
 			machine_is_compatible("PowerBook1,1"))
 			sleepy_trackpad = 1;
-		init_waitqueue_head(&adb_probe_task_wq);
+		init_completion(&adb_probe_task_comp);
 		adbdev_init();
 		adb_reset_bus();
 	}
@@ -435,7 +436,7 @@
 static void
 adb_probe_wakeup(struct adb_request *req)
 {
-	wake_up(&adb_probe_task_wq);
+	complete(&adb_probe_task_comp);
 }
 
 static struct adb_request adb_sreq;
@@ -484,20 +485,11 @@
 	if ((flags & ADBREQ_SYNC) &&
 	    (current->pid && adb_probe_task_pid &&
 	    adb_probe_task_pid == current->pid)) {
-		DECLARE_WAITQUEUE(wait, current);
 		req->done = adb_probe_wakeup;
-		add_wait_queue(&adb_probe_task_wq, &wait);
 		rc = adb_controller->send_request(req, 0);
 		if (rc || req->complete)
 			goto bail;
-		for (;;) {
-			set_current_state(TASK_UNINTERRUPTIBLE);
-			if (req->complete)
-				break;
-			schedule();
-		}
-		current->state = TASK_RUNNING;
-		remove_wait_queue(&adb_probe_task_wq, &wait);
+		wait_for_completion(&adb_probe_task_comp);
 		rc = 0;
 		goto bail;
 	}
@@ -707,17 +699,16 @@
 		return ret;
 
 	req = NULL;
+	spin_lock_irqsave(&state->lock, flags);
 	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;
 		
@@ -729,12 +720,15 @@
 			ret = -ERESTARTSYS;
 			break;
 		}
+		spin_unlock_irqrestore(&state->lock, flags);
 		schedule();
+		spin_lock_irqsave(&state->lock, flags);
 	}
 
 	current->state = TASK_RUNNING;
 	remove_wait_queue(&state->wait_queue, &wait);
-
+	spin_unlock_irqrestore(&state->lock, flags);
+	
 	if (ret)
 		return ret;
 
@@ -757,6 +751,8 @@
 
 	if (count < 2 || count > sizeof(req->data))
 		return -EINVAL;
+	if (adb_controller == NULL)
+		return -ENXIO;
 	ret = verify_area(VERIFY_READ, buf, count);
 	if (ret)
 		return ret;
@@ -776,7 +772,10 @@
 		goto out;
 
 	atomic_inc(&state->n_pending);
-	if (adb_controller == NULL) return -ENXIO;
+
+	/* If a probe is in progress, wait for it to complete */
+	while (adb_probe_task_pid != 0 || test_bit(0, &adb_probe_task_flag))
+		schedule();
 
 	/* Special case for ADB_BUSRESET request, all others are sent to
 	   the controller */
@@ -784,6 +783,8 @@
 		&&(req->data[1] == ADB_BUSRESET)) {
 		ret = do_adb_reset_bus();
 		atomic_dec(&state->n_pending);
+		if (ret == 0)
+			ret = count;
 		goto out;
 	} else {	
 		req->reply_expected = ((req->data[1] & 0xc) == 0xc);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/macintosh/adbhid.c linux/drivers/macintosh/adbhid.c
--- linux.orig/drivers/macintosh/adbhid.c	Mon Jan  7 23:41:09 2002
+++ linux/drivers/macintosh/adbhid.c	Wed Dec 26 16:32:31 2001
@@ -273,26 +273,39 @@
 		break;
 	case 0x1f: /* Powerbook button device */
 	  {
+	  	int down = (data[1] == (data[1] & 0xf));
 #ifdef CONFIG_PMAC_BACKLIGHT
 		int backlight = get_backlight_level();
-
+#endif
 		/*
 		 * XXX: Where is the contrast control for the passive?
 		 *  -- Cort
 		 */
 
-		switch (data[1]) {
+		switch (data[1] & 0x0f) {
 		case 0x8:	/* mute */
+			input_report_key(&adbhid[id]->input, KEY_MUTE,
+				data[1] == (data[1] & 0xf));
 			break;
 
-		case 0x7:	/* contrast decrease */
+		case 0x7:	/* volume decrease */
+			input_report_key(&adbhid[id]->input, KEY_VOLUMEDOWN,
+				data[1] == (data[1] & 0xf));
 			break;
 
-		case 0x6:	/* contrast increase */
+		case 0x6:	/* volume increase */
+			input_report_key(&adbhid[id]->input, KEY_VOLUMEUP,
+				data[1] == (data[1] & 0xf));
+ 			break;
+
+		case 0xb:	/* eject */
+			input_report_key(&adbhid[id]->input, KEY_EJECTCD,
+				data[1] == (data[1] & 0xf));
 			break;
 
+#ifdef CONFIG_PMAC_BACKLIGHT
 		case 0xa:	/* brightness decrease */
-			if (backlight < 0)
+			if (!down || backlight < 0)
 				break;
 			if (backlight > BACKLIGHT_OFF)
 				set_backlight_level(backlight-1);
@@ -301,15 +314,15 @@
 			break;
 
 		case 0x9:	/* brightness increase */
-			if (backlight < 0)
+			if (!down || backlight < 0)
 				break;
 			if (backlight < BACKLIGHT_MAX)
 				set_backlight_level(backlight+1);
 			else 
 				set_backlight_level(BACKLIGHT_MAX);
 			break;
-		}
 #endif /* CONFIG_PMAC_BACKLIGHT */
+		}
 	  }
 	  break;
 	}
@@ -504,6 +517,11 @@
 		case 0x1f: /* Powerbook button device */
 			sprintf(adbhid[id]->name, "ADB Powerbook buttons on ID %d:%d.%02x",
 				id, default_id, original_handler_id);
+			adbhid[id]->input.evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
+			set_bit(KEY_MUTE, adbhid[id]->input.keybit);
+			set_bit(KEY_VOLUMEUP, adbhid[id]->input.keybit);
+			set_bit(KEY_VOLUMEDOWN, adbhid[id]->input.keybit);
+			set_bit(KEY_EJECTCD, adbhid[id]->input.keybit);
 			break;
 		}
 		if (adbhid[id]->name[0])
@@ -542,16 +560,38 @@
 }
 
 
+static u16
+adbhid_input_reregister(int id, int default_id, int org_handler_id,
+			int cur_handler_id, int mk)
+{
+	if (adbhid[id]) {
+		if (adbhid[id]->input.idproduct !=
+		    ((id << 12)|(default_id << 8)|org_handler_id)) {
+			adbhid_input_unregister(id);
+			adbhid_input_register(id, default_id, org_handler_id,
+				cur_handler_id, mk);
+		}
+	} else
+		adbhid_input_register(id, default_id, org_handler_id,
+			cur_handler_id, mk);
+	return 1<<id;
+}
+
+static void
+adbhid_input_devcleanup(u16 exist)
+{
+	int i;
+	for(i=1; i<16; i++)
+	    if (adbhid[i] && !(exist&(1<<i)))
+		adbhid_input_unregister(i);
+}
+ 
 static void
 adbhid_probe(void)
 {
 	struct adb_request req;
 	int i, default_id, org_handler_id, cur_handler_id;
-
-	for (i = 1; i < 16; i++) {
-		if (adbhid[i])
-			adbhid_input_unregister(i);
-	}
+	u16 reg = 0;
 
 	adb_register(ADB_MOUSE, 0, &mouse_ids, adbhid_mouse_input);
 	adb_register(ADB_KEYBOARD, 0, &keyboard_ids, adbhid_keyboard_input);
@@ -580,14 +620,14 @@
 			printk("ADB keyboard at %d, handler 1\n", id);
 
 		adb_get_infos(id, &default_id, &cur_handler_id);
-		adbhid_input_register(id, default_id, org_handler_id, cur_handler_id, 0);
+		reg |= adbhid_input_reregister(id, default_id, org_handler_id, cur_handler_id, 0);
 	}
 
 	for (i = 0; i < buttons_ids.nids; i++) {
 		int id = buttons_ids.id[i];
 
 		adb_get_infos(id, &default_id, &org_handler_id);
-		adbhid_input_register(id, default_id, org_handler_id, org_handler_id, 0);
+		reg |= adbhid_input_reregister(id, default_id, org_handler_id, org_handler_id, 0);
 	}
 
 	/* Try to switch all mice to handler 4, or 2 for three-button
@@ -676,9 +716,10 @@
 		printk("\n");
 
 		adb_get_infos(id, &default_id, &cur_handler_id);
-		adbhid_input_register(id, default_id, org_handler_id,
+		reg |= adbhid_input_reregister(id, default_id, org_handler_id,
 				      cur_handler_id, mouse_kind);
         }
+	adbhid_input_devcleanup(reg);
 }
 
 static void 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/macintosh/ans-lcd.c linux/drivers/macintosh/ans-lcd.c
--- linux.orig/drivers/macintosh/ans-lcd.c	Thu Jan  1 00:00:00 1970
+++ linux/drivers/macintosh/ans-lcd.c	Mon Jan  7 13:00:34 2002
@@ -0,0 +1,171 @@
+/*
+ * /dev/lcd driver for Apple Network Servers.
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/fcntl.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <asm/uaccess.h>
+#include <asm/sections.h>
+#include <asm/prom.h>
+#include <asm/ans-lcd.h>
+#include <asm/io.h>
+
+#define ANSLCD_ADDR		0xf301c000
+#define ANSLCD_CTRL_IX 0x00
+#define ANSLCD_DATA_IX 0x10
+
+static unsigned long anslcd_short_delay = 80;
+static unsigned long anslcd_long_delay = 3280;
+static volatile unsigned char* anslcd_ptr;
+
+#undef DEBUG
+
+static void __pmac
+anslcd_write_byte_ctrl ( unsigned char c )
+{
+#ifdef DEBUG
+	printk(KERN_DEBUG "LCD: CTRL byte: %02x\n",c);
+#endif
+	out_8(anslcd_ptr + ANSLCD_CTRL_IX, c);
+	switch(c) {
+		case 1:
+		case 2:
+		case 3:
+			udelay(anslcd_long_delay); break;
+		default: udelay(anslcd_short_delay);
+	}
+}
+
+static void __pmac
+anslcd_write_byte_data ( unsigned char c )
+{
+	out_8(anslcd_ptr + ANSLCD_DATA_IX, c);
+	udelay(anslcd_short_delay);
+}
+
+static ssize_t __pmac
+anslcd_write( struct file * file, const char * buf, 
+				size_t count, loff_t *ppos )
+{
+	const char * p = buf;
+	int i;
+
+#ifdef DEBUG
+	printk(KERN_DEBUG "LCD: write\n");
+#endif
+
+	if ( verify_area(VERIFY_READ, buf, count) )
+		return -EFAULT;
+	for ( i = *ppos; count > 0; ++i, ++p, --count ) 
+	{
+		char c;
+		__get_user(c, p);
+		anslcd_write_byte_data( c );
+	}
+	*ppos = i;
+	return p - buf;
+}
+
+static int __pmac
+anslcd_ioctl( struct inode * inode, struct file * file,
+				unsigned int cmd, unsigned long arg )
+{
+	char ch, *temp;
+
+#ifdef DEBUG
+	printk(KERN_DEBUG "LCD: ioctl(%d,%d)\n",cmd,arg);
+#endif
+
+	switch ( cmd )
+	{
+	case ANSLCD_CLEAR:
+		anslcd_write_byte_ctrl ( 0x38 );
+		anslcd_write_byte_ctrl ( 0x0f );
+		anslcd_write_byte_ctrl ( 0x06 );
+		anslcd_write_byte_ctrl ( 0x01 );
+		anslcd_write_byte_ctrl ( 0x02 );
+		return 0;
+	case ANSLCD_SENDCTRL:
+		temp = (char *) arg;
+		__get_user(ch, temp);
+		for (; ch; temp++) { /* FIXME: This is ugly, but should work, as a \0 byte is not a valid command code */
+			anslcd_write_byte_ctrl ( ch );
+			__get_user(ch, temp);
+		}
+		return 0;
+	case ANSLCD_SETSHORTDELAY:
+		if (!capable(CAP_SYS_ADMIN))
+			return -EACCES;
+		anslcd_short_delay=arg;
+		return 0;
+	case ANSLCD_SETLONGDELAY:
+		if (!capable(CAP_SYS_ADMIN))
+			return -EACCES;
+		anslcd_long_delay=arg;
+		return 0;
+	default:
+		return -EINVAL;
+	}
+}
+
+static int __pmac
+anslcd_open( struct inode * inode, struct file * file )
+{
+	return 0;
+}
+
+struct file_operations anslcd_fops = {
+	write:	anslcd_write,
+	ioctl:	anslcd_ioctl,
+	open:	anslcd_open,
+};
+
+static struct miscdevice anslcd_dev = {
+	ANSLCD_MINOR,
+	"anslcd",
+	&anslcd_fops
+};
+
+const char anslcd_logo[] =	"********************"  /* Line #1 */
+				"*      LINUX!      *"  /* Line #3 */
+				"*    Welcome to    *"  /* Line #2 */
+				"********************"; /* Line #4 */
+
+int __init
+anslcd_init(void)
+{
+	int a;
+	struct device_node* node;
+
+	node = find_devices("lcd");
+	if (!node || !node->parent)
+		return -ENODEV;
+	if (strcmp(node->parent->name, "gc"))
+		return -ENODEV;
+
+	anslcd_ptr = (volatile unsigned char*)ioremap(ANSLCD_ADDR, 0x20);
+	
+	misc_register(&anslcd_dev);
+
+#ifdef DEBUG
+	printk(KERN_DEBUG "LCD: init\n");
+#endif
+
+	anslcd_write_byte_ctrl ( 0x38 );
+	anslcd_write_byte_ctrl ( 0x0c );
+	anslcd_write_byte_ctrl ( 0x06 );
+	anslcd_write_byte_ctrl ( 0x01 );
+	anslcd_write_byte_ctrl ( 0x02 );
+	for(a=0;a<80;a++) {
+		anslcd_write_byte_data(anslcd_logo[a]);
+	}
+	return 0;
+}
+
+__initcall(anslcd_init);
+
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/macintosh/apm_emu.c linux/drivers/macintosh/apm_emu.c
--- linux.orig/drivers/macintosh/apm_emu.c	Thu Jan  1 00:00:00 1970
+++ linux/drivers/macintosh/apm_emu.c	Mon Jan  7 13:00:34 2002
@@ -0,0 +1,545 @@
+/* APM emulation layer for PowerMac
+ * 
+ * Copyright 2001 Benjamin Herrenschmidt (benh@kernel.crashing.org)
+ *
+ * Lots of code inherited from apm.c, see appropriate notice in
+ *  arch/i386/kernel/apm.c
+ *
+ * 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, 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.
+ *
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/poll.h>
+#include <linux/types.h>
+#include <linux/stddef.h>
+#include <linux/timer.h>
+#include <linux/fcntl.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/proc_fs.h>
+#include <linux/miscdevice.h>
+#include <linux/apm_bios.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/pm.h>
+#include <linux/kernel.h>
+#include <linux/smp_lock.h>
+
+#include <linux/adb.h>
+#include <linux/pmu.h>
+
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <asm/machdep.h>
+
+#undef DEBUG
+
+#ifdef DEBUG
+#define DBG(args...) printk(KERN_DEBUG args)
+//#define DBG(args...) xmon_printf(args)
+#else
+#define DBG(args...) do { } while (0)
+#endif
+
+/*
+ * The apm_bios device is one of the misc char devices.
+ * This is its minor number.
+ */
+#define	APM_MINOR_DEV	134
+
+/*
+ * Maximum number of events stored
+ */
+#define APM_MAX_EVENTS		20
+
+#define FAKE_APM_BIOS_VERSION	0x0101
+
+#define APM_USER_NOTIFY_TIMEOUT	(5*HZ)
+
+/*
+ * The per-file APM data
+ */
+struct apm_user {
+	int		magic;
+	struct apm_user *	next;
+	int		suser: 1;
+	int		suspend_waiting: 1;
+	int		suspends_pending;
+	int		suspends_read;
+	int		event_head;
+	int		event_tail;
+	apm_event_t	events[APM_MAX_EVENTS];
+};
+
+/*
+ * The magic number in apm_user
+ */
+#define APM_BIOS_MAGIC		0x4101
+
+/*
+ * Local variables
+ */
+static int			suspends_pending;
+
+static DECLARE_WAIT_QUEUE_HEAD(apm_waitqueue);
+static DECLARE_WAIT_QUEUE_HEAD(apm_suspend_waitqueue);
+static struct apm_user *	user_list;
+
+static int apm_notify_sleep(struct pmu_sleep_notifier *self, int when);
+static struct pmu_sleep_notifier apm_sleep_notifier = {
+	apm_notify_sleep,
+	SLEEP_LEVEL_USERLAND,
+};
+
+static char			driver_version[] = "0.5";	/* no spaces */
+
+#ifdef DEBUG
+static char *	apm_event_name[] = {
+	"system standby",
+	"system suspend",
+	"normal resume",
+	"critical resume",
+	"low battery",
+	"power status change",
+	"update time",
+	"critical suspend",
+	"user standby",
+	"user suspend",
+	"system standby resume",
+	"capabilities change"
+};
+#define NR_APM_EVENT_NAME	\
+		(sizeof(apm_event_name) / sizeof(apm_event_name[0]))
+
+#endif
+
+static int queue_empty(struct apm_user *as)
+{
+	return as->event_head == as->event_tail;
+}
+
+static apm_event_t get_queued_event(struct apm_user *as)
+{
+	as->event_tail = (as->event_tail + 1) % APM_MAX_EVENTS;
+	return as->events[as->event_tail];
+}
+
+static void queue_event(apm_event_t event, struct apm_user *sender)
+{
+	struct apm_user *	as;
+
+	DBG("apm_emu: queue_event(%s)\n", apm_event_name[event-1]);
+	if (user_list == NULL)
+		return;
+	for (as = user_list; as != NULL; as = as->next) {
+		if (as == sender)
+			continue;
+		as->event_head = (as->event_head + 1) % APM_MAX_EVENTS;
+		if (as->event_head == as->event_tail) {
+			static int notified;
+
+			if (notified++ == 0)
+			    printk(KERN_ERR "apm_emu: an event queue overflowed\n");
+			as->event_tail = (as->event_tail + 1) % APM_MAX_EVENTS;
+		}
+		as->events[as->event_head] = event;
+		if (!as->suser)
+			continue;
+		switch (event) {
+		case APM_SYS_SUSPEND:
+		case APM_USER_SUSPEND:
+			as->suspends_pending++;
+			suspends_pending++;
+			break;
+		case APM_NORMAL_RESUME:
+			as->suspend_waiting = 0;
+			break;
+		}
+	}
+	wake_up_interruptible(&apm_waitqueue);
+}
+
+static int check_apm_user(struct apm_user *as, const char *func)
+{
+	if ((as == NULL) || (as->magic != APM_BIOS_MAGIC)) {
+		printk(KERN_ERR "apm_emu: %s passed bad filp\n", func);
+		return 1;
+	}
+	return 0;
+}
+
+static ssize_t do_read(struct file *fp, char *buf, size_t count, loff_t *ppos)
+{
+	struct apm_user *	as;
+	int			i;
+	apm_event_t		event;
+	DECLARE_WAITQUEUE(wait, current);
+
+	as = fp->private_data;
+	if (check_apm_user(as, "read"))
+		return -EIO;
+	if (count < sizeof(apm_event_t))
+		return -EINVAL;
+	if (queue_empty(as)) {
+		if (fp->f_flags & O_NONBLOCK)
+			return -EAGAIN;
+		add_wait_queue(&apm_waitqueue, &wait);
+repeat:
+		set_current_state(TASK_INTERRUPTIBLE);
+		if (queue_empty(as) && !signal_pending(current)) {
+			schedule();
+			goto repeat;
+		}
+		set_current_state(TASK_RUNNING);
+		remove_wait_queue(&apm_waitqueue, &wait);
+	}
+	i = count;
+	while ((i >= sizeof(event)) && !queue_empty(as)) {
+		event = get_queued_event(as);
+		DBG("apm_emu: do_read, returning: %s\n", apm_event_name[event-1]);
+		if (copy_to_user(buf, &event, sizeof(event))) {
+			if (i < count)
+				break;
+			return -EFAULT;
+		}
+		switch (event) {
+		case APM_SYS_SUSPEND:
+		case APM_USER_SUSPEND:
+			as->suspends_read++;
+			break;
+		}
+		buf += sizeof(event);
+		i -= sizeof(event);
+	}
+	if (i < count)
+		return count - i;
+	if (signal_pending(current))
+		return -ERESTARTSYS;
+	return 0;
+}
+
+static unsigned int do_poll(struct file *fp, poll_table * wait)
+{
+	struct apm_user * as;
+
+	as = fp->private_data;
+	if (check_apm_user(as, "poll"))
+		return 0;
+	poll_wait(fp, &apm_waitqueue, wait);
+	if (!queue_empty(as))
+		return POLLIN | POLLRDNORM;
+	return 0;
+}
+
+static int do_ioctl(struct inode * inode, struct file *filp,
+		    u_int cmd, u_long arg)
+{
+	struct apm_user *	as;
+	DECLARE_WAITQUEUE(wait, current);
+
+	as = filp->private_data;
+	if (check_apm_user(as, "ioctl"))
+		return -EIO;
+	if (!as->suser)
+		return -EPERM;
+	switch (cmd) {
+	case APM_IOC_SUSPEND:
+		/* If a suspend message was sent to userland, we
+		 * consider this as a confirmation message
+		 */
+		if (as->suspends_read > 0) {
+			as->suspends_read--;
+			as->suspends_pending--;
+			suspends_pending--;
+		} else {
+			// Route to PMU suspend ?
+			break;
+		}
+		as->suspend_waiting = 1;
+		add_wait_queue(&apm_waitqueue, &wait);
+		DBG("apm_emu: ioctl waking up sleep waiter !\n");
+		wake_up(&apm_suspend_waitqueue);
+		mb();
+		while(as->suspend_waiting && !signal_pending(current)) {
+			set_current_state(TASK_INTERRUPTIBLE);
+			schedule();
+		}
+		set_current_state(TASK_RUNNING);
+		remove_wait_queue(&apm_waitqueue, &wait);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int do_release(struct inode * inode, struct file * filp)
+{
+	struct apm_user *	as;
+
+	as = filp->private_data;
+	if (check_apm_user(as, "release"))
+		return 0;
+	filp->private_data = NULL;
+	lock_kernel();
+	if (as->suspends_pending > 0) {
+		suspends_pending -= as->suspends_pending;
+		if (suspends_pending <= 0)
+			wake_up(&apm_suspend_waitqueue);
+	}
+	if (user_list == as)
+		user_list = as->next;
+	else {
+		struct apm_user *	as1;
+
+		for (as1 = user_list;
+		     (as1 != NULL) && (as1->next != as);
+		     as1 = as1->next)
+			;
+		if (as1 == NULL)
+			printk(KERN_ERR "apm: filp not in user list\n");
+		else
+			as1->next = as->next;
+	}
+	unlock_kernel();
+	kfree(as);
+	return 0;
+}
+
+static int do_open(struct inode * inode, struct file * filp)
+{
+	struct apm_user *	as;
+
+	as = (struct apm_user *)kmalloc(sizeof(*as), GFP_KERNEL);
+	if (as == NULL) {
+		printk(KERN_ERR "apm: cannot allocate struct of size %d bytes\n",
+		       sizeof(*as));
+		return -ENOMEM;
+	}
+	as->magic = APM_BIOS_MAGIC;
+	as->event_tail = as->event_head = 0;
+	as->suspends_pending = 0;
+	as->suspends_read = 0;
+	/*
+	 * XXX - this is a tiny bit broken, when we consider BSD
+         * process accounting. If the device is opened by root, we
+	 * instantly flag that we used superuser privs. Who knows,
+	 * we might close the device immediately without doing a
+	 * privileged operation -- cevans
+	 */
+	as->suser = capable(CAP_SYS_ADMIN);
+	as->next = user_list;
+	user_list = as;
+	filp->private_data = as;
+
+	DBG("apm_emu: opened by %s, suser: %d\n", current->comm, (int)as->suser);
+
+	return 0;
+}
+
+/* Wait for all clients to ack the suspend request. APM API
+ * doesn't provide a way to NAK, but this could be added
+ * here.
+ */
+static int wait_all_suspend(void)
+{
+	DECLARE_WAITQUEUE(wait, current);
+
+	add_wait_queue(&apm_suspend_waitqueue, &wait);
+	DBG("apm_emu: wait_all_suspend(), suspends_pending: %d\n", suspends_pending);
+	while(suspends_pending > 0) {
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule();
+	}
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(&apm_suspend_waitqueue, &wait);
+
+	DBG("apm_emu: wait_all_suspend() - complete !\n");
+	
+	return 1;
+}
+
+static int apm_notify_sleep(struct pmu_sleep_notifier *self, int when)
+{
+	switch(when) {
+		case PBOOK_SLEEP_REQUEST:
+			queue_event(APM_SYS_SUSPEND, NULL);
+			if (!wait_all_suspend())
+				return PBOOK_SLEEP_REFUSE;
+			break;
+		case PBOOK_SLEEP_REJECT:
+		case PBOOK_WAKE:
+			queue_event(APM_NORMAL_RESUME, NULL);
+			break;
+	}
+	return PBOOK_SLEEP_OK;
+}
+
+#define APM_CRITICAL		10
+#define APM_LOW			30
+
+static int apm_emu_get_info(char *buf, char **start, off_t fpos, int length)
+{
+	/* Arguments, with symbols from linux/apm_bios.h.  Information is
+	   from the Get Power Status (0x0a) call unless otherwise noted.
+
+	   0) Linux driver version (this will change if format changes)
+	   1) APM BIOS Version.  Usually 1.0, 1.1 or 1.2.
+	   2) APM flags from APM Installation Check (0x00):
+	      bit 0: APM_16_BIT_SUPPORT
+	      bit 1: APM_32_BIT_SUPPORT
+	      bit 2: APM_IDLE_SLOWS_CLOCK
+	      bit 3: APM_BIOS_DISABLED
+	      bit 4: APM_BIOS_DISENGAGED
+	   3) AC line status
+	      0x00: Off-line
+	      0x01: On-line
+	      0x02: On backup power (BIOS >= 1.1 only)
+	      0xff: Unknown
+	   4) Battery status
+	      0x00: High
+	      0x01: Low
+	      0x02: Critical
+	      0x03: Charging
+	      0x04: Selected battery not present (BIOS >= 1.2 only)
+	      0xff: Unknown
+	   5) Battery flag
+	      bit 0: High
+	      bit 1: Low
+	      bit 2: Critical
+	      bit 3: Charging
+	      bit 7: No system battery
+	      0xff: Unknown
+	   6) Remaining battery life (percentage of charge):
+	      0-100: valid
+	      -1: Unknown
+	   7) Remaining battery life (time units):
+	      Number of remaining minutes or seconds
+	      -1: Unknown
+	   8) min = minutes; sec = seconds */
+
+	unsigned short  ac_line_status = 0xff;
+	unsigned short  battery_status = 0xff;
+	unsigned short  battery_flag   = 0xff;
+	int		percentage     = -1;
+	int             time_units     = -1;
+	int		real_count     = 0;
+	int		i;
+	char *		p = buf;
+
+	ac_line_status = ((pmu_power_flags & PMU_PWR_AC_PRESENT) != 0);
+	for (i=0; i<pmu_battery_count; i++) {
+		if (percentage < 0)
+			percentage = 0;
+		if (time_units < 0)
+			time_units = 0;
+		if (pmu_batteries[i].flags & PMU_BATT_PRESENT) {
+			percentage += (pmu_batteries[i].charge * 100) /
+				pmu_batteries[i].max_charge;
+			/* hrm... should we provide the remaining charge
+			 * time when AC is plugged ? If yes, just remove
+			 * that test --BenH
+			 */
+			if (!ac_line_status)
+				time_units += pmu_batteries[i].time_remaining / 60;
+			real_count++;
+			if (pmu_batteries[i].flags & PMU_BATT_CHARGING)
+				battery_flag |= 0x08;
+		}
+	}
+	if (real_count) {
+		percentage /= real_count;
+		if (percentage <= APM_CRITICAL) {
+			battery_status = 0x02;
+			battery_flag = 0x04;
+		} else if (percentage <= APM_LOW) {
+			battery_status = 0x01;
+			battery_flag = 0x02;
+		} else {
+			battery_status = 0x00;
+			battery_flag = 0x01;
+		}
+		if (battery_flag & 0x08)
+			battery_status = 0x03;
+	}
+	p += sprintf(p, "%s %d.%d 0x%02x 0x%02x 0x%02x 0x%02x %d%% %d %s\n",
+		     driver_version,
+		     (FAKE_APM_BIOS_VERSION >> 8) & 0xff,
+		     FAKE_APM_BIOS_VERSION & 0xff,
+		     0,
+		     ac_line_status,
+		     battery_status,
+		     battery_flag,
+		     percentage,
+		     time_units,
+		     "min");
+
+	return p - buf;
+}
+
+static struct file_operations apm_bios_fops = {
+	owner:		THIS_MODULE,
+	read:		do_read,
+	poll:		do_poll,
+	ioctl:		do_ioctl,
+	open:		do_open,
+	release:	do_release,
+};
+
+static struct miscdevice apm_device = {
+	APM_MINOR_DEV,
+	"apm_bios",
+	&apm_bios_fops
+};
+
+static int __init apm_emu_init(void)
+{
+	struct proc_dir_entry *apm_proc;
+
+	if (sys_ctrler != SYS_CTRLER_PMU) {
+		printk(KERN_INFO "apm_emu: Requires a machine with a PMU.\n");
+		return -ENODEV;
+	}
+		
+	apm_proc = create_proc_info_entry("apm", 0, NULL, apm_emu_get_info);
+	if (apm_proc)
+		SET_MODULE_OWNER(apm_proc);
+
+	misc_register(&apm_device);
+
+	pmu_register_sleep_notifier(&apm_sleep_notifier);
+
+	printk(KERN_INFO "apm_emu: APM Emulation %s initialized.\n", driver_version);
+
+	return 0;
+}
+
+static void __exit apm_emu_exit(void)
+{
+	pmu_unregister_sleep_notifier(&apm_sleep_notifier);
+	misc_deregister(&apm_device);
+	remove_proc_entry("apm", NULL);
+
+	printk(KERN_INFO "apm_emu: APM Emulation removed.\n");
+}
+
+module_init(apm_emu_init);
+module_exit(apm_emu_exit);
+
+MODULE_AUTHOR("Benjamin Herrenschmidt");
+MODULE_DESCRIPTION("APM emulation layer for PowerMac");
+MODULE_LICENSE("GPL");
+EXPORT_NO_SYMBOLS;
+
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/macintosh/mac_hid.c linux/drivers/macintosh/mac_hid.c
--- linux.orig/drivers/macintosh/mac_hid.c	Mon Jan  7 23:41:09 2002
+++ linux/drivers/macintosh/mac_hid.c	Wed Dec 26 16:32:31 2001
@@ -200,15 +200,15 @@
 	0, 0, 0, KEY_KPCOMMA, 0, KEY_INTL3, 0, 0,		/* 0x00-0x07 */
 	0, 0, 0, 0, KEY_LANG1, KEY_LANG2, 0, 0,			/* 0x08-0x0f */
 	0, 0, 0, 0, 0, 0, 0, 0,					/* 0x10-0x17 */
-	0, 0, 0, 0, KEY_KPENTER, KEY_RIGHTCTRL, 0, 0,		/* 0x18-0x1f */
-	0, 0, 0, 0, 0, 0, 0, 0,					/* 0x20-0x27 */
+	0, 0, 0, 0, KEY_KPENTER, KEY_RIGHTCTRL, KEY_VOLUMEUP, 0,/* 0x18-0x1f */
+	0, 0, 0, 0, 0, KEY_VOLUMEDOWN, KEY_MUTE, 0,		/* 0x20-0x27 */
 	0, 0, 0, 0, 0, 0, 0, 0,					/* 0x28-0x2f */
 	0, 0, 0, 0, 0, KEY_KPSLASH, 0, KEY_SYSRQ,		/* 0x30-0x37 */
-	KEY_RIGHTALT, 0, 0, 0, 0, 0, 0, 0,			/* 0x38-0x3f */
+	KEY_RIGHTALT, 0, 0, KEY_EJECTCD, 0, 0, 0, 0,		/* 0x38-0x3f */
 	0, 0, 0, 0, 0, 0, 0, KEY_HOME,				/* 0x40-0x47 */
 	KEY_UP, KEY_PAGEUP, 0, KEY_LEFT, 0, KEY_RIGHT, 0, KEY_END, /* 0x48-0x4f */
 	KEY_DOWN, KEY_PAGEDOWN, KEY_INSERT, KEY_DELETE, 0, 0, 0, 0, /* 0x50-0x57 */
-	0, 0, 0, KEY_LEFTMETA, KEY_RIGHTMETA, KEY_COMPOSE, 0, 0, /* 0x58-0x5f */
+	0, 0, 0, KEY_LEFTMETA, KEY_RIGHTMETA, KEY_COMPOSE, KEY_POWER, 0, /* 0x58-0x5f */
 	0, 0, 0, 0, 0, 0, 0, 0,					/* 0x60-0x67 */
 	0, 0, 0, 0, 0, 0, 0, KEY_MACRO,				/* 0x68-0x6f */
 	0, 0, 0, 0, 0, 0, 0, 0,					/* 0x70-0x77 */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/macintosh/macserial.c linux/drivers/macintosh/macserial.c
--- linux.orig/drivers/macintosh/macserial.c	Mon Jan  7 23:41:09 2002
+++ linux/drivers/macintosh/macserial.c	Wed Dec 26 16:32:31 2001
@@ -40,7 +40,8 @@
 #include <asm/system.h>
 #include <asm/segment.h>
 #include <asm/bitops.h>
-#include <asm/feature.h>
+#include <asm/machdep.h>
+#include <asm/pmac_feature.h>
 #include <linux/adb.h>
 #include <linux/pmu.h>
 #ifdef CONFIG_KGDB
@@ -1179,7 +1180,7 @@
 	}
 
 	memset(info->curregs, 0, sizeof(info->curregs));
-	memset(info->curregs, 0, sizeof(info->pendregs));
+	memset(info->pendregs, 0, sizeof(info->pendregs));
 
 	info->flags &= ~ZILOG_INITIALIZED;
 }
@@ -1194,69 +1195,34 @@
 {
 	int delay = 0;
 
-	if (feature_test(info->dev_node, FEATURE_Serial_enable) < 0)
-		return 0;	/* don't have serial power control */
-
-	/* The timings looks strange but that's the ones MacOS seems
-	   to use for the internal modem. I think we can use a lot faster
-	   ones, at least whe not using the modem, this should be tested.
-	 */
 	if (state) {
 		PWRDBG("ttyS%02d: powering up hardware\n", info->line);
-		if (feature_test(info->dev_node, FEATURE_Serial_enable) == 0) {
-			feature_set(info->dev_node, FEATURE_Serial_enable);
-			mdelay(10);
-			feature_set(info->dev_node, FEATURE_Serial_reset);
-			mdelay(15);
-			feature_clear(info->dev_node, FEATURE_Serial_reset);
-			mdelay(10);
-		}
-		if (info->zs_chan_a == info->zs_channel)
-			feature_set(info->dev_node, FEATURE_Serial_IO_A);
-		else
-			feature_set(info->dev_node, FEATURE_Serial_IO_B);
-		delay = 10;
-		if (info->is_cobalt_modem) {
-			feature_set_modem_power(info->dev_node, 1);
+		pmac_call_feature(
+			PMAC_FTR_SCC_ENABLE,
+			info->dev_node, info->port_type, 1);
+		if (info->is_internal_modem) {
+			pmac_call_feature(
+				PMAC_FTR_MODEM_ENABLE,
+				info->dev_node, 0, 1);
 			delay = 2500;	/* wait for 2.5s before using */
-		}
-#ifdef CONFIG_PMAC_PBOOK
-		if (info->is_irda)
-			pmu_enable_irled(1);
-#endif /* CONFIG_PMAC_PBOOK */
+		} else if (info->is_irda)
+			mdelay(50);	/* Do better here once the problems
+			                 * with blocking have been ironed out
+			                 */
 	} else {
 		/* TODO: Make that depend on a timer, don't power down
 		 * immediately
 		 */
 		PWRDBG("ttyS%02d: shutting down hardware\n", info->line);
-		if (info->is_cobalt_modem) {
+		if (info->is_internal_modem) {
 			PWRDBG("ttyS%02d: shutting down modem\n", info->line);
-			feature_set_modem_power(info->dev_node, 0);
-		}
-#ifdef CONFIG_PMAC_PBOOK
-		if (info->is_irda)
-			pmu_enable_irled(0);
-#endif /* CONFIG_PMAC_PBOOK */
-
-		if (info->zs_chan_a == info->zs_channel && !info->is_irda) {
-			PWRDBG("ttyS%02d: shutting down SCC channel A\n", info->line);
-			feature_clear(info->dev_node, FEATURE_Serial_IO_A);
-		} else if (!info->is_irda) {
-			PWRDBG("ttyS%02d: shutting down SCC channel B\n", info->line);
-			feature_clear(info->dev_node, FEATURE_Serial_IO_B);
-		}
-		/* XXX for now, shut down SCC core only on powerbooks */
-		if (is_powerbook
-		    && !(feature_test(info->dev_node, FEATURE_Serial_IO_A) ||
-			 feature_test(info->dev_node, FEATURE_Serial_IO_B))) {
-			PWRDBG("ttyS%02d: shutting down SCC core\n", info->line);
-			feature_set(info->dev_node, FEATURE_Serial_reset);
-			mdelay(15);
-			feature_clear(info->dev_node, FEATURE_Serial_reset);
-			mdelay(25);
-			feature_clear(info->dev_node, FEATURE_Serial_enable);
-			mdelay(5);
-		}
+			pmac_call_feature(
+				PMAC_FTR_MODEM_ENABLE,
+				info->dev_node, 0, 0);
+		}
+		pmac_call_feature(
+			PMAC_FTR_SCC_ENABLE,
+			info->dev_node, info->port_type, 0);
 	}
 	return delay;
 }
@@ -2381,7 +2347,7 @@
  * Initialize one channel, both the mac_serial and mac_zschannel
  * structs.  We use the dev_node field of the mac_serial struct.
  */
-static void
+static int
 chan_init(struct mac_serial *zss, struct mac_zschannel *zs_chan,
 	  struct mac_zschannel *zs_chan_a)
 {
@@ -2411,12 +2377,15 @@
 
 	/* setup misc varariables */
 	zss->kgdb_channel = 0;
-	zss->is_cobalt_modem = device_is_compatible(ch, "cobalt");
 
-	/* XXX tested only with wallstreet PowerBook,
-	   should do no harm anyway */
+	/* For now, we assume you either have a slot-names property
+	 * with "Modem" in it, or your channel is compatible with
+	 * "cobalt". Might need additional fixups
+	 */
+	zss->is_internal_modem = device_is_compatible(ch, "cobalt");
 	conn = get_property(ch, "AAPL,connector", &len);
 	zss->is_irda = conn && (strcmp(conn, "infrared") == 0);
+	zss->port_type = PMAC_SCC_ASYNC;
 	/* 1999 Powerbook G3 has slot-names property instead */
 	slots = (struct slot_names_prop *)get_property(ch, "slot-names", &len);
 	if (slots && slots->count > 0) {
@@ -2425,8 +2394,29 @@
 		else if (strcmp(slots->name, "Modem") == 0)
 			zss->is_internal_modem = 1;
 	}
+	if (zss->is_irda)
+		zss->port_type = PMAC_SCC_IRDA;
+	if (zss->is_internal_modem) {
+		struct device_node* i2c_modem = find_devices("i2c-modem");
+		if (i2c_modem) {
+			char* mid = get_property(i2c_modem, "modem-id", NULL);
+			if (mid) switch(*mid) {
+			case 0x04 :
+			case 0x05 :
+			case 0x07 :
+			case 0x08 :
+			case 0x0b :
+			case 0x0c :
+				zss->port_type = PMAC_SCC_I2S1;
+			}
+			printk(KERN_INFO "macserial: i2c-modem detected, id: %d\n",
+				mid ? (*mid) : 0);
+		} else {
+			printk(KERN_INFO "macserial: serial modem detected\n");
+		}
+	}
 
-	if (zss->has_dma) {
+	while (zss->has_dma) {
 		zss->dma_priv = NULL;
 		/* it seems that the last two addresses are the
 		   DMA controllers */
@@ -2437,11 +2427,13 @@
 		zss->tx_dma_irq = ch->intrs[1].line;
 		zss->rx_dma_irq = ch->intrs[2].line;
 		spin_lock_init(&zss->rx_dma_lock);
+		break;
 	}
 
 	init_timer(&zss->powerup_timer);
 	zss->powerup_timer.function = powerup_done;
 	zss->powerup_timer.data = (unsigned long) zss;
+	return 0;
 }
 
 /* Ask the PROM how many Z8530s we have and initialize their zs_channels */
@@ -2496,13 +2488,15 @@
 			continue;
 
 		/* set up A side */
-		chan_init(&zs_soft[chip + chan_a_index], zs_chan, zs_chan);
+		if (chan_init(&zs_soft[chip + chan_a_index], zs_chan, zs_chan))
+			continue;
 		++zs_chan;
 
 		/* set up B side, if it exists */
 		if (nchan > 1)
-			chan_init(&zs_soft[chip + 1 - chan_a_index],
-				  zs_chan, zs_chan - 1);
+			if (chan_init(&zs_soft[chip + 1 - chan_a_index],
+				  zs_chan, zs_chan - 1))
+				continue;
 		++zs_chan;
 	}
 	*pp = 0;
@@ -2528,13 +2522,35 @@
 	if (zs_chain == 0)
 		probe_sccs();
 
-	/* XXX assume it's a powerbook if we have a via-pmu */
+	/* XXX assume it's a powerbook if we have a via-pmu
+	 * 
+	 * This is OK for core99 machines as well.
+	 */
 	is_powerbook = find_devices("via-pmu") != 0;
 
-	/* Register the interrupt handler for each one */
+	/* Register the interrupt handler for each one
+	 * We also request the OF resources here as probe_sccs()
+	 * might be called too early for that
+	 */
 	save_flags(flags); cli();
 	for (i = 0; i < zs_channels_found; ++i) {
+		struct device_node* ch = zs_soft[i].dev_node;
+		if (!request_OF_resource(ch, 0, NULL)) {
+			printk(KERN_ERR "macserial: can't request IO resource !\n");
+			return -ENODEV;
+		}
 		if (zs_soft[i].has_dma) {
+			if (!request_OF_resource(ch, ch->n_addrs - 2, " (tx dma)")) {
+				printk(KERN_ERR "macserial: can't request TX DMA resource !\n");
+				zs_soft[i].has_dma = 0;
+				goto no_dma;
+			}
+			if (!request_OF_resource(ch, ch->n_addrs - 1, " (rx dma)")) {
+				release_OF_resource(ch, ch->n_addrs - 2);
+				printk(KERN_ERR "macserial: can't request RX DMA resource !\n");
+				zs_soft[i].has_dma = 0;
+				goto no_dma;
+			}
 			if (request_irq(zs_soft[i].tx_dma_irq, rs_txdma_irq, 0,
 					"SCC-txdma", &zs_soft[i]))
 				printk(KERN_ERR "macserial: can't get irq %d\n",
@@ -2546,6 +2562,7 @@
 				       zs_soft[i].rx_dma_irq);
 			disable_irq(zs_soft[i].rx_dma_irq);
 		}
+no_dma:		
 		if (request_irq(zs_soft[i].irq, rs_interrupt, 0,
 				"SCC", &zs_soft[i]))
 			printk(KERN_ERR "macserial: can't get irq %d\n",
@@ -2676,9 +2693,7 @@
 		connector = get_property(info->dev_node, "AAPL,connector", &lenp);
 		if (connector)
 			printk(", port = %s", connector);
-		if (info->is_cobalt_modem)
-			printk(" (cobalt modem)");
-		else if (info->is_internal_modem)
+		if (info->is_internal_modem)
 			printk(" (internal modem)");
 		if (info->is_irda)
 			printk(" (IrDA)");
@@ -2711,6 +2726,12 @@
 		if (zs_soft[i].has_dma) {
 			free_irq(zs_soft[i].tx_dma_irq, &zs_soft[i]);
 			free_irq(zs_soft[i].rx_dma_irq, &zs_soft[i]);
+		}
+		release_OF_resource(zs_soft[i].dev_node, 0);
+		if (zs_soft[i].has_dma) {
+			struct device_node* ch = zs_soft[i].dev_node;
+			release_OF_resource(ch, ch->n_addrs - 2);
+			release_OF_resource(ch, ch->n_addrs - 1);
 		}
 	}
 	restore_flags(flags);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/macintosh/macserial.h linux/drivers/macintosh/macserial.h
--- linux.orig/drivers/macintosh/macserial.h	Mon Jan  7 23:41:09 2002
+++ linux/drivers/macintosh/macserial.h	Wed Dec 26 16:32:31 2001
@@ -111,8 +111,8 @@
 	char kgdb_channel;  /* Kgdb is running on this channel */
 	char is_cons;       /* Is this our console. */
 	char is_internal_modem; /* is connected to an internal modem */
-	char is_cobalt_modem;	/* is a gatwick-based cobalt modem */
 	char is_irda;		/* is connected to an IrDA codec */
+	int port_type;		/* Port type for pmac_feature */
 	unsigned char tx_active; /* character is being xmitted */
 	unsigned char tx_stopped; /* output is suspended */
 	unsigned char power_wait; /* waiting for power-up delay to expire */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/macintosh/mediabay.c linux/drivers/macintosh/mediabay.c
--- linux.orig/drivers/macintosh/mediabay.c	Mon Jan  7 23:41:09 2002
+++ linux/drivers/macintosh/mediabay.c	Wed Dec 26 16:32:31 2001
@@ -25,9 +25,13 @@
 #include <asm/prom.h>
 #include <asm/pgtable.h>
 #include <asm/io.h>
-#include <asm/feature.h>
+#include <asm/machdep.h>
+#include <asm/pmac_feature.h>
 #include <asm/mediabay.h>
 #include <asm/sections.h>
+#include <asm/ohare.h>
+#include <asm/heathrow.h>
+#include <asm/keylargo.h>
 #include <linux/adb.h>
 #include <linux/pmu.h>
 
@@ -40,7 +44,7 @@
 #endif
 
 #undef MB_USE_INTERRUPTS
-#undef MB_DEBUG
+#define MB_DEBUG
 #define MB_IGNORE_SIGNALS
 
 #ifdef MB_DEBUG
@@ -49,24 +53,46 @@
 #define MBDBG(fmt, arg...)	do { } while (0)
 #endif
 
-struct media_bay_hw {
-	unsigned char	b0;
-	unsigned char	contents;
-	unsigned char	b2;
-	unsigned char	b3;
+/* Type of media bay */
+enum {
+	mb_ohare,
+	mb_heathrow,
+	mb_keylargo
+};
+
+#define MB_FCR32(bay, r)	((bay)->base + ((r) >> 2))
+#define MB_FCR8(bay, r)		(((volatile u8*)((bay)->base)) + (r))
+
+#define MB_IN32(bay,r)		(in_le32(MB_FCR32(bay,r)))
+#define MB_OUT32(bay,r,v)	(out_le32(MB_FCR32(bay,r), (v)))
+#define MB_BIS(bay,r,v)		(MB_OUT32((bay), (r), MB_IN32((bay), r) | (v)))
+#define MB_BIC(bay,r,v)		(MB_OUT32((bay), (r), MB_IN32((bay), r) & ~(v)))
+#define MB_IN8(bay,r)		(in_8(MB_FCR8(bay,r)))
+#define MB_OUT8(bay,r,v)	(out_8(MB_FCR8(bay,r), (v)))
+
+struct media_bay_info;
+
+struct mb_ops {
+	char*	name;
+	u8	(*content)(struct media_bay_info* bay);
+	void	(*power)(struct media_bay_info* bay, int on_off);
+	int	(*setup_bus)(struct media_bay_info* bay, u8 device_id);
+	void	(*un_reset)(struct media_bay_info* bay);
+	void	(*un_reset_ide)(struct media_bay_info* bay);
 };
 
 struct media_bay_info {
-	volatile struct media_bay_hw*	addr;
-	volatile u8*			extint_gpio;
+	volatile u32*			base;
 	int				content_id;
 	int				state;
 	int				last_value;
 	int				value_count;
 	int				timer;
 	struct device_node*		dev_node;
-	int				pismo;	/* New PowerBook3,1 */
-	int				gpio_cache;
+	int				mb_type;
+	struct mb_ops*			ops;
+	int				index;
+	int				cached_gpio;
 #ifdef CONFIG_BLK_DEV_IDE
 	unsigned long			cd_base;
 	int 				cd_index;
@@ -77,33 +103,12 @@
 
 #define MAX_BAYS	2
 
-static volatile struct media_bay_info media_bays[MAX_BAYS];
+static struct media_bay_info media_bays[MAX_BAYS];
 int media_bay_count = 0;
 
-inline int mb_content(volatile struct media_bay_info *bay)
-{
-        if (bay->pismo) {
-                unsigned char new_gpio = in_8(bay->extint_gpio + 0xe) & 2;
-                if (new_gpio) {
-                	bay->gpio_cache = new_gpio;
-                        return MB_NO;
-                } else if (bay->gpio_cache != new_gpio) {
-                        /* make sure content bits are set */
-                        feature_set(bay->dev_node, FEATURE_Mediabay_content);
-                        udelay(5);
-                        bay->gpio_cache = new_gpio;
-                }
-                return (in_le32((unsigned*)bay->addr) >> 4) & 0xf;
-        } else {
-                int cont = (in_8(&bay->addr->contents) >> 4) & 7;
-                return (cont == 7) ? MB_NO : cont;
-        }
-}
-
 #ifdef CONFIG_BLK_DEV_IDE
 /* check the busy bit in the media-bay ide interface
    (assumes the media-bay contains an ide device) */
-//#define MB_IDE_READY(i)	((inb(media_bays[i].cd_base + 0x70) & 0xc0) == 0x40)
 #define MB_IDE_READY(i)	((inb(media_bays[i].cd_base + 0x70) & 0x80) == 0)
 #endif
 
@@ -116,7 +121,7 @@
  * Consider the media-bay ID value stable if it is the same for
  * this number of milliseconds
  */
-#define MB_STABLE_DELAY	40
+#define MB_STABLE_DELAY	100
 
 /* Wait after powering up the media bay this delay in ms
  * timeout bumped for some powerbooks
@@ -166,175 +171,259 @@
 	mb_powering_down	/* Powering down (avoid too fast down/up) */
 };
 
-static void poll_media_bay(int which);
-static void set_media_bay(int which, int id);
-static void set_mb_power(int which, int onoff);
-static void media_bay_step(int i);
-static int media_bay_task(void *);
+#define MB_POWER_SOUND		0x08
+#define MB_POWER_FLOPPY		0x04
+#define MB_POWER_ATA		0x02
+#define MB_POWER_PCI		0x01
+#define MB_POWER_OFF		0x00
 
-#ifdef MB_USE_INTERRUPTS
-static void media_bay_intr(int irq, void *devid, struct pt_regs *regs);
-#endif
+/*
+ * Functions for polling content of media bay
+ */
+ 
+static u8 __pmac
+ohare_mb_content(struct media_bay_info *bay)
+{
+	return (MB_IN32(bay, OHARE_MBCR) >> 12) & 7;
+}
+
+static u8 __pmac
+heathrow_mb_content(struct media_bay_info *bay)
+{
+	return (MB_IN32(bay, HEATHROW_MBCR) >> 12) & 7;
+}
+
+static u8 __pmac
+keylargo_mb_content(struct media_bay_info *bay)
+{
+	int new_gpio;
+
+	new_gpio = MB_IN8(bay, KL_GPIO_MEDIABAY_IRQ) & KEYLARGO_GPIO_INPUT_DATA;
+	if (new_gpio) {
+		bay->cached_gpio = new_gpio;
+		return MB_NO;
+	} else if (bay->cached_gpio != new_gpio) {
+		MB_BIS(bay, KEYLARGO_MBCR, KL_MBCR_MB0_ENABLE);
+		(void)MB_IN32(bay, KEYLARGO_MBCR);
+		udelay(5);
+		MB_BIC(bay, KEYLARGO_MBCR, 0x0000000F);
+		(void)MB_IN32(bay, KEYLARGO_MBCR);
+		udelay(5);
+		bay->cached_gpio = new_gpio;
+	}
+	return (MB_IN32(bay, KEYLARGO_MBCR) >> 4) & 7;
+}
 
 /*
- * 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.
+ * Functions for powering up/down the bay, puts the bay device
+ * into reset state as well
  */
 
-void __pmac
-media_bay_init(void)
+static void __pmac
+ohare_mb_power(struct media_bay_info* bay, int on_off)
 {
-	struct device_node *np;
-	int		n,i;
-	
-	for (i=0; i<MAX_BAYS; i++) {
-		memset((char *)&media_bays[i], 0, sizeof(struct media_bay_info));
-		media_bays[i].content_id	= -1;
-#ifdef CONFIG_BLK_DEV_IDE
-		media_bays[i].cd_index		= -1;
-#endif
+	if (on_off) {
+		/* Power up device, assert it's reset line */
+		MB_BIC(bay, OHARE_FCR, OH_BAY_RESET_N);
+		MB_BIC(bay, OHARE_FCR, OH_BAY_POWER_N);
+	} else {
+		/* Disable all devices */
+		MB_BIC(bay, OHARE_FCR, OH_BAY_DEV_MASK);
+		MB_BIC(bay, OHARE_FCR, OH_FLOPPY_ENABLE);
+		/* Cut power from bay, release reset line */
+		MB_BIS(bay, OHARE_FCR, OH_BAY_POWER_N);
+		MB_BIS(bay, OHARE_FCR, OH_BAY_RESET_N);
+		MB_BIS(bay, OHARE_FCR, OH_IDE1_RESET_N);
 	}
-	
-	np = find_devices("media-bay");
-	n = 0;
-	while(np && (n<MAX_BAYS)) {
-		if (np->n_addrs == 0)
-			continue;
-		media_bays[n].addr = (volatile struct media_bay_hw *)
-			ioremap(np->addrs[0].address, sizeof(struct media_bay_hw));
+	MB_BIC(bay, OHARE_MBCR, 0x00000F00);
+}
 
-		media_bays[n].pismo = device_is_compatible(np, "keylargo-media-bay");
-		if (media_bays[n].pismo) {
-			if (!np->parent || strcmp(np->parent->name, "mac-io")) {
-				printk(KERN_ERR "Pismo media-bay has no mac-io parent !\n");
-				continue;
-			}
-			media_bays[n].extint_gpio = ioremap(np->parent->addrs[0].address
-				+ 0x58, 0x10);
-		}
+static void __pmac
+heathrow_mb_power(struct media_bay_info* bay, int on_off)
+{
+	if (on_off) {
+		/* Power up device, assert it's reset line */
+		MB_BIC(bay, HEATHROW_FCR, HRW_BAY_RESET_N);
+		MB_BIC(bay, HEATHROW_FCR, HRW_BAY_POWER_N);
+	} else {
+		/* Disable all devices */
+		MB_BIC(bay, HEATHROW_FCR, HRW_BAY_DEV_MASK);
+		MB_BIC(bay, HEATHROW_FCR, HRW_SWIM_ENABLE);
+		/* Cut power from bay, release reset line */
+		MB_BIS(bay, HEATHROW_FCR, HRW_BAY_POWER_N);
+		MB_BIS(bay, HEATHROW_FCR, HRW_BAY_RESET_N);
+		MB_BIS(bay, HEATHROW_FCR, HRW_IDE1_RESET_N);
+	}
+	MB_BIC(bay, HEATHROW_MBCR, 0x00000F00);
+}
 
-#ifdef MB_USE_INTERRUPTS
-		if (np->n_intrs == 0) {
-			printk(KERN_ERR "media bay %d has no irq\n",n);
-			continue;
-		}
+static void __pmac
+keylargo_mb_power(struct media_bay_info* bay, int on_off)
+{
+	if (on_off) {
+		/* Power up device, assert it's reset line */
+            	MB_BIC(bay, KEYLARGO_MBCR, KL_MBCR_MB0_DEV_RESET);
+            	MB_BIC(bay, KEYLARGO_MBCR, KL_MBCR_MB0_DEV_POWER);
+	} else {
+		/* Disable all devices */
+		MB_BIC(bay, KEYLARGO_MBCR, KL_MBCR_MB0_DEV_MASK);
+		MB_BIC(bay, KEYLARGO_FCR1, KL1_EIDE0_ENABLE);
+		/* Cut power from bay, release reset line */
+		MB_BIS(bay, KEYLARGO_MBCR, KL_MBCR_MB0_DEV_POWER);
+		MB_BIS(bay, KEYLARGO_MBCR, KL_MBCR_MB0_DEV_RESET);
+		MB_BIS(bay, KEYLARGO_FCR1, KL1_EIDE0_RESET_N);
+	}
+	MB_BIC(bay, KEYLARGO_MBCR, 0x0000000F);
+}
 
-		if (request_irq(np->intrs[0].line, media_bay_intr, 0, "Media bay", (void *)n)) {
-			printk(KERN_ERR "Couldn't get IRQ %d for media bay %d\n",
-				np->intrs[0].line, n);
-			continue;
-		}
-#endif	
-		media_bay_count++;
-	
-		media_bays[n].dev_node		= np;
+/*
+ * Functions for configuring the media bay for a given type of device,
+ * enable the related busses
+ */
 
-		/* Force an immediate detect */
-		set_mb_power(n,0);
-		mdelay(MB_POWER_DELAY);
-		if(!media_bays[n].pismo)
-			out_8(&media_bays[n].addr->contents, 0x70);
-		mdelay(MB_STABLE_DELAY);
-		media_bays[n].content_id = MB_NO;
-		media_bays[n].last_value = mb_content(&media_bays[n]);
-		media_bays[n].value_count = MS_TO_HZ(MB_STABLE_DELAY);
-		media_bays[n].state = mb_empty;
-		do {
-			mdelay(1000/HZ);
-			media_bay_step(n);
-		} while((media_bays[n].state != mb_empty) &&
-			(media_bays[n].state != mb_up));
+static int __pmac
+ohare_mb_setup_bus(struct media_bay_info* bay, u8 device_id)
+{
+	switch(device_id) {
+		case MB_FD:
+		case MB_FD1:
+			MB_BIS(bay, OHARE_FCR, OH_BAY_FLOPPY_ENABLE);
+			MB_BIS(bay, OHARE_FCR, OH_FLOPPY_ENABLE);
+			return 0;
+		case MB_CD:
+			MB_BIC(bay, OHARE_FCR, OH_IDE1_RESET_N);
+			MB_BIS(bay, OHARE_FCR, OH_BAY_IDE_ENABLE);
+			return 0;
+		case MB_PCI:
+			MB_BIS(bay, OHARE_FCR, OH_BAY_PCI_ENABLE);
+			return 0;
+	}
+	return -ENODEV;
+}
 
-		n++;
-		np=np->next;
+static int __pmac
+heathrow_mb_setup_bus(struct media_bay_info* bay, u8 device_id)
+{
+	switch(device_id) {
+		case MB_FD:
+		case MB_FD1:
+			MB_BIS(bay, HEATHROW_FCR, HRW_BAY_FLOPPY_ENABLE);
+			MB_BIS(bay, HEATHROW_FCR, HRW_SWIM_ENABLE);
+			return 0;
+		case MB_CD:
+			MB_BIC(bay, HEATHROW_FCR, HRW_IDE1_RESET_N);
+			MB_BIS(bay, HEATHROW_FCR, HRW_BAY_IDE_ENABLE);
+			return 0;
+		case MB_PCI:
+			MB_BIS(bay, HEATHROW_FCR, HRW_BAY_PCI_ENABLE);
+			return 0;
 	}
+	return -ENODEV;
+}
 
-	if (media_bay_count)
-	{
-		printk(KERN_INFO "Registered %d media-bay(s)\n", media_bay_count);
+static int __pmac
+keylargo_mb_setup_bus(struct media_bay_info* bay, u8 device_id)
+{
+	switch(device_id) {
+		case MB_CD:
+			MB_BIS(bay, KEYLARGO_MBCR, KL_MBCR_MB0_IDE_ENABLE);
+			MB_BIC(bay, KEYLARGO_FCR1, KL1_EIDE0_RESET_N);
+			MB_BIS(bay, KEYLARGO_FCR1, KL1_EIDE0_ENABLE);
+			return 0;
+		case MB_PCI:
+			MB_BIS(bay, KEYLARGO_MBCR, KL_MBCR_MB0_PCI_ENABLE);
+			return 0;
+		case MB_SOUND:
+			MB_BIS(bay, KEYLARGO_MBCR, KL_MBCR_MB0_SOUND_ENABLE);
+			return 0;
+	}
+	return -ENODEV;
+}
 
-#ifdef CONFIG_PMAC_PBOOK
-		pmu_register_sleep_notifier(&mb_sleep_notifier);
-#endif /* CONFIG_PMAC_PBOOK */
+/*
+ * Functions for tweaking resets
+ */
 
-		kernel_thread(media_bay_task, NULL,
-			      CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
-	}
+static void __pmac
+ohare_mb_un_reset(struct media_bay_info* bay)
+{
+	MB_BIS(bay, OHARE_FCR, OH_BAY_RESET_N);
 }
 
-#ifdef MB_USE_INTERRUPTS
 static void __pmac
-media_bay_intr(int irq, void *devid, struct pt_regs *regs)
+heathrow_mb_un_reset(struct media_bay_info* bay)
 {
+	MB_BIS(bay, HEATHROW_FCR, HRW_BAY_RESET_N);
+}
+
+static void __pmac
+keylargo_mb_un_reset(struct media_bay_info* bay)
+{
+	MB_BIS(bay, KEYLARGO_MBCR, KL_MBCR_MB0_DEV_RESET);
+}
+
+static void __pmac
+ohare_mb_un_reset_ide(struct media_bay_info* bay)
+{
+	MB_BIS(bay, OHARE_FCR, OH_IDE1_RESET_N);
+}
+
+static void __pmac
+heathrow_mb_un_reset_ide(struct media_bay_info* bay)
+{
+	MB_BIS(bay, HEATHROW_FCR, HRW_IDE1_RESET_N);
 }
-#endif
 
 static void __pmac
-set_mb_power(int which, int onoff)
+keylargo_mb_un_reset_ide(struct media_bay_info* bay)
 {
-	volatile struct media_bay_info*	mb = &media_bays[which];
+	MB_BIS(bay, KEYLARGO_FCR1, KL1_EIDE0_RESET_N);
+}
 
+static inline void __pmac
+set_mb_power(struct media_bay_info* bay, int onoff)
+{
+	/* Power up up and assert the bay reset line */
 	if (onoff) {
-		feature_set(mb->dev_node, FEATURE_Mediabay_power);
-		udelay(10);
-		feature_set(mb->dev_node, FEATURE_Mediabay_reset);
-		udelay(10);
-		mb->state = mb_powering_up;
-		MBDBG("mediabay%d: powering up\n", which);
-	} else {
-		feature_clear(mb->dev_node, FEATURE_Mediabay_floppy_enable);
-		if (mb->pismo)
-			feature_clear(mb->dev_node, FEATURE_IDE0_enable);
-		else
-			feature_clear(mb->dev_node, FEATURE_IDE1_enable);
-		feature_clear(mb->dev_node, FEATURE_Mediabay_IDE_switch);
-		feature_clear(mb->dev_node, FEATURE_Mediabay_PCI_enable);
-		feature_clear(mb->dev_node, FEATURE_SWIM3_enable);
-		feature_clear(mb->dev_node, FEATURE_Mediabay_power);
-		mb->state = mb_powering_down;
-		MBDBG("mediabay%d: powering down\n", which);
+		bay->ops->power(bay, 1);
+		bay->state = mb_powering_up;
+		MBDBG("mediabay%d: powering up\n", bay->index);
+	} else { 
+		/* Make sure everything is powered down & disabled */
+		bay->ops->power(bay, 0);
+		bay->state = mb_powering_down;
+		MBDBG("mediabay%d: powering down\n", bay->index);
 	}
-	mb->timer = MS_TO_HZ(MB_POWER_DELAY);
+	bay->timer = MS_TO_HZ(MB_POWER_DELAY);
 }
 
 static void __pmac
-set_media_bay(int which, int id)
+poll_media_bay(struct media_bay_info* bay)
 {
-	volatile struct media_bay_info* bay;
+	int id = bay->ops->content(bay);
 
-	bay = &media_bays[which];
-	
-	switch (id) {
-	case MB_CD:
-		if (bay->pismo) {
-			feature_set(bay->dev_node, FEATURE_Mediabay_IDE_switch);
-			udelay(10);
-			feature_set(bay->dev_node, FEATURE_IDE0_enable);
-			udelay(10);
-			feature_set(bay->dev_node, FEATURE_IDE0_reset);
-		} else {
-			feature_set(bay->dev_node, FEATURE_IDE1_enable);
-			udelay(10);
-			feature_set(bay->dev_node, FEATURE_IDE1_reset);
+	if (id == bay->last_value) {
+	    if (id != bay->content_id
+	        && ++bay->value_count >= MS_TO_HZ(MB_STABLE_DELAY)) {
+	        /* If the device type changes without going thru "MB_NO", we force
+	           a pass by "MB_NO" to make sure things are properly reset */
+	        if ((id != MB_NO) && (bay->content_id != MB_NO)) {
+	            id = MB_NO;
+		    MBDBG("mediabay%d: forcing MB_NO\n", bay->index);
 		}
-		printk(KERN_INFO "media bay %d contains a CD-ROM drive\n", which);
-		break;
-	case MB_FD:
-	case MB_FD1:
-		feature_set(bay->dev_node, FEATURE_Mediabay_floppy_enable);
-		feature_set(bay->dev_node, FEATURE_SWIM3_enable);
-		printk(KERN_INFO "media bay %d contains a floppy disk drive\n", which);
-		break;
-	case MB_NO:
-		break;
-	default:
-		printk(KERN_INFO "media bay %d contains an unknown device (%d)\n",
-		       which, id);
-		break;
+		MBDBG("mediabay%d: switching to %d\n", bay->index, id);
+		set_mb_power(bay, id != MB_NO);
+		bay->content_id = id;
+		if (id == MB_NO) {
+#ifdef CONFIG_BLK_DEV_IDE
+		    bay->cd_retry = 0;
+#endif
+		    printk(KERN_INFO "media bay %d is empty\n", bay->index);
+		}
+ 	    }
+	} else {
+		bay->last_value = id;
+		bay->value_count = 0;
 	}
 }
 
@@ -412,11 +501,11 @@
 static void __pmac
 media_bay_step(int i)
 {
-	volatile struct media_bay_info* bay = &media_bays[i];
+	struct media_bay_info* bay = &media_bays[i];
 
 	/* We don't poll when powering down */
 	if (bay->state != mb_powering_down)
-	    poll_media_bay(i);
+	    poll_media_bay(bay);
 
 	/* If timer expired or polling IDE busy, run state machine */
 	if ((bay->state != mb_ide_waiting) && (bay->timer != 0) && ((--bay->timer) != 0))
@@ -424,13 +513,17 @@
 
 	switch(bay->state) {
 	case mb_powering_up:
-	    	set_media_bay(i, bay->last_value);
+	    	if (bay->ops->setup_bus(bay, bay->last_value) < 0) {
+			MBDBG("mediabay%d: device not supported (kind:%d)\n", i, bay->content_id);
+	    		set_mb_power(bay, 0);
+	    		break;
+	    	}
 	    	bay->timer = MS_TO_HZ(MB_RESET_DELAY);
 	    	bay->state = mb_enabling_bay;
 		MBDBG("mediabay%d: enabling (kind:%d)\n", i, bay->content_id);
 		break;
 	case mb_enabling_bay:
-	    	feature_clear(bay->dev_node, FEATURE_Mediabay_reset);
+		bay->ops->un_reset(bay);
 	    	bay->timer = MS_TO_HZ(MB_SETUP_DELAY);
 	    	bay->state = mb_resetting;
 		MBDBG("mediabay%d: waiting reset (kind:%d)\n", i, bay->content_id);
@@ -444,16 +537,13 @@
 	    	}
 #ifdef CONFIG_BLK_DEV_IDE
 		MBDBG("mediabay%d: waiting IDE reset (kind:%d)\n", i, bay->content_id);
-		if (bay->pismo)
-	    		feature_clear(bay->dev_node, FEATURE_IDE0_reset);
-		else
-	    		feature_clear(bay->dev_node, FEATURE_IDE1_reset);
+		bay->ops->un_reset_ide(bay);
 	    	bay->timer = MS_TO_HZ(MB_IDE_WAIT);
 	    	bay->state = mb_ide_resetting;
 #else
 		printk(KERN_DEBUG "media-bay %d is ide (not compiled in kernel)\n", i);
-		set_mb_power(i, 0);
-#endif // #ifdef CONFIG_BLK_DEV_IDE
+		set_mb_power(bay, 0);
+#endif /* CONFIG_BLK_DEV_IDE */
 	    	break;
 	    
 #ifdef CONFIG_BLK_DEV_IDE
@@ -481,7 +571,7 @@
 				/* We eventually do a retry */
 				bay->cd_retry++;
 				printk("IDE register error\n");
-				set_mb_power(i, 0);
+				set_mb_power(bay, 0);
 			} else {
 				printk(KERN_DEBUG "media-bay %d is ide %d\n", i, bay->cd_index);
 				MBDBG("mediabay %d IDE ready\n", i);
@@ -491,10 +581,10 @@
 	    	if (bay->timer == 0) {
 			printk("\nIDE Timeout in bay %d !\n", i);
 			MBDBG("mediabay%d: nIDE Timeout !\n", i);
-			set_mb_power(i, 0);
+			set_mb_power(bay, 0);
 	    	}
 		break;
-#endif // #ifdef CONFIG_BLK_DEV_IDE
+#endif /* CONFIG_BLK_DEV_IDE */
 
 	case mb_powering_down:
 	    	bay->state = mb_empty;
@@ -514,7 +604,7 @@
 				bay->content_id = MB_NO;
 			}
 	    	}
-#endif	    
+#endif /* CONFIG_BLK_DEV_IDE */    
 		MBDBG("mediabay%d: end of power down\n", i);
 	    	break;
 	}
@@ -526,7 +616,7 @@
  * with the IDE driver.  It needs to be a thread because
  * ide_register can't be called from interrupt context.
  */
-int __pmac
+static int __pmac
 media_bay_task(void *x)
 {
 	int	i;
@@ -547,37 +637,12 @@
 	}
 }
 
-void __pmac
-poll_media_bay(int which)
+#ifdef MB_USE_INTERRUPTS
+static void __pmac
+media_bay_intr(int irq, void *devid, struct pt_regs *regs)
 {
-	volatile struct media_bay_info* bay = &media_bays[which];
-	int id = mb_content(bay);
-
-	if (id == bay->last_value) {
-	    if (id != bay->content_id
-	        && ++bay->value_count >= MS_TO_HZ(MB_STABLE_DELAY)) {
-	        /* If the device type changes without going thru "MB_NO", we force
-	           a pass by "MB_NO" to make sure things are properly reset */
-	        if ((id != MB_NO) && (bay->content_id != MB_NO)) {
-	            id = MB_NO;
-		    MBDBG("mediabay%d: forcing MB_NO\n", which);
-		}
-		MBDBG("mediabay%d: switching to %d\n", which, id);
-		set_mb_power(which, id != MB_NO);
-		bay->content_id = id;
-		if (id == MB_NO) {
-#ifdef CONFIG_BLK_DEV_IDE
-		    bay->cd_retry = 0;
-#endif
-		    printk(KERN_INFO "media bay %d is empty\n", which);
-		}
- 	    }
-	} else {
-		bay->last_value = id;
-		bay->value_count = 0;
-	}
 }
-
+#endif
 
 #ifdef CONFIG_PMAC_PBOOK
 /*
@@ -586,7 +651,7 @@
 int __pmac
 mb_notify_sleep(struct pmu_sleep_notifier *self, int when)
 {
-	volatile struct media_bay_info* bay;
+	struct media_bay_info* bay;
 	int i;
 	
 	switch (when) {
@@ -597,7 +662,7 @@
 	case PBOOK_SLEEP_NOW:
 		for (i=0; i<media_bay_count; i++) {
 			bay = &media_bays[i];
-			set_mb_power(i, 0);
+			set_mb_power(bay, 0);
 			mdelay(10);
 		}
 		break;
@@ -609,14 +674,11 @@
 			   they seem to help the 3400 get it right.
 			 */
 			/* Force MB power to 0 */
-			set_mb_power(i, 0);
+			set_mb_power(bay, 0);
 			mdelay(MB_POWER_DELAY);
-			if (!bay->pismo)
-				out_8(&bay->addr->contents, 0x70);
-			mdelay(MB_STABLE_DELAY);
-			if (mb_content(bay) != bay->content_id)
+			if (bay->ops->content(bay) != bay->content_id)
 				continue;
-			set_mb_power(i, 1);
+			set_mb_power(bay, 1);
 			bay->last_value = bay->content_id;
 			bay->value_count = MS_TO_HZ(MB_STABLE_DELAY);
 			bay->timer = MS_TO_HZ(MB_POWER_DELAY);
@@ -634,4 +696,135 @@
 	return PBOOK_SLEEP_OK;
 }
 #endif /* CONFIG_PMAC_PBOOK */
+
+
+/* Definitions of "ops" structures.
+ */
+static struct mb_ops ohare_mb_ops __pmacdata = {
+	name:		"Ohare",
+	content:	ohare_mb_content,
+	power:		ohare_mb_power,
+	setup_bus:	ohare_mb_setup_bus,
+	un_reset:	ohare_mb_un_reset,
+	un_reset_ide:	ohare_mb_un_reset_ide,
+};
+
+static struct mb_ops heathrow_mb_ops __pmacdata = {
+	name:		"Heathrow",
+	content:	heathrow_mb_content,
+	power:		heathrow_mb_power,
+	setup_bus:	heathrow_mb_setup_bus,
+	un_reset:	heathrow_mb_un_reset,
+	un_reset_ide:	heathrow_mb_un_reset_ide,
+};
+
+static struct mb_ops keylargo_mb_ops __pmacdata = {
+	name:		"KeyLargo",
+	content:	keylargo_mb_content,
+	power:		keylargo_mb_power,
+	setup_bus:	keylargo_mb_setup_bus,
+	un_reset:	keylargo_mb_un_reset,
+	un_reset_ide:	keylargo_mb_un_reset_ide,
+};
+
+/*
+ * 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 __pmac
+media_bay_init(void)
+{
+	struct device_node *np;
+	int		n,i;
+	
+	for (i=0; i<MAX_BAYS; i++) {
+		memset((char *)&media_bays[i], 0, sizeof(struct media_bay_info));
+		media_bays[i].content_id	= -1;
+#ifdef CONFIG_BLK_DEV_IDE
+		media_bays[i].cd_index		= -1;
+#endif
+	}
+	
+	np = find_devices("media-bay");
+	n = 0;
+	while(np && (n<MAX_BAYS)) {
+		struct media_bay_info* bay = &media_bays[n];
+		if (!np->parent || np->n_addrs == 0 || !request_OF_resource(np, 0, NULL)) {
+			np = np->next;
+			printk(KERN_ERR "media-bay: Can't request IO resource !\n");
+			continue;
+		}
+		bay->mb_type = mb_ohare;
+
+		if (device_is_compatible(np, "keylargo-media-bay")) {
+			bay->mb_type = mb_keylargo;
+			bay->ops = &keylargo_mb_ops;
+		} else if (device_is_compatible(np, "heathrow-media-bay")) {
+			bay->mb_type = mb_heathrow;
+			bay->ops = &heathrow_mb_ops;
+		} else if (device_is_compatible(np, "ohare-media-bay")) {
+			bay->mb_type = mb_ohare;
+			bay->ops = &ohare_mb_ops;
+		} else {
+			printk(KERN_ERR "mediabay: Unknown bay type !\n");
+			np = np->next;
+			continue;
+		}
+		bay->base = (volatile u32*)ioremap(np->parent->addrs[0].address, 0x1000);
+
+		/* Enable probe logic on keylargo */
+		if (bay->mb_type == mb_keylargo)
+			MB_BIS(bay, KEYLARGO_MBCR, KL_MBCR_MB0_ENABLE);
+#ifdef MB_USE_INTERRUPTS
+		if (np->n_intrs == 0) {
+			printk(KERN_ERR "media bay %d has no irq\n",n);
+			np = np->next;
+			continue;
+		}
+
+		if (request_irq(np->intrs[0].line, media_bay_intr, 0, "Media bay", (void *)n)) {
+			printk(KERN_ERR "Couldn't get IRQ %d for media bay %d\n",
+				np->intrs[0].line, n);
+			np = np->next;
+			continue;
+		}
+#endif	
+		media_bay_count++;
+	
+		printk(KERN_INFO "mediabay%d: Registered %s media-bay\n", n, bay->ops->name);
+		bay->dev_node = np;
+		bay->index = n;
+
+		/* Force an immediate detect */
+		set_mb_power(bay, 0);
+		mdelay(MB_POWER_DELAY);
+		bay->content_id = MB_NO;
+		bay->last_value = bay->ops->content(bay);
+		bay->value_count = MS_TO_HZ(MB_STABLE_DELAY);
+		bay->state = mb_empty;
+		do {
+			mdelay(1000/HZ);
+			media_bay_step(n);
+		} while((bay->state != mb_empty) &&
+			(bay->state != mb_up));
+
+		n++;
+		np=np->next;
+	}
+
+	if (media_bay_count)
+	{
+#ifdef CONFIG_PMAC_PBOOK
+		pmu_register_sleep_notifier(&mb_sleep_notifier);
+#endif /* CONFIG_PMAC_PBOOK */
+
+		kernel_thread(media_bay_task, NULL,
+			      CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
+	}
+}
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/macintosh/rtc.c linux/drivers/macintosh/rtc.c
--- linux.orig/drivers/macintosh/rtc.c	Mon Jan  7 23:41:09 2002
+++ linux/drivers/macintosh/rtc.c	Wed Dec 26 16:32:31 2001
@@ -34,14 +34,13 @@
 void get_rtc_time(struct rtc_time *t)
 {
 	unsigned long nowtime;
-    
+
 	nowtime = (ppc_md.get_rtc_time)();
 
 	to_tm(nowtime, t);
 
 	t->tm_year -= 1900;
-	t->tm_mon -= 1;
-	t->tm_wday -= 1;
+	t->tm_mon -= 1; /* Make sure userland has a 0-based month */
 }
 
 /* Set the current date and time in the real time clock. */
@@ -49,7 +48,8 @@
 {
 	unsigned long nowtime;
 
-	nowtime = mktime(t->tm_year+1900, t->tm_mon+1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);
+	nowtime = mktime(t->tm_year+1900, t->tm_mon+1, t->tm_mday,
+			t->tm_hour, t->tm_min, t->tm_sec);
 
 	(ppc_md.set_rtc_time)(nowtime);
 }
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/macintosh/via-cuda.c linux/drivers/macintosh/via-cuda.c
--- linux.orig/drivers/macintosh/via-cuda.c	Mon Jan  7 23:41:09 2002
+++ linux/drivers/macintosh/via-cuda.c	Wed Dec 26 16:32:31 2001
@@ -191,6 +191,8 @@
     if (via == NULL)
 	return -ENODEV;
 
+    request_OF_resource(vias, 0, NULL);
+
     if (request_irq(CUDA_IRQ, cuda_interrupt, 0, "ADB", cuda_interrupt)) {
 	printk(KERN_ERR "cuda_init: can't get irq %d\n", CUDA_IRQ);
 	return -EAGAIN;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/macintosh/via-pmu.c linux/drivers/macintosh/via-pmu.c
--- linux.orig/drivers/macintosh/via-pmu.c	Mon Jan  7 23:41:09 2002
+++ linux/drivers/macintosh/via-pmu.c	Wed Dec 26 16:32:31 2001
@@ -9,9 +9,9 @@
  * and the RTC (real time clock) chip.
  *
  * Copyright (C) 1998 Paul Mackerras and Fabio Riccardi.
+ * Copyright (C) 2001 Benjamin Herrenschmidt
  * 
- * todo: - Check this driver for smp safety (new Core99 motherboards).
- *       - Cleanup synchro between VIA interrupt and GPIO-based PMU
+ * todo: - Cleanup synchro between VIA interrupt and GPIO-based PMU
  *         interrupt.
  *
  *
@@ -45,10 +45,12 @@
 #include <asm/sections.h>
 #include <asm/irq.h>
 #include <asm/hardirq.h>
-#include <asm/feature.h>
+#include <asm/pmac_feature.h>
 #include <asm/uaccess.h>
 #include <asm/mmu_context.h>
+#include <asm/sections.h>
 #include <asm/cputable.h>
+#include <asm/time.h>
 #ifdef CONFIG_PMAC_BACKLIGHT
 #include <asm/backlight.h>
 #endif
@@ -56,9 +58,13 @@
 /* Some compile options */
 #undef SUSPEND_USES_PMU
 #define DEBUG_SLEEP
+#undef HACKED_PCI_SAVE
 
 /* Misc minor number allocated for /dev/pmu */
-#define PMU_MINOR	154
+#define PMU_MINOR		154
+
+/* How many iterations between battery polls */
+#define BATTERY_POLLING_COUNT	2
 
 static volatile unsigned char *via;
 
@@ -108,7 +114,7 @@
 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 interrupt_data[256]; /* Made bigger: I've been told that might happen */
 static unsigned char *reply_ptr;
 static int data_index;
 static int data_len;
@@ -126,9 +132,26 @@
 static int pmu_version;
 static int drop_interrupts;
 #ifdef CONFIG_PMAC_PBOOK
+static int option_lid_wakeup = 1;
 static int sleep_in_progress;
+static int can_sleep;
+#endif /* CONFIG_PMAC_PBOOK */
+
+static struct proc_dir_entry *proc_pmu_root;
+static struct proc_dir_entry *proc_pmu_info;
+static struct proc_dir_entry *proc_pmu_options;
+
+#ifdef CONFIG_PMAC_PBOOK
+int pmu_battery_count;
+int pmu_cur_battery;
+unsigned int pmu_power_flags;
+struct pmu_battery_info pmu_batteries[PMU_MAX_BATTERIES];
+static int query_batt_timer = BATTERY_POLLING_COUNT;
+static struct adb_request batt_req;
+static struct proc_dir_entry *proc_pmu_batt[PMU_MAX_BATTERIES];
 #endif /* CONFIG_PMAC_PBOOK */
 
+int __fake_sleep;
 int asleep;
 struct notifier_block *sleep_notifier_list;
 
@@ -153,15 +176,22 @@
 static void pmu_done(struct adb_request *req);
 static void pmu_handle_data(unsigned char *data, int len,
 			    struct pt_regs *regs);
-static void set_volume(int level);
 static void gpio1_interrupt(int irq, void *arg, struct pt_regs *regs);
+static int proc_get_info(char *page, char **start, off_t off,
+			  int count, int *eof, void *data);
 #ifdef CONFIG_PMAC_BACKLIGHT
 static int pmu_set_backlight_level(int level, void* data);
 static int pmu_set_backlight_enable(int on, int level, void* data);
 #endif /* CONFIG_PMAC_BACKLIGHT */
 #ifdef CONFIG_PMAC_PBOOK
 static void pmu_pass_intr(unsigned char *data, int len);
-#endif
+static int proc_get_batt(char *page, char **start, off_t off,
+			int count, int *eof, void *data);
+#endif /* CONFIG_PMAC_PBOOK */
+static int proc_read_options(char *page, char **start, off_t off,
+			int count, int *eof, void *data);
+static int proc_write_options(struct file *file, const char *buffer,
+			unsigned long count, void *data);
 
 #ifdef CONFIG_ADB
 struct adb_driver via_pmu_driver = {
@@ -309,6 +339,10 @@
 	} else
 		pmu_kind = PMU_UNKNOWN;
 
+#ifdef CONFIG_PMAC_PBOOK
+	if (pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,-1) >= 0)
+		can_sleep = 1;
+#endif /* CONFIG_PMAC_PBOOK */
 	via = (volatile unsigned char *) ioremap(vias->addrs->address, 0x2000);
 
 	out_8(&via[IER], IER_CLR | 0x7f);	/* disable all intrs */
@@ -362,9 +396,14 @@
 	if (vias == NULL)
 		return -ENODEV;
 
+	request_OF_resource(vias, 0, NULL);
+
 	bright_req_1.complete = 1;
 	bright_req_2.complete = 1;
 	bright_req_3.complete = 1;
+#ifdef CONFIG_PMAC_PBOOK
+	batt_req.complete = 1;
+#endif
 
 	if (request_irq(vias->intrs[0].line, via_pmu_interrupt, 0, "VIA-PMU",
 			(void *)0)) {
@@ -388,6 +427,46 @@
 	register_backlight_controller(&pmu_backlight_controller, NULL, "pmu");
 #endif /* CONFIG_PMAC_BACKLIGHT */
 
+#ifdef CONFIG_PMAC_PBOOK
+  	if (machine_is_compatible("AAPL,3400/2400") ||
+  		machine_is_compatible("AAPL,3500"))
+		pmu_battery_count = 1;
+	else if (machine_is_compatible("AAPL,PowerBook1998") ||
+		machine_is_compatible("PowerBook1,1"))
+		pmu_battery_count = 2;
+	else {
+		struct device_node* prim = find_devices("power-mgt");
+		u32 *prim_info = NULL;
+		if (prim)
+			prim_info = (u32 *)get_property(prim, "prim-info", NULL);
+		if (prim_info) {
+			/* Other stuffs here yet unknown */
+			pmu_battery_count = (prim_info[6] >> 16) & 0xff;
+		}
+	}
+#endif /# CONFIG_PMAC_PBOOK */
+	/* Create /proc/pmu */
+	proc_pmu_root = proc_mkdir("pmu", 0);
+	if (proc_pmu_root) {
+		int i;
+		proc_pmu_info = create_proc_read_entry("info", 0, proc_pmu_root,
+					proc_get_info, NULL);
+#ifdef CONFIG_PMAC_PBOOK
+		for (i=0; i<pmu_battery_count; i++) {
+			char title[16];
+			sprintf(title, "battery_%d", i);
+			proc_pmu_batt[i] = create_proc_read_entry(title, 0, proc_pmu_root,
+						proc_get_batt, (void *)i);
+		}
+#endif /* CONFIG_PMAC_PBOOK */
+		proc_pmu_options = create_proc_entry("options", 0600, proc_pmu_root);
+		if (proc_pmu_options) {
+			proc_pmu_options->nlink = 1;
+			proc_pmu_options->read_proc = proc_read_options;
+			proc_pmu_options->write_proc = proc_write_options;
+		}
+	}
+
 	/* Make sure PMU settle down before continuing. This is _very_ important
 	 * since the IDE probe may shut interrupts down for quite a bit of time. If
 	 * a PMU communication is pending while this happens, the PMU may timeout
@@ -448,8 +527,8 @@
 	pmu_request(&req, NULL, 1, PMU_GET_VERSION);
 	while (!req.complete)
 		pmu_poll();
-	if (req.reply_len > 1)
-		pmu_version = req.reply[1];
+	if (req.reply_len > 0)
+		pmu_version = req.reply[0];
 
 	return 1;
 }
@@ -460,6 +539,263 @@
 	return pmu_kind;
 }
 
+#ifdef CONFIG_PMAC_PBOOK
+
+/* 
+ * WARNING ! This code probably needs some debugging... -- BenH.
+ */
+static void __pmac
+done_battery_state_ohare(struct adb_request* req)
+{
+	unsigned int bat_flags = 0;
+	int current = 0;
+	unsigned int capa, max, voltage, time;
+	int lrange[] = { 0, 275, 850, 1680, 2325, 
+				2765, 3160, 3500, 3830, 4115, 
+				4360, 4585, 4795, 4990, 5170, 
+				5340, 5510, 5710, 5930, 6150, 
+				6370, 6500
+				};
+	
+	if (req->reply[0] & 0x01)
+		pmu_power_flags |= PMU_PWR_AC_PRESENT;
+	else
+		pmu_power_flags &= ~PMU_PWR_AC_PRESENT;
+
+	if (req->reply[0] & 0x04) {
+		int vb, i, j, charge, pcharge;
+		bat_flags |= PMU_BATT_PRESENT;
+		vb = (req->reply[1] << 8) | req->reply[2];
+		voltage = ((vb * 2650) + 726650)/100;
+		current = *((signed char *)&req->reply[5]);
+		if ((req->reply[0] & 0x01) == 0 && (current > 200))
+			vb += (current - 200) * 15;
+		else if (req->reply[0] & 0x02)
+			vb = (vb - 10) * 100;
+	  	i = (33000 - vb) / 10;
+	  	j = i - (i % 100);
+	  	if (j <= 0)
+	       		charge = 0;
+	  	else if (j >= 21)
+	       		charge = 650000;
+	  	else
+			charge = (lrange[j + 1] - lrange[j]) * (i - j) + (lrange[j] * 100);
+		charge = (1000 - charge / 650) / 10;
+		if (req->reply[0] & 0x40) {
+			pcharge = (req->reply[6] << 8) + req->reply[7];
+			if (pcharge > 6500)
+				pcharge = 6500;
+			pcharge *= 100;
+			pcharge = (1000 - pcharge / 650) / 10;
+			if (pcharge < charge)
+				charge = pcharge;
+		}
+		capa = charge;
+		max = 100;
+		time = (charge * 274) / current;
+		current = -current;
+		
+	} else
+		capa = max = current = voltage = time = 0;
+
+	if ((req->reply[0] & 0x02) && (current > 0))
+		bat_flags |= PMU_BATT_CHARGING;
+	if (req->reply[0] & 0x04) /* CHECK THIS ONE */
+		bat_flags |= PMU_BATT_PRESENT;
+
+	pmu_batteries[pmu_cur_battery].flags = bat_flags;
+	pmu_batteries[pmu_cur_battery].charge = capa;
+	pmu_batteries[pmu_cur_battery].max_charge = max;
+	pmu_batteries[pmu_cur_battery].current = current;
+	pmu_batteries[pmu_cur_battery].voltage = voltage;
+	pmu_batteries[pmu_cur_battery].time_remaining = time;
+}
+
+static void __pmac
+done_battery_state_smart(struct adb_request* req)
+{
+	/* format:
+	 *  [0] : format of this structure (known: 3,4,5)
+	 *  [1] : flags
+	 *  
+	 *  format 3 & 4:
+	 *  
+	 *  [2] : charge
+	 *  [3] : max charge
+	 *  [4] : current
+	 *  [5] : voltage
+	 *  
+	 *  format 5:
+	 *  
+	 *  [2][3] : charge
+	 *  [4][5] : max charge
+	 *  [6][7] : current
+	 *  [8][9] : voltage
+	 */
+	 
+	unsigned int bat_flags = 0;
+	int current;
+	unsigned int capa, max, voltage;
+	
+	if (req->reply[1] & 0x01)
+		pmu_power_flags |= PMU_PWR_AC_PRESENT;
+	else
+		pmu_power_flags &= ~PMU_PWR_AC_PRESENT;
+
+
+	if (req->reply[1] & 0x04) {
+		bat_flags |= PMU_BATT_PRESENT;
+		switch(req->reply[0]) {
+			case 3:
+			case 4: capa = req->reply[2];
+				max = req->reply[3];
+				current = *((signed char *)&req->reply[4]);
+				voltage = req->reply[5];
+				break;
+			case 5: capa = (req->reply[2] << 8) | req->reply[3];
+				max = (req->reply[4] << 8) | req->reply[5];
+				current = *((signed short *)&req->reply[6]);
+				voltage = (req->reply[8] << 8) | req->reply[9];
+				break;
+			default:
+				printk(KERN_WARNING "pmu.c : unrecognized battery info, len: %d, %02x %02x %02x %02x\n",
+					req->reply_len, req->reply[0], req->reply[1], req->reply[2], req->reply[3]);
+				break;
+		}
+	} else
+		capa = max = current = voltage = 0;
+
+	if ((req->reply[1] & 0x01) && (current > 0))
+		bat_flags |= PMU_BATT_CHARGING;
+
+	pmu_batteries[pmu_cur_battery].flags = bat_flags;
+	pmu_batteries[pmu_cur_battery].charge = capa;
+	pmu_batteries[pmu_cur_battery].max_charge = max;
+	pmu_batteries[pmu_cur_battery].current = current;
+	pmu_batteries[pmu_cur_battery].voltage = voltage;
+	if (current) {
+		if ((req->reply[1] & 0x01) && (current > 0))
+			pmu_batteries[pmu_cur_battery].time_remaining
+				= ((max-capa) * 3600) / current;
+		else
+			pmu_batteries[pmu_cur_battery].time_remaining
+				= (capa * 3600) / (-current);
+	} else
+		pmu_batteries[pmu_cur_battery].time_remaining = 0;
+
+	pmu_cur_battery = (pmu_cur_battery + 1) % pmu_battery_count;
+}
+
+static void __pmac
+query_battery_state(void)
+{
+	if (!batt_req.complete)
+		return;
+	if (pmu_kind == PMU_OHARE_BASED)
+		pmu_request(&batt_req, done_battery_state_ohare,
+			1, PMU_BATTERY_STATE);
+	else
+		pmu_request(&batt_req, done_battery_state_smart,
+			2, PMU_SMART_BATTERY_STATE, pmu_cur_battery+1);
+}
+
+#endif /* CONFIG_PMAC_PBOOK */
+
+static int
+proc_get_info(char *page, char **start, off_t off,
+		int count, int *eof, void *data)
+{
+	char* p = page;
+
+	p += sprintf(p, "PMU driver version     : %d\n", PMU_DRIVER_VERSION);
+	p += sprintf(p, "PMU firmware version   : %02x\n", pmu_version);
+#ifdef CONFIG_PMAC_PBOOK
+	p += sprintf(p, "AC Power               : %d\n",
+		((pmu_power_flags & PMU_PWR_AC_PRESENT) != 0));
+	p += sprintf(p, "Battery count          : %d\n", pmu_battery_count);
+#endif /* CONFIG_PMAC_PBOOK */
+
+	return p - page;
+}
+
+#ifdef CONFIG_PMAC_PBOOK
+static int
+proc_get_batt(char *page, char **start, off_t off,
+		int count, int *eof, void *data)
+{
+	int batnum = (int)data;
+	char *p = page;
+	
+	p += sprintf(p, "\n");
+	p += sprintf(p, "flags      : %08x\n",
+		pmu_batteries[batnum].flags);
+	p += sprintf(p, "charge     : %d\n",
+		pmu_batteries[batnum].charge);
+	p += sprintf(p, "max_charge : %d\n",
+		pmu_batteries[batnum].max_charge);
+	p += sprintf(p, "current    : %d\n",
+		pmu_batteries[batnum].current);
+	p += sprintf(p, "voltage    : %d\n",
+		pmu_batteries[batnum].voltage);
+	p += sprintf(p, "time rem.  : %d\n",
+		pmu_batteries[batnum].time_remaining);
+
+	return p - page;
+}
+#endif /* CONFIG_PMAC_PBOOK */
+
+static int
+proc_read_options(char *page, char **start, off_t off,
+			int count, int *eof, void *data)
+{
+	char *p = page;
+
+#ifdef CONFIG_PMAC_PBOOK
+	if (pmu_kind == PMU_KEYLARGO_BASED && can_sleep)
+		p += sprintf(p, "lid_wakeup=%d\n", option_lid_wakeup);
+#endif /* CONFIG_PMAC_PBOOK */
+
+	return p - page;
+}
+			
+static int
+proc_write_options(struct file *file, const char *buffer,
+			unsigned long count, void *data)
+{
+	char tmp[33];
+	char *label, *val;
+	unsigned long fcount = count;
+	
+	if (!count)
+		return -EINVAL;
+	if (count > 32)
+		count = 32;
+	if (copy_from_user(tmp, buffer, count))
+		return -EFAULT;
+	tmp[count] = 0;
+
+	label = tmp;
+	while(*label == ' ')
+		label++;
+	val = label;
+	while(*val && (*val != '=')) {
+		if (*val == ' ')
+			*val = 0;
+		val++;
+	}
+	if ((*val) == 0)
+		return -EINVAL;
+	*(val++) = 0;
+	while(*val == ' ')
+		val++;
+#ifdef CONFIG_PMAC_PBOOK
+	if (pmu_kind == PMU_KEYLARGO_BASED && can_sleep)
+		if (!strcmp(label, "lid_wakeup"))
+			option_lid_wakeup = ((*val) == '1');
+#endif /* CONFIG_PMAC_PBOOK */
+	return fcount;
+}
+
 #ifdef CONFIG_ADB
 /* Send an ADB command */
 static int __openfirmware
@@ -622,11 +958,7 @@
 	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_len = 0;
 	req->reply_expected = 0;
 	return pmu_queue_request(req);
 }
@@ -835,10 +1167,14 @@
 	spin_lock_irqsave(&pmu_lock, flags);
 	++disable_poll;
 		
-	while ((intr = in_8(&via[IFR])) != 0) {
+	for (;;) {
+		intr = in_8(&via[IFR]) & (SR_INT | CB1_INT);
+		if (intr == 0)
+			break;
 		if (++nloop > 1000) {
 			printk(KERN_DEBUG "PMU: stuck in intr loop, "
-			       "intr=%x pmu_state=%d\n", intr, pmu_state);
+			       "intr=%x, ier=%x pmu_state=%d\n",
+			       intr, in_8(&via[IER]), pmu_state);
 			break;
 		}
 		out_8(&via[IFR], intr);
@@ -847,15 +1183,6 @@
 		else if (intr & CB1_INT)
 			adb_int_pending = 1;
 	}
-	/* This is not necessary except if synchronous ADB requests are done
-	 * with interrupts off, which should not happen. Since I'm not sure
-	 * this "wiring" will remain, I'm commenting it out for now. Please do
-	 * not remove. -- BenH.
-	 */
-#if 0
-	if (gpio_reg && !pmu_suspended && (in_8(gpio_reg + 0x9) & 0x02) == 0)
-		adb_int_pending = 1;
-#endif
 
 	if (pmu_state == idle) {
 		if (adb_int_pending) {
@@ -866,9 +1193,8 @@
 			wait_for_ack();
 			send_byte(PMU_INT_ACK);
 			adb_int_pending = 0;
-		} else if (current_req) {
+		} else if (current_req)
 			pmu_start();
-		}
 	}
 	
 	--disable_poll;
@@ -878,8 +1204,10 @@
 static void __openfirmware
 gpio1_interrupt(int irq, void *arg, struct pt_regs *regs)
 {
-	adb_int_pending = 1;
-	via_pmu_interrupt(0, 0, 0);
+	if ((in_8(gpio_reg + 0x9) & 0x02) == 0) {
+		adb_int_pending = 1;
+		via_pmu_interrupt(0, 0, 0);
+	}
 }
 
 static void __openfirmware
@@ -1040,16 +1368,25 @@
 				adb_input(data+1, len-1, regs, 1);
 #endif /* CONFIG_ADB */		
 		}
-	} else if (data[0] == 0x08 && len == 3) {
-		/* sound/brightness buttons pressed */
+	} else {
+		/* Sound/brightness button pressed */
+		if ((data[0] & PMU_INT_SNDBRT) && len == 3) {
 #ifdef CONFIG_PMAC_BACKLIGHT
-		set_backlight_level(data[1] >> 4);
+			set_backlight_level(data[1] >> 4);
 #endif
-		set_volume(data[2]);
-	} else {
+		}
 #ifdef CONFIG_PMAC_PBOOK
-		pmu_pass_intr(data, len);
-#endif
+		/* Environement or tick interrupt, query batteries */
+		if (pmu_battery_count && (data[0] & PMU_INT_TICK)) {
+			if ((--query_batt_timer) == 0) {
+				query_battery_state();
+				query_batt_timer = BATTERY_POLLING_COUNT;
+			}
+		} else if (pmu_battery_count && (data[0] & PMU_INT_ENVIRONMENT))
+			query_battery_state();
+ 		if (data[0])
+			pmu_pass_intr(data, len);
+#endif /* CONFIG_PMAC_PBOOK */
 	}
 }
 
@@ -1116,11 +1453,6 @@
 		pmu_poll();
 }
 
-static void __openfirmware
-set_volume(int level)
-{
-}
-
 void __openfirmware
 pmu_restart(void)
 {
@@ -1266,10 +1598,14 @@
  * PCI devices which may get powered off when we sleep.
  */
 static struct pci_save {
+#ifndef HACKED_PCI_SAVE
 	u16	command;
 	u16	cache_lat;
 	u16	intr;
 	u32	rom_address;
+#else
+	u32	config[16];
+#endif	
 } *pbook_pci_saves;
 static int n_pbook_pci_saves;
 
@@ -1293,14 +1629,24 @@
 		return;
 
 	pci_for_each_dev(pd) {
+#ifndef HACKED_PCI_SAVE
 		pci_read_config_word(pd, PCI_COMMAND, &ps->command);
 		pci_read_config_word(pd, PCI_CACHE_LINE_SIZE, &ps->cache_lat);
 		pci_read_config_word(pd, PCI_INTERRUPT_LINE, &ps->intr);
 		pci_read_config_dword(pd, PCI_ROM_ADDRESS, &ps->rom_address);
+#else
+		int i;
+		for (i=1;i<16;i++)
+			pci_read_config_dword(pd, i<<4, &ps->config[i]);
+#endif
 		++ps;
 	}
 }
 
+/* For this to work, we must take care of a few things: If gmac was enabled
+ * during boot, it will be in the pci dev list. If it's disabled at this point
+ * (and it will probably be), then you can't access it's config space.
+ */
 static void __openfirmware
 pbook_pci_restore(void)
 {
@@ -1310,7 +1656,13 @@
 	int j;
 
 	pci_for_each_dev(pd) {
+#ifdef HACKED_PCI_SAVE
+		int i;
 		ps++;
+		for (i=2;i<16;i++)
+			pci_write_config_dword(pd, i<<4, ps->config[i]);
+		pci_write_config_dword(pd, 4, ps->config[1]);
+#else
 		if (ps->command == 0)
 			continue;
 		pci_read_config_word(pd, PCI_COMMAND, &cmd);
@@ -1330,8 +1682,8 @@
 				ps->intr);
 			pci_write_config_word(pd, PCI_COMMAND, ps->command);
 			break;
-			/* other header types not restored at present */
 		}
+#endif	
 	}
 }
 
@@ -1370,7 +1722,7 @@
 	}
 	mdelay(50);
 }
-#endif /* DEBUG_SLEEP */
+#endif
 
 /*
  * Put the powerbook to sleep.
@@ -1403,6 +1755,15 @@
 	out_8(&via[IER], IER_SET | SR_INT | CB1_INT);
 }
 
+static inline void wakeup_decrementer(void)
+{
+	set_dec(tb_ticks_per_jiffy);
+	/* No currently-supported powerbook has a 601,
+	 * so use get_tbl, not native
+	 */
+	last_jiffy_stamp(0) = tb_last_stamp = get_tbl();
+}
+
 #define	GRACKLE_PM	(1<<7)
 #define GRACKLE_DOZE	(1<<5)
 #define	GRACKLE_NAP	(1<<4)
@@ -1436,6 +1797,10 @@
 	 * vmalloc's are done before actual sleep of block drivers */
 	fsync_dev(0);
 
+	/* Give the disks a little time to actually finish writing */
+	for (wait = jiffies + (HZ/2); time_before(jiffies, wait); )
+		mb();
+
 	/* Sleep can fail now. May not be very robust but useful for debugging */
 	ret = broadcast_sleep(PBOOK_SLEEP_NOW, PBOOK_WAKE);
 	if (ret != PBOOK_SLEEP_OK) {
@@ -1443,13 +1808,9 @@
 		return -EBUSY;
 	}
 
-	/* Give the disks a little time to actually finish writing */
-	for (wait = jiffies + (HZ/2); time_before(jiffies, wait); )
-		mb();
-
 	/* Wait for completion of async backlight requests */
 	while (!bright_req_1.complete || !bright_req_2.complete ||
-		!bright_req_3.complete)
+		!bright_req_3.complete || !batt_req.complete)
 		pmu_poll();
 	
 	/* Turn off various things. Darwin does some retry tests here... */
@@ -1495,7 +1856,7 @@
 	/* The VIA is supposed not to be restored correctly*/
 	save_via_state();
 	/* We shut down some HW */
-	feature_prepare_for_sleep();
+	pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,1);
 
 	pci_read_config_word(grackle, 0x70, &pmcr1);
 	/* Apparently, MacOS uses NAP mode for Grackle ??? */
@@ -1512,7 +1873,7 @@
 	pci_write_config_word(grackle, 0x70, pmcr1);
 
 	/* Make sure the PMU is idle */
-	feature_wake_up();
+	pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,0);
 	restore_via_state();
 	
 	/* Restore L2 cache */
@@ -1522,11 +1883,6 @@
 	/* Restore userland MMU context */
 	set_context(current->active_mm->context, current->active_mm->pgd);
 
-	/* Re-enable DEC interrupts and kick DEC */
-	asm volatile("mtdec %0" : : "r" (0x7fffffff));
-	sti();
-	asm volatile("mtdec %0" : : "r" (0x10000000));
-
 	/* Power things up */
 	pmu_request(&req, NULL, 2, PMU_SET_INTR_MASK, 0xfc);
 	while (!req.complete)
@@ -1558,6 +1914,10 @@
 	/* Leave some time for HW to settle down */
 	mdelay(100);
 
+	/* Restart jiffies & scheduling */
+	wakeup_decrementer();
+	sti();
+
 	/* Notify drivers */
 	broadcast_wake();
 
@@ -1571,7 +1931,7 @@
 	struct adb_request req;
 	int ret, timeout;
 	
-	if (!feature_can_sleep()) {
+	if (!can_sleep) {
 		printk(KERN_ERR "Sleep mode not supported on this machine\n");
 		return -ENOSYS;
 	}
@@ -1591,20 +1951,19 @@
 	 * vmalloc's are done before actual sleep of block drivers */
 	fsync_dev(0);
 
+	/* Give the disks a little time to actually finish writing */
+	for (wait = jiffies + HZ; time_before(jiffies, wait); )
+		mb();
+
 	/* Sleep can fail now. May not be very robust but useful for debugging */
 	ret = broadcast_sleep(PBOOK_SLEEP_NOW, PBOOK_WAKE);
 	if (ret != PBOOK_SLEEP_OK) {
 		printk("pmu: sleep failed\n");
 		return -EBUSY;
 	}
-
-	/* Give the disks a little time to actually finish writing */
-	for (wait = jiffies + HZ; time_before(jiffies, wait); )
-		mb();
-
 	/* Wait for completion of async backlight requests */
 	while (!bright_req_1.complete || !bright_req_2.complete ||
-		!bright_req_3.complete)
+		!bright_req_3.complete || !batt_req.complete)
 		pmu_poll();
 
 	/* Tell PMU what events will wake us up */
@@ -1614,7 +1973,8 @@
 		pmu_poll();
 
 	pmu_request(&req, NULL, 4, PMU_POWER_EVENTS, PMU_PWR_SET_WAKEUP_EVENTS,
-		0, PMU_PWR_WAKEUP_KEY | PMU_PWR_WAKEUP_LID_OPEN);
+		0, PMU_PWR_WAKEUP_KEY |
+		(option_lid_wakeup ? PMU_PWR_WAKEUP_LID_OPEN : 0));
 	while (!req.complete)
 		pmu_poll();
 		
@@ -1651,10 +2011,12 @@
 	/* Save the state of PCI config space for some slots */
 	//pbook_pci_save();
 
-	/* Ask the PMU to put us to sleep */
-	pmu_request(&req, NULL, 5, PMU_SLEEP, 'M', 'A', 'T', 'T');
-	while (!req.complete && pmu_state != idle)
-		pmu_poll();
+	if (!__fake_sleep) {
+		/* Ask the PMU to put us to sleep */
+		pmu_request(&req, NULL, 5, PMU_SLEEP, 'M', 'A', 'T', 'T');
+		while (!req.complete && pmu_state != idle)
+			pmu_poll();
+	}
 
 	out_8(&via[B], in_8(&via[B]) | TREQ);
 	wait_for_ack();
@@ -1666,13 +2028,16 @@
 	 * talk to the PMU after this, so I moved it to _after_ sending the
 	 * sleep command to it. Still need to be checked.
 	 */
-	feature_prepare_for_sleep();
+	pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,1);
 
 	/* Call low-level ASM sleep handler */
-	low_sleep_handler();
+	if (__fake_sleep)
+		mdelay(5000);
+	else
+		low_sleep_handler();
 
 	/* Restore Apple core ASICs state */
-	feature_wake_up();
+	pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,0);
 
 	/* Restore VIA */
 	restore_via_state();
@@ -1694,11 +2059,6 @@
 	/* Restore userland MMU context */
 	set_context(current->active_mm->context, current->active_mm->pgd);
 
-	/* Re-enable DEC interrupts and kick DEC */
-	asm volatile("mtdec %0" : : "r" (0x7fffffff));
-	sti();
-	asm volatile("mtdec %0" : : "r" (0x10000000));
-
 	/* Tell PMU we are ready */
 	pmu_request(&req, NULL, 2, PMU_SYSTEM_READY, 2);
 	while (!req.complete)
@@ -1725,6 +2085,10 @@
 	/* Leave some time for HW to settle down */
 	mdelay(100);
 
+	/* Restart jiffies & scheduling */
+	wakeup_decrementer();
+	sti();
+
 	/* Notify drivers */
 	broadcast_wake();
 
@@ -1766,6 +2130,10 @@
 	 * vmalloc's are done before actual sleep of block drivers */
 	fsync_dev(0);
 
+	/* Give the disks a little time to actually finish writing */
+	for (wait = jiffies + (HZ/4); time_before(jiffies, wait); )
+		mb();
+
 	/* Sleep can fail now. May not be very robust but useful for debugging */
 	ret = broadcast_sleep(PBOOK_SLEEP_NOW, PBOOK_WAKE);
 	if (ret != PBOOK_SLEEP_OK) {
@@ -1773,13 +2141,9 @@
 		return -EBUSY;
 	}
 
-	/* Give the disks a little time to actually finish writing */
-	for (wait = jiffies + (HZ/4); time_before(jiffies, wait); )
-		mb();
-
 	/* Wait for completion of async backlight requests */
 	while (!bright_req_1.complete || !bright_req_2.complete ||
-		!bright_req_3.complete)
+		!bright_req_3.complete || !batt_req.complete)
 		pmu_poll();
 
 	/* Disable all interrupts except pmu */
@@ -1811,6 +2175,8 @@
 	while (!sleep_req.complete)
 		mb();
 
+	pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,1);
+
 	/* displacement-flush the L2 cache - necessary? */
 	for (p = KERNELBASE; p < KERNELBASE + 0x100000; p += 0x1000)
 		i = *(volatile int *)p;
@@ -1825,20 +2191,23 @@
 
 	/* OK, we're awake again, start restoring things */
 	out_be32(mem_ctrl_sleep, 0x3f);
+	pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,0);
 	pbook_pci_restore();
 
 	/* wait for the PMU interrupt sequence to complete */
 	while (asleep)
 		mb();
 
-	/* Re-enable DEC interrupts and kick DEC */
-	asm volatile("mtdec %0" : : "r" (0x7fffffff));
-	sti();
-	asm volatile("mtdec %0" : : "r" (0x10000000));
-
 	/* reenable interrupts */
 	pmac_sleep_restore_intrs();
 
+	/* Leave some time for HW to settle down */
+	mdelay(100);
+
+	/* Restart jiffies & scheduling */
+	wakeup_decrementer();
+	sti();
+
 	/* Notify drivers */
 	broadcast_wake();
 
@@ -1877,6 +2246,7 @@
 	spin_lock_irqsave(&all_pvt_lock, flags);
 	for (list = &all_pmu_pvt; (list = list->next) != &all_pmu_pvt; ) {
 		pp = list_entry(list, struct pmu_private, list);
+		spin_lock(&pp->lock);
 		i = pp->rb_put + 1;
 		if (i >= RB_SIZE)
 			i = 0;
@@ -1887,6 +2257,7 @@
 			pp->rb_put = i;
 			wake_up_interruptible(&pp->wait);
 		}
+		spin_unlock(&pp->lock);
 	}
 	spin_unlock_irqrestore(&all_pvt_lock, flags);
 }
@@ -1914,6 +2285,7 @@
 {
 	struct pmu_private *pp = file->private_data;
 	DECLARE_WAITQUEUE(wait, current);
+	unsigned long flags;
 	int ret;
 
 	if (count < 1 || pp == 0)
@@ -1922,38 +2294,41 @@
 	if (ret)
 		return ret;
 
+	spin_lock_irqsave(&pp->lock, flags);
 	add_wait_queue(&pp->wait, &wait);
 	current->state = TASK_INTERRUPTIBLE;
 
 	for (;;) {
 		ret = -EAGAIN;
-		spin_lock(&pp->lock);
 		if (pp->rb_get != pp->rb_put) {
 			int i = pp->rb_get;
 			struct rb_entry *rp = &pp->rb_buf[i];
 			ret = rp->len;
+			spin_unlock_irqrestore(&pp->lock, flags);
 			if (ret > count)
 				ret = count;
 			if (ret > 0 && copy_to_user(buf, rp->data, ret))
 				ret = -EFAULT;
 			if (++i >= RB_SIZE)
 				i = 0;
+			spin_lock_irqsave(&pp->lock, flags);
 			pp->rb_get = i;
 		}
-		spin_unlock(&pp->lock);
 		if (ret >= 0)
 			break;
-
 		if (file->f_flags & O_NONBLOCK)
 			break;
 		ret = -ERESTARTSYS;
 		if (signal_pending(current))
 			break;
+		spin_unlock_irqrestore(&pp->lock, flags);
 		schedule();
+		spin_lock_irqsave(&pp->lock, flags);
 	}
 	current->state = TASK_RUNNING;
 	remove_wait_queue(&pp->wait, &wait);
-
+	spin_unlock_irqrestore(&pp->lock, flags);
+	
 	return ret;
 }
 
@@ -1967,14 +2342,15 @@
 {
 	struct pmu_private *pp = filp->private_data;
 	unsigned int mask = 0;
-
+	unsigned long flags;
+	
 	if (pp == 0)
 		return 0;
 	poll_wait(filp, &pp->wait, wait);
-	spin_lock(&pp->lock);
+	spin_lock_irqsave(&pp->lock, flags);
 	if (pp->rb_get != pp->rb_put)
 		mask |= POLLIN;
-	spin_unlock(&pp->lock);
+	spin_unlock_irqrestore(&pp->lock, flags);
 	return mask;
 }
 
@@ -2025,7 +2401,7 @@
 		sleep_in_progress = 0;
 		return error;
 	case PMU_IOC_CAN_SLEEP:
-		return put_user(feature_can_sleep(), (__u32 *)arg);
+		return put_user((u32)can_sleep, (__u32 *)arg);
 
 #ifdef CONFIG_PMAC_BACKLIGHT
 	/* Backlight should have its own device or go via
@@ -2155,5 +2531,8 @@
 EXPORT_SYMBOL(pmu_register_sleep_notifier);
 EXPORT_SYMBOL(pmu_unregister_sleep_notifier);
 EXPORT_SYMBOL(pmu_enable_irled);
+EXPORT_SYMBOL(pmu_battery_count);
+EXPORT_SYMBOL(pmu_batteries);
+EXPORT_SYMBOL(pmu_power_flags);
 #endif /* CONFIG_PMAC_PBOOK */
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/mtd/devices/blkmtd.c linux/drivers/mtd/devices/blkmtd.c
--- linux.orig/drivers/mtd/devices/blkmtd.c	Mon Jan  7 23:41:09 2002
+++ linux/drivers/mtd/devices/blkmtd.c	Wed Dec 26 15:53:59 2001
@@ -1,10 +1,11 @@
 /* 
- * $Id: blkmtd.c,v 1.3 2001/10/02 15:33:20 dwmw2 Exp $
+ * $Id: blkmtd.c,v 1.7 2001/11/10 17:06:30 spse Exp $
+ *
  * blkmtd.c - use a block device as a fake MTD
  *
  * Author: Simon Evans <spse@secret.org.uk>
  *
- * Copyright (C) 2001 Simon Evans <spse@secret.org.uk>
+ * Copyright (C) 2001 Simon Evans
  * 
  * Licence: GPL
  *
@@ -13,7 +14,7 @@
  *       cache to cache access. Writes update the page cache with the
  *       new data but make a copy of the new page(s) and then a kernel
  *       thread writes pages out to the device in the background. This
- *       ensures tht writes are order even if a page is updated twice.
+ *       ensures that writes are order even if a page is updated twice.
  *       Also, since pages in the page cache are never marked as dirty,
  *       we dont have to worry about writepage() being called on some 
  *       random page which may not be in the write order.
@@ -30,7 +31,7 @@
  *       small memory systems and too small on large memory systems.
  * 
  *       Page cache usage may still be a bit wrong. Check we are doing
- *       everything proberly.
+ *       everything properly.
  * 
  *       Somehow allow writes to dirty the page cache so we dont use too
  *       much memory making copies of outgoing pages. Need to handle case
@@ -39,15 +40,9 @@
  * 
  *       Reading should read multiple pages at once rather than using 
  *       readpage() for each one. This is easy and will be fixed asap.
- *
- *       Dont run the write_thread if readonly. This is also easy and will
- *       be fixed asap.
- * 
- *       Even though the multiple erase regions are used if the default erase
- *       block size doesnt match the device properly, erases currently wont
- *       work on the last page if it is not a full page.
  */
 
+
 #include <linux/config.h>
 #include <linux/module.h>
 
@@ -59,20 +54,31 @@
 #include <linux/mtd/compatmac.h>
 #include <linux/mtd/mtd.h>
 
+#ifdef CONFIG_MTD_DEBUG
+#ifdef CONFIG_PROC_FS
+#  include <linux/proc_fs.h>
+#  define BLKMTD_PROC_DEBUG
+   static struct proc_dir_entry *blkmtd_proc;
+#endif
+#endif
+
+
 /* Default erase size in K, always make it a multiple of PAGE_SIZE */
 #define CONFIG_MTD_BLKDEV_ERASESIZE 128
-#define VERSION "1.1"
+#define VERSION "1.7"
 extern int *blk_size[];
 extern int *blksize_size[];
 
 /* Info for the block device */
 typedef struct mtd_raw_dev_data_s {
   struct block_device *binding;
-  int sector_size, sector_bits, total_sectors;
+  int sector_size, sector_bits;
+  int partial_last_page;   // 0 if device ends on page boundary, else page no of last page
+  int last_page_sectors;   // Number of sectors in last page if partial_last_page != 0
   size_t totalsize;
   int readonly;
   struct address_space as;
-  struct file *file;
+  struct mtd_info mtd_info;
 } mtd_raw_dev_data_t;
 
 /* Info for each queue item in the write queue */
@@ -85,24 +91,28 @@
 } mtdblkdev_write_queue_t;
 
 
+/* Our erase page - always remains locked. */
+static struct page *erase_page;
+
 /* Static info about the MTD, used in cleanup_module */
-static struct mtd_info *mtd_info;
+static mtd_raw_dev_data_t *mtd_rawdevice;
 
 /* Write queue fixed size */
 #define WRITE_QUEUE_SZ 512
 
 /* Storage for the write queue */
-static mtdblkdev_write_queue_t write_queue[WRITE_QUEUE_SZ];
+static mtdblkdev_write_queue_t *write_queue;
+static int write_queue_sz = WRITE_QUEUE_SZ;
 static int volatile write_queue_head;
 static int volatile write_queue_tail;
 static int volatile write_queue_cnt;
 static spinlock_t mbd_writeq_lock = SPIN_LOCK_UNLOCKED;
 
 /* Tell the write thread to finish */
-static volatile int write_task_finish = 0;
+static volatile int write_task_finish;
 
 /* ipc with the write thread */
-#if LINUX_VERSION_CODE > 0x020300
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,0)
 static DECLARE_MUTEX_LOCKED(thread_sem);
 static DECLARE_WAIT_QUEUE_HEAD(thr_wq);
 static DECLARE_WAIT_QUEUE_HEAD(mtbd_sync_wq);
@@ -113,13 +123,14 @@
 #endif
 
 
-
 /* Module parameters passed by insmod/modprobe */
 char *device;    /* the block device to use */
 int erasesz;     /* optional default erase size */
 int ro;          /* optional read only flag */
 int bs;          /* optionally force the block size (avoid using) */
 int count;       /* optionally force the block count (avoid using) */
+int wqs;         /* optionally set the write queue size */
+
 
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
 MODULE_LICENSE("GPL");
@@ -135,10 +146,10 @@
 MODULE_PARM_DESC(bs, "force the block size in bytes");
 MODULE_PARM(count, "i");
 MODULE_PARM_DESC(count, "force the block count");
+MODULE_PARM(wqs, "i");
 #endif
 
 
-
 /* Page cache stuff */
 
 /* writepage() - should never be called - catch it anyway */
@@ -150,13 +161,13 @@
 
 
 /* readpage() - reads one page from the block device */                 
-static int blkmtd_readpage(struct file *file, struct page *page)
+static int blkmtd_readpage(mtd_raw_dev_data_t *rawdevice, struct page *page)
 {  
   int err;
   int sectornr, sectors, i;
   struct kiobuf *iobuf;
-  mtd_raw_dev_data_t *rawdevice = (mtd_raw_dev_data_t *)file->private_data;
   kdev_t dev;
+  unsigned long *blocks;
 
   if(!rawdevice) {
     printk("blkmtd: readpage: PANIC file->private_data == NULL\n");
@@ -168,7 +179,7 @@
 	bdevname(dev), page, page->index);
 
   if(Page_Uptodate(page)) {
-    DEBUG(1, "blkmtd: readpage page %ld is already upto date\n", page->index);
+    DEBUG(2, "blkmtd: readpage page %ld is already upto date\n", page->index);
     UnlockPage(page);
     return 0;
   }
@@ -184,8 +195,9 @@
       mtdblkdev_write_queue_t *item = &write_queue[i];
       if(page->index >= item->pagenr && page->index < item->pagenr+item->pagecnt) {
 	/* yes it is */
-	int index = item->pagenr - page->index;
-	DEBUG(1, "blkmtd: readpage: found page %ld in outgoing write queue\n",
+	int index = page->index - item->pagenr;
+		
+	DEBUG(2, "blkmtd: readpage: found page %ld in outgoing write queue\n",
 	      page->index);
 	if(item->iserase) {
 	  memset(page_address(page), 0xff, PAGE_SIZE);
@@ -199,7 +211,7 @@
 	return 0;
       }
       i++;
-      i %= WRITE_QUEUE_SZ;
+      i %= write_queue_sz;
     }
   }
   spin_unlock(&mbd_writeq_lock);
@@ -208,8 +220,24 @@
   DEBUG(3, "blkmtd: readpage: getting kiovec\n");
   err = alloc_kiovec(1, &iobuf);
   if (err) {
+    printk("blkmtd: cant allocate kiobuf\n");
+    SetPageError(page);
     return err;
   }
+
+  /* Pre 2.4.4 doesnt have space for the block list in the kiobuf */ 
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,4)
+  blocks = kmalloc(KIO_MAX_SECTORS * sizeof(unsigned long));
+  if(blocks == NULL) {
+    printk("blkmtd: cant allocate iobuf blocks\n");
+    free_kiovec(1, &iobuf);
+    SetPageError(page);
+    return -ENOMEM;
+  }
+#else 
+  blocks = iobuf->blocks;
+#endif
+
   iobuf->offset = 0;
   iobuf->nr_pages = 1;
   iobuf->length = PAGE_SIZE;
@@ -217,16 +245,34 @@
   iobuf->maplist[0] = page;
   sectornr = page->index << (PAGE_SHIFT - rawdevice->sector_bits);
   sectors = 1 << (PAGE_SHIFT - rawdevice->sector_bits);
+  if(rawdevice->partial_last_page && page->index == rawdevice->partial_last_page) {
+    DEBUG(3, "blkmtd: handling partial last page\n");
+    sectors = rawdevice->last_page_sectors;
+  }
   DEBUG(3, "blkmtd: readpage: sectornr = %d sectors = %d\n", sectornr, sectors);
   for(i = 0; i < sectors; i++) {
-    iobuf->blocks[i] = sectornr++;
+    blocks[i] = sectornr++;
   }
+  /* If only a partial page read in, clear the rest of the page */
+  if(rawdevice->partial_last_page && page->index == rawdevice->partial_last_page) {
+    int offset = rawdevice->last_page_sectors << rawdevice->sector_bits;
+    int count = PAGE_SIZE-offset;
+    DEBUG(3, "blkmtd: clear last partial page: offset = %d count = %d\n", offset, count);
+    memset(page_address(page)+offset, 0, count);
+    sectors = rawdevice->last_page_sectors;
+  }
+
 
   DEBUG(3, "bklmtd: readpage: starting brw_kiovec\n");
-  err = brw_kiovec(READ, 1, &iobuf, dev, iobuf->blocks, rawdevice->sector_size);
+  err = brw_kiovec(READ, 1, &iobuf, dev, blocks, rawdevice->sector_size);
   DEBUG(3, "blkmtd: readpage: finished, err = %d\n", err);
   iobuf->locked = 0;
   free_kiovec(1, &iobuf);
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,4)
+  kfree(blocks);
+#endif
+
   if(err != PAGE_SIZE) {
     printk("blkmtd: readpage: error reading page %ld\n", page->index);
     memset(page_address(page), 0, PAGE_SIZE);
@@ -246,7 +292,7 @@
                     
 static struct address_space_operations blkmtd_aops = {
   writepage:     blkmtd_writepage,
-  readpage:      blkmtd_readpage,
+  readpage:      NULL,
 }; 
 
 
@@ -256,6 +302,7 @@
   int err;
   struct task_struct *tsk = current;
   struct kiobuf *iobuf;
+  unsigned long *blocks;
 
   DECLARE_WAITQUEUE(wait, tsk);
   DEBUG(1, "blkmtd: writetask: starting (pid = %d)\n", tsk->pid);
@@ -268,8 +315,23 @@
   spin_unlock_irq(&tsk->sigmask_lock);
   exit_sighand(tsk);
 
-  if(alloc_kiovec(1, &iobuf))
+  if(alloc_kiovec(1, &iobuf)) {
+    printk("blkmtd: write_queue_task cant allocate kiobuf\n");
     return 0;
+  }
+
+  /* Pre 2.4.4 doesnt have space for the block list in the kiobuf */ 
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,4)
+  blocks = kmalloc(KIO_MAX_SECTORS * sizeof(unsigned long));
+  if(blocks == NULL) {
+    printk("blkmtd: write_queue_task cant allocate iobuf blocks\n");
+    free_kiovec(1, &iobuf);
+    return 0;
+  }
+#else 
+  blocks = iobuf->blocks;
+#endif
+
   DEBUG(2, "blkmtd: writetask: entering main loop\n");
   add_wait_queue(&thr_wq, &wait);
 
@@ -280,30 +342,39 @@
       /* If nothing in the queue, wake up anyone wanting to know when there
 	 is space in the queue then sleep for 2*HZ */
       spin_unlock(&mbd_writeq_lock);
-      DEBUG(3, "blkmtd: writetask: queue empty\n");
+      DEBUG(4, "blkmtd: writetask: queue empty\n");
       if(waitqueue_active(&mtbd_sync_wq))
 	 wake_up(&mtbd_sync_wq);
       interruptible_sleep_on_timeout(&thr_wq, 2*HZ);
-      DEBUG(3, "blkmtd: writetask: woken up\n");
+      DEBUG(4, "blkmtd: writetask: woken up\n");
       if(write_task_finish)
 	break;
     } else {
       /* we have stuff to write */
       mtdblkdev_write_queue_t *item = &write_queue[write_queue_tail];
       struct page **pages = item->pages;
-      int pagecnt = item->pagecnt;
-      int pagenr = item->pagenr;
+
       int i;
+      int sectornr = item->pagenr << (PAGE_SHIFT - item->rawdevice->sector_bits);
+      int sectorcnt = item->pagecnt << (PAGE_SHIFT - item->rawdevice->sector_bits);
       int max_sectors = KIO_MAX_SECTORS >> (item->rawdevice->sector_bits - 9);
       kdev_t dev = to_kdev_t(item->rawdevice->binding->bd_dev);
-  
+
+      /* If we are writing to the last page on the device and it doesnt end
+       * on a page boundary, subtract the number of sectors that dont exist.
+       */
+      if(item->rawdevice->partial_last_page && 
+	 (item->pagenr + item->pagecnt -1) == item->rawdevice->partial_last_page) {
+	sectorcnt -= (1 << (PAGE_SHIFT - item->rawdevice->sector_bits));
+	sectorcnt += item->rawdevice->last_page_sectors;
+      }
 
       DEBUG(3, "blkmtd: writetask: got %d queue items\n", write_queue_cnt);
       set_current_state(TASK_RUNNING);
       spin_unlock(&mbd_writeq_lock);
 
-      DEBUG(2, "blkmtd: write_task: writing pagenr = %d pagecnt = %d", 
-	    item->pagenr, item->pagecnt);
+      DEBUG(2, "blkmtd: writetask: writing pagenr = %d pagecnt = %d sectornr = %d sectorcnt = %d\n", 
+	    item->pagenr, item->pagecnt, sectornr, sectorcnt);
 
       iobuf->offset = 0;
       iobuf->locked = 1;
@@ -311,31 +382,33 @@
       /* Loop through all the pages to be written in the queue item, remembering
 	 we can only write KIO_MAX_SECTORS at a time */
 	 
-      while(pagecnt) {
-	int sectornr = pagenr << (PAGE_SHIFT - item->rawdevice->sector_bits);
-	int sectorcnt = pagecnt << (PAGE_SHIFT - item->rawdevice->sector_bits);
+      while(sectorcnt) {
 	int cursectors = (sectorcnt < max_sectors) ? sectorcnt : max_sectors;
 	int cpagecnt = (cursectors << item->rawdevice->sector_bits) + PAGE_SIZE-1;
 	cpagecnt >>= PAGE_SHIFT;
 	
-	for(i = 0; i < cpagecnt; i++) 
+	for(i = 0; i < cpagecnt; i++) {
+	  if(item->iserase) {
+	    iobuf->maplist[i] = erase_page;
+	  } else {
 	    iobuf->maplist[i] = *(pages++);
+	  }
+	}
 	
 	for(i = 0; i < cursectors; i++) {
-	  iobuf->blocks[i] = sectornr++;
+	  blocks[i] = sectornr++;
 	}
 	
 	iobuf->nr_pages = cpagecnt;
 	iobuf->length = cursectors << item->rawdevice->sector_bits;
 	DEBUG(3, "blkmtd: write_task: about to kiovec\n");
-	err = brw_kiovec(WRITE, 1, &iobuf, dev, iobuf->blocks, item->rawdevice->sector_size);
+	err = brw_kiovec(WRITE, 1, &iobuf, dev, blocks, item->rawdevice->sector_size);
 	DEBUG(3, "bklmtd: write_task: done, err = %d\n", err);
 	if(err != (cursectors << item->rawdevice->sector_bits)) {
 	  /* if an error occured - set this to exit the loop */
-	  pagecnt = 0;
+	  sectorcnt = 0;
 	} else {
-	  pagenr += cpagecnt;
-	  pagecnt -= cpagecnt;
+	  sectorcnt -= cursectors;
 	}
       }
 
@@ -345,12 +418,14 @@
       spin_lock(&mbd_writeq_lock);
       write_queue_cnt--;
       write_queue_tail++;
-      write_queue_tail %= WRITE_QUEUE_SZ;
-      for(i = 0 ; i < item->pagecnt; i++) {
-	UnlockPage(item->pages[i]);
-	__free_pages(item->pages[i], 0);
+      write_queue_tail %= write_queue_sz;
+      if(!item->iserase) {
+	for(i = 0 ; i < item->pagecnt; i++) {
+	  UnlockPage(item->pages[i]);
+	  __free_pages(item->pages[i], 0);
+	}
+	kfree(item->pages);
       }
-      kfree(item->pages);
       item->pages = NULL;
       spin_unlock(&mbd_writeq_lock);
       /* Tell others there is some space in the write queue */
@@ -361,6 +436,11 @@
   remove_wait_queue(&thr_wq, &wait);
   DEBUG(1, "blkmtd: writetask: exiting\n");
   free_kiovec(1, &iobuf);
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,4)
+  kfree(blocks);
+#endif
+
   /* Tell people we have exitd */
   up(&thread_sem);
   return 0;
@@ -372,43 +452,45 @@
 			    int pagenr, int pagecnt, int iserase)
 {
   struct page *outpage;
-  struct page **new_pages;
+  struct page **new_pages = NULL;
   mtdblkdev_write_queue_t *item;
   int i;
   DECLARE_WAITQUEUE(wait, current);
-  DEBUG(2, "mtdblkdev: queue_page_write: adding pagenr = %d pagecnt = %d\n", pagenr, pagecnt);
+  DEBUG(2, "blkmtd: queue_page_write: adding pagenr = %d pagecnt = %d\n", pagenr, pagecnt);
 
   if(!pagecnt)
     return 0;
 
-  if(pages == NULL)
+  if(pages == NULL && !iserase)
     return -EINVAL;
 
   /* create a array for the list of pages */
-  new_pages = kmalloc(pagecnt * sizeof(struct page *), GFP_KERNEL);
-  if(new_pages == NULL)
-    return -ENOMEM;
+  if(!iserase) {
+    new_pages = kmalloc(pagecnt * sizeof(struct page *), GFP_KERNEL);
+    if(new_pages == NULL)
+      return -ENOMEM;
 
-  /* make copies of the pages in the page cache */
-  for(i = 0; i < pagecnt; i++) {
-    outpage = alloc_pages(GFP_KERNEL, 0);
-    if(!outpage) {
-      while(i--) {
-	UnlockPage(new_pages[i]);
-	__free_pages(new_pages[i], 0);
+    /* make copies of the pages in the page cache */
+    for(i = 0; i < pagecnt; i++) {
+      outpage = alloc_pages(GFP_KERNEL, 0);
+      if(!outpage) {
+	while(i--) {
+	  UnlockPage(new_pages[i]);
+	  __free_pages(new_pages[i], 0);
+	}
+	kfree(new_pages);
+	return -ENOMEM;
       }
-      kfree(new_pages);
-      return -ENOMEM;
+      lock_page(outpage);
+      memcpy(page_address(outpage), page_address(pages[i]), PAGE_SIZE);
+      new_pages[i] = outpage;
     }
-    lock_page(outpage);
-    memcpy(page_address(outpage), page_address(pages[i]), PAGE_SIZE);
-    new_pages[i] = outpage;
   }
 
   /* wait until there is some space in the write queue */
  test_lock:
   spin_lock(&mbd_writeq_lock);
-  if(write_queue_cnt == WRITE_QUEUE_SZ) {
+  if(write_queue_cnt == write_queue_sz) {
     spin_unlock(&mbd_writeq_lock);
     DEBUG(3, "blkmtd: queue_page: Queue full\n");
     current->state = TASK_UNINTERRUPTIBLE;
@@ -417,11 +499,11 @@
     schedule();
     current->state = TASK_RUNNING;
     remove_wait_queue(&mtbd_sync_wq, &wait);
-    DEBUG(3, "blkmtd: queue_page: Queue has %d items in it\n", write_queue_cnt);
+    DEBUG(3, "blkmtd: queue_page_write: Queue has %d items in it\n", write_queue_cnt);
     goto test_lock;
   }
 
-  DEBUG(3, "blkmtd: queue_write_page: qhead: %d qtail: %d qcnt: %d\n", 
+  DEBUG(3, "blkmtd: queue_page_write: qhead: %d qtail: %d qcnt: %d\n", 
 	write_queue_head, write_queue_tail, write_queue_cnt);
 
   /* fix up the queue item */
@@ -433,9 +515,9 @@
   item->iserase = iserase;
 
   write_queue_head++;
-  write_queue_head %= WRITE_QUEUE_SZ;
+  write_queue_head %= write_queue_sz;
   write_queue_cnt++;
-  DEBUG(3, "blkmtd: queue_write_page: qhead: %d qtail: %d qcnt: %d\n", 
+  DEBUG(3, "blkmtd: queue_page_write: qhead: %d qtail: %d qcnt: %d\n", 
 	write_queue_head, write_queue_tail, write_queue_cnt);
   spin_unlock(&mbd_writeq_lock);
   DEBUG(2, "blkmtd: queue_page_write: finished\n");
@@ -447,6 +529,8 @@
 static int blkmtd_erase(struct mtd_info *mtd, struct erase_info *instr)
 {
   mtd_raw_dev_data_t *rawdevice = mtd->priv;
+  struct mtd_erase_region_info *einfo = mtd->eraseregions;
+  int numregions = mtd->numeraseregions;
   size_t from;
   u_long len;
   int err = 0;
@@ -462,17 +546,23 @@
   from = instr->addr;
   len = instr->len;
 
-  /* check page alignment of start and length */
-  DEBUG(2, "blkmtd: erase: dev = `%s' from = %d len = %ld\n",
+  /* check erase region has valid start and length */
+  DEBUG(2, "blkmtd: erase: dev = `%s' from = 0x%x len = 0x%lx\n",
 	bdevname(rawdevice->binding->bd_dev), from, len);
-  if(from % PAGE_SIZE) {
-    printk("blkmtd: erase: addr not page aligned (addr = %d)\n", from);
-    instr->state = MTD_ERASE_FAILED;
-    err = -EIO;
+  while(numregions) {
+    DEBUG(3, "blkmtd: checking erase region = 0x%08X size = 0x%X num = 0x%x\n",
+	  einfo->offset, einfo->erasesize, einfo->numblocks);
+    if(from >= einfo->offset && from < einfo->offset + (einfo->erasesize * einfo->numblocks)) {
+      if(len == einfo->erasesize && ( (from - einfo->offset) % einfo->erasesize == 0))
+	break;
+    }
+    numregions--;
+    einfo++;
   }
 
-  if(len % PAGE_SIZE) {
-    printk("blkmtd: erase: len not a whole number of pages (len = %ld)\n", len);
+  if(!numregions) {
+    /* Not a valid erase block */
+    printk("blkmtd: erase: invalid erase request 0x%lX @ 0x%08X\n", len, from);
     instr->state = MTD_ERASE_FAILED;
     err = -EIO;
   }
@@ -483,9 +573,14 @@
     struct page *page, **pages;
     int i = 0;
 
+    /* Handle the last page of the device not being whole */
+    if(len < PAGE_SIZE)
+      len = PAGE_SIZE;
+
     pagenr = from >> PAGE_SHIFT;
     pagecnt = len >> PAGE_SHIFT;
     DEBUG(3, "blkmtd: erase: pagenr = %d pagecnt = %d\n", pagenr, pagecnt);
+
     pages = kmalloc(pagecnt * sizeof(struct page *), GFP_KERNEL);
     if(pages == NULL) {
       err = -ENOMEM;
@@ -493,9 +588,10 @@
       goto erase_out;
     }
 
+
     while(pagecnt) {
       /* get the page via the page cache */
-      DEBUG(3, "blkmtd: erase: doing grap_cache_page() for page %d\n", pagenr);
+      DEBUG(3, "blkmtd: erase: doing grab_cache_page() for page %d\n", pagenr);
       page = grab_cache_page(&rawdevice->as, pagenr);
       if(!page) {
 	DEBUG(3, "blkmtd: erase: grab_cache_page() failed for page %d\n", pagenr);
@@ -511,7 +607,7 @@
       i++;
     }
     DEBUG(3, "blkmtd: erase: queuing page write\n");
-    err = queue_page_write(rawdevice, pages, from >> PAGE_SHIFT, len >> PAGE_SHIFT, 1);
+    err = queue_page_write(rawdevice, NULL, from >> PAGE_SHIFT, len >> PAGE_SHIFT, 1);
     pagecnt = len >> PAGE_SHIFT;
     if(!err) {
       while(pagecnt--) {
@@ -567,7 +663,7 @@
     struct page *page;
     int cpylen;
     DEBUG(3, "blkmtd: read: looking for page: %d\n", pagenr);
-    page = read_cache_page(&rawdevice->as, pagenr, (filler_t *)blkmtd_readpage, rawdevice->file);
+    page = read_cache_page(&rawdevice->as, pagenr, (filler_t *)blkmtd_readpage, rawdevice);
     if(IS_ERR(page)) {
       return PTR_ERR(page);
     }
@@ -683,7 +779,7 @@
     struct page *page;
     
     DEBUG(3, "blkmtd: write: doing partial start, page = %d len = %d offset = %d\n", pagenr, len1, offset);
-    page = read_cache_page(&rawdevice->as, pagenr, (filler_t *)blkmtd_readpage, rawdevice->file);
+    page = read_cache_page(&rawdevice->as, pagenr, (filler_t *)blkmtd_readpage, rawdevice);
 
     if(IS_ERR(page)) {
       kfree(pages);
@@ -716,6 +812,7 @@
       memcpy(page_address(page), buf, PAGE_SIZE);
       pages[pagecnt++] = page;
       UnlockPage(page);
+      SetPageUptodate(page);
       pagenr++;
       pagesc--;
       buf += PAGE_SIZE;
@@ -728,7 +825,7 @@
     /* do the third region */
     struct page *page;
     DEBUG(3, "blkmtd: write: doing partial end, page = %d len = %d\n", pagenr, len3);
-    page = read_cache_page(&rawdevice->as, pagenr, (filler_t *)blkmtd_readpage, rawdevice->file);
+    page = read_cache_page(&rawdevice->as, pagenr, (filler_t *)blkmtd_readpage, rawdevice);
     if(IS_ERR(page)) {
       err = PTR_ERR(page);
       goto write_err;
@@ -766,6 +863,10 @@
 static void blkmtd_sync(struct mtd_info *mtd)
 {
   DECLARE_WAITQUEUE(wait, current);
+  mtd_raw_dev_data_t *rawdevice = mtd->priv;
+  if(rawdevice->readonly)
+    return;
+
   DEBUG(2, "blkmtd: sync: called\n");
 
  stuff_inq:
@@ -784,36 +885,144 @@
   }
   spin_unlock(&mbd_writeq_lock);
 
-  DEBUG(2, "blkmtdL sync: finished\n");
+  DEBUG(2, "blkmtd: sync: finished\n");
 }
 
+
+#ifdef BLKMTD_PROC_DEBUG
+/* procfs stuff */
+static int blkmtd_proc_read(char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+  int clean = 0, dirty = 0, locked = 0;
+  struct list_head *temp;
+  int i, len, pages = 0, cnt;
+  MOD_INC_USE_COUNT;
+  spin_lock(&mbd_writeq_lock);
+  cnt = write_queue_cnt;
+  i = write_queue_tail;
+  while(cnt) {
+    if(!write_queue[i].iserase)
+      pages += write_queue[i].pagecnt;
+    i++;
+    i %= write_queue_sz;
+    cnt--;
+  }
+
+  /* Count the size of the page lists */
+  list_for_each(temp, &mtd_rawdevice->as.clean_pages) {
+    clean++;
+  }
+  list_for_each(temp, &mtd_rawdevice->as.dirty_pages) {
+    dirty++;
+  }
+  list_for_each(temp, &mtd_rawdevice->as.locked_pages) {
+    locked++;
+  }
+
+  len = sprintf(page, "Write queue head: %d\nWrite queue tail: %d\n"
+		"Write queue count: %d\nPages in queue: %d (%dK)\n"
+		"Clean Pages: %d\nDirty Pages: %d\nLocked Pages: %d\n"
+		"nrpages: %ld\n",
+		write_queue_head, write_queue_tail, write_queue_cnt,
+		pages, pages << (PAGE_SHIFT-10), clean, dirty, locked,
+		mtd_rawdevice->as.nrpages);
+  if(len <= count)
+    *eof = 1;
+  spin_unlock(&mbd_writeq_lock);
+  MOD_DEC_USE_COUNT;
+  return len;
+}
+#endif
+
+
 /* Cleanup and exit - sync the device and kill of the kernel thread */
 static void __exit cleanup_blkmtd(void)
 {
-  if (mtd_info) {
-    mtd_raw_dev_data_t *rawdevice = mtd_info->priv;
-    // sync the device
-    if (rawdevice) {
-      blkmtd_sync(mtd_info);
+#ifdef BLKMTD_PROC_DEBUG
+  if(blkmtd_proc) {
+    remove_proc_entry("blkmtd_debug", NULL);
+  }
+#endif
+
+  if (mtd_rawdevice) {
+    /* sync the device */
+    if (!mtd_rawdevice->readonly) {
+      blkmtd_sync(&mtd_rawdevice->mtd_info);
       write_task_finish = 1;
       wake_up_interruptible(&thr_wq);
       down(&thread_sem);
-      if(rawdevice->binding != NULL)
-	blkdev_put(rawdevice->binding, BDEV_RAW);
-      filp_close(rawdevice->file, NULL);
-      kfree(mtd_info->priv);
     }
-    if(mtd_info->eraseregions)
-      kfree(mtd_info->eraseregions);
-    del_mtd_device(mtd_info);
-    kfree(mtd_info);
-    mtd_info = NULL;
+    del_mtd_device(&mtd_rawdevice->mtd_info);
+    if(mtd_rawdevice->binding != NULL)
+      blkdev_put(mtd_rawdevice->binding, BDEV_RAW);
+
+    if(mtd_rawdevice->mtd_info.eraseregions)
+      kfree(mtd_rawdevice->mtd_info.eraseregions);
+    if(mtd_rawdevice->mtd_info.name)
+      kfree(mtd_rawdevice->mtd_info.name);
+
+    kfree(mtd_rawdevice);
+  }
+  if(write_queue)
+    kfree(write_queue);
+
+  if(erase_page) {
+    UnlockPage(erase_page);
+    __free_pages(erase_page, 0);
   }
   printk("blkmtd: unloaded for %s\n", device);
 }
 
 extern struct module __this_module;
 
+#ifndef MODULE
+
+/* Handle kernel boot params */
+
+
+static int __init param_blkmtd_device(char *str)
+{
+  device = str;
+  return 1;
+}
+
+
+static int __init param_blkmtd_erasesz(char *str)
+{
+  erasesz = simple_strtol(str, NULL, 0);
+  return 1;
+}
+
+
+static int __init param_blkmtd_ro(char *str)
+{
+  ro = simple_strtol(str, NULL, 0);
+  return 1;
+}
+
+
+static int __init param_blkmtd_bs(char *str)
+{
+  bs = simple_strtol(str, NULL, 0);
+  return 1;
+}
+
+
+static int __init param_blkmtd_count(char *str)
+{
+  count = simple_strtol(str, NULL, 0);
+  return 1;
+}
+
+__setup("blkmtd_device=", param_blkmtd_device);
+__setup("blkmtd_erasesz=", param_blkmtd_erasesz);
+__setup("blkmtd_ro=", param_blkmtd_ro);
+__setup("blkmtd_bs=", param_blkmtd_bs);
+__setup("blkmtd_count=", param_blkmtd_count);
+
+#endif
+
+
 /* for a given size and initial erase size, calculate the number and size of each
    erase region */
 static int __init calc_erase_regions(struct mtd_erase_region_info *info, size_t erase_size, size_t total_size)
@@ -842,12 +1051,16 @@
 }
 
 
+extern kdev_t name_to_kdev_t(char *line) __init;
+
 /* Startup */
 static int __init init_blkmtd(void)
 {
+#ifdef MODULE
   struct file *file = NULL;
   struct inode *inode;
-  mtd_raw_dev_data_t *rawdevice = NULL;
+#endif
+
   int maj, min;
   int i, blocksize, blocksize_bits;
   loff_t size = 0;
@@ -856,15 +1069,12 @@
   kdev_t rdev;
   int err;
   int mode;
-  int totalsize = 0, total_sectors = 0;
   int regions;
 
-  mtd_info = NULL;
-
-  // Check args
+  /* Check args */
   if(device == 0) {
     printk("blkmtd: error, missing `device' name\n");
-    return 1;
+    return -EINVAL;
   }
 
   if(ro)
@@ -873,12 +1083,25 @@
   if(erasesz)
     erase_size = erasesz;
 
-  DEBUG(1, "blkmtd: got device = `%s' erase size = %dK readonly = %s\n", device, erase_size, readonly ? "yes" : "no");
-  // Get a handle on the device
+  if(wqs) {
+    if(wqs < 16) 
+      wqs = 16;
+    if(wqs > 4*WRITE_QUEUE_SZ)
+      wqs = 4*WRITE_QUEUE_SZ;
+    write_queue_sz = wqs;
+  }
+
+  DEBUG(1, "blkmtd: device = `%s' erase size = %dK readonly = %s queue size = %d\n",
+	device, erase_size, readonly ? "yes" : "no", write_queue_sz);
+  /* Get a handle on the device */
   mode = (readonly) ? O_RDONLY : O_RDWR;
+
+#ifdef MODULE
+
   file = filp_open(device, mode, 0);
   if(IS_ERR(file)) {
-    DEBUG(2, "blkmtd: open_namei returned %ld\n", PTR_ERR(file));
+    printk("blkmtd: error, cant open device %s\n", device);
+    DEBUG(2, "blkmtd: filp_open returned %ld\n", PTR_ERR(file));
     return 1;
   }
   
@@ -891,11 +1114,19 @@
     return 1;
   }
   rdev = inode->i_rdev;
-  //filp_close(file, NULL);
-  DEBUG(1, "blkmtd: found a block device major = %d, minor = %d\n",
-	 MAJOR(rdev), MINOR(rdev));
+  filp_close(file, NULL);
+#else
+  rdev = name_to_kdev_t(device);
+#endif
+
   maj = MAJOR(rdev);
   min = MINOR(rdev);
+  DEBUG(1, "blkmtd: found a block device major = %d, minor = %d\n", maj, min);
+
+  if(!rdev) {
+    printk("blkmtd: bad block device: `%s'\n", device);
+    return 1;
+  }
 
   if(maj == MTD_BLOCK_MAJOR) {
     printk("blkmtd: attempting to use an MTD device as a block device\n");
@@ -927,116 +1158,159 @@
       size = ((loff_t) blk_size[maj][min] << BLOCK_SIZE_BITS) >> blocksize_bits;
     }
   }
-  total_sectors = size;
   size *= blocksize;
-  totalsize = size;
   DEBUG(1, "blkmtd: size = %ld\n", (long int)size);
 
   if(size == 0) {
     printk("blkmtd: cant determine size\n");
     return 1;
   }
-  rawdevice = (mtd_raw_dev_data_t *)kmalloc(sizeof(mtd_raw_dev_data_t), GFP_KERNEL);
-  if(rawdevice == NULL) {
+
+  mtd_rawdevice = (mtd_raw_dev_data_t *)kmalloc(sizeof(mtd_raw_dev_data_t), GFP_KERNEL);
+  if(mtd_rawdevice == NULL) {
     err = -ENOMEM;
     goto init_err;
   }
-  memset(rawdevice, 0, sizeof(mtd_raw_dev_data_t));
-  // get the block device
-  rawdevice->binding = bdget(kdev_t_to_nr(MKDEV(maj, min)));
-  err = blkdev_get(rawdevice->binding, mode, 0, BDEV_RAW);
+  memset(mtd_rawdevice, 0, sizeof(mtd_raw_dev_data_t));
+  /* get the block device */
+  mtd_rawdevice->binding = bdget(kdev_t_to_nr(MKDEV(maj, min)));
+  err = blkdev_get(mtd_rawdevice->binding, mode, 0, BDEV_RAW);
   if (err) {
     goto init_err;
   }
-  rawdevice->totalsize = totalsize;
-  rawdevice->total_sectors = total_sectors;
-  rawdevice->sector_size = blocksize;
-  rawdevice->sector_bits = blocksize_bits;
-  rawdevice->readonly = readonly;
-
-  DEBUG(2, "sector_size = %d, sector_bits = %d\n", rawdevice->sector_size, rawdevice->sector_bits);
-
-  mtd_info = (struct mtd_info *)kmalloc(sizeof(struct mtd_info), GFP_KERNEL);
-  if (mtd_info == NULL) {
-    err = -ENOMEM;
+  mtd_rawdevice->totalsize = size;
+  mtd_rawdevice->sector_size = blocksize;
+  mtd_rawdevice->sector_bits = blocksize_bits;
+  mtd_rawdevice->readonly = readonly;
+
+  /* See if device ends on page boundary */
+  if(size % PAGE_SIZE) {
+    mtd_rawdevice->partial_last_page = size >> PAGE_SHIFT;
+    mtd_rawdevice->last_page_sectors = (size & (PAGE_SIZE-1)) >> blocksize_bits;
+  }
+
+  DEBUG(2, "sector_size = %d, sector_bits = %d, partial_last_page = %d last_page_sectors = %d\n", 
+	mtd_rawdevice->sector_size, mtd_rawdevice->sector_bits,
+	mtd_rawdevice->partial_last_page, mtd_rawdevice->last_page_sectors);
+
+  /* Setup the MTD structure */
+  /* make the name contain the block device in */
+  mtd_rawdevice->mtd_info.name = kmalloc(9 + strlen(device), GFP_KERNEL);
+  if(mtd_rawdevice->mtd_info.name == NULL)
     goto init_err;
-  }
-  memset(mtd_info, 0, sizeof(*mtd_info));
 
-  // Setup the MTD structure
-  mtd_info->name = "blkmtd block device";
+  sprintf(mtd_rawdevice->mtd_info.name, "blkmtd: %s", device);
   if(readonly) {
-    mtd_info->type = MTD_ROM;
-    mtd_info->flags = MTD_CAP_ROM;
-    mtd_info->erasesize = erase_size << 10;
+    mtd_rawdevice->mtd_info.type = MTD_ROM;
+    mtd_rawdevice->mtd_info.flags = MTD_CAP_ROM;
+    mtd_rawdevice->mtd_info.erasesize = erase_size << 10;
   } else {
-    mtd_info->type = MTD_RAM;
-    mtd_info->flags = MTD_CAP_RAM;
-    mtd_info->erasesize = erase_size << 10;
-  }
-  mtd_info->size = size;
-  mtd_info->erase = blkmtd_erase;
-  mtd_info->read = blkmtd_read;
-  mtd_info->write = blkmtd_write;
-  mtd_info->sync = blkmtd_sync;
-  mtd_info->point = 0;
-  mtd_info->unpoint = 0;
+    mtd_rawdevice->mtd_info.type = MTD_RAM;
+    mtd_rawdevice->mtd_info.flags = MTD_CAP_RAM;
+    mtd_rawdevice->mtd_info.erasesize = erase_size << 10;
+  }
+  mtd_rawdevice->mtd_info.size = size;
+  mtd_rawdevice->mtd_info.erase = blkmtd_erase;
+  mtd_rawdevice->mtd_info.read = blkmtd_read;
+  mtd_rawdevice->mtd_info.write = blkmtd_write;
+  mtd_rawdevice->mtd_info.sync = blkmtd_sync;
+  mtd_rawdevice->mtd_info.point = 0;
+  mtd_rawdevice->mtd_info.unpoint = 0;
 
-  mtd_info->priv = rawdevice;
+  mtd_rawdevice->mtd_info.priv = mtd_rawdevice;
   regions = calc_erase_regions(NULL, erase_size << 10, size);
   DEBUG(1, "blkmtd: init: found %d erase regions\n", regions);
-  mtd_info->eraseregions = kmalloc(regions * sizeof(struct mtd_erase_region_info), GFP_KERNEL);
-  if(mtd_info->eraseregions == NULL) {
+  mtd_rawdevice->mtd_info.eraseregions = kmalloc(regions * sizeof(struct mtd_erase_region_info), GFP_KERNEL);
+  if(mtd_rawdevice->mtd_info.eraseregions == NULL) {
+    err = -ENOMEM;
+    goto init_err;
   }
-  mtd_info->numeraseregions = regions;
-  calc_erase_regions(mtd_info->eraseregions, erase_size << 10, size);
+  mtd_rawdevice->mtd_info.numeraseregions = regions;
+  calc_erase_regions(mtd_rawdevice->mtd_info.eraseregions, erase_size << 10, size);
 
   /* setup the page cache info */
-  INIT_LIST_HEAD(&rawdevice->as.clean_pages);
-  INIT_LIST_HEAD(&rawdevice->as.dirty_pages);
-  INIT_LIST_HEAD(&rawdevice->as.locked_pages);
-  rawdevice->as.nrpages = 0;
-  rawdevice->as.a_ops = &blkmtd_aops;
-  rawdevice->as.host = inode;
-  rawdevice->as.i_mmap = NULL;
-  rawdevice->as.i_mmap_shared = NULL;
-  spin_lock_init(&rawdevice->as.i_shared_lock);
-  rawdevice->as.gfp_mask = GFP_KERNEL;
-  rawdevice->file = file;
-
-  file->private_data = rawdevice;
+  
+  mtd_rawdevice->as.nrpages = 0;
+  INIT_LIST_HEAD(&mtd_rawdevice->as.clean_pages);
+  INIT_LIST_HEAD(&mtd_rawdevice->as.dirty_pages);
+  INIT_LIST_HEAD(&mtd_rawdevice->as.locked_pages);
+  mtd_rawdevice->as.host = NULL;
+  spin_lock_init(&(mtd_rawdevice->as.i_shared_lock));
+
+  mtd_rawdevice->as.a_ops = &blkmtd_aops;
+  mtd_rawdevice->as.i_mmap = NULL;
+  mtd_rawdevice->as.i_mmap_shared = NULL;
+  mtd_rawdevice->as.gfp_mask = GFP_KERNEL;
 
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
-   mtd_info->module = THIS_MODULE;			
+  mtd_rawdevice->mtd_info.module = THIS_MODULE;			
 #endif
-   if (add_mtd_device(mtd_info)) {
-     err = -EIO;
-     goto init_err;
-   }
-   init_waitqueue_head(&thr_wq);
-   init_waitqueue_head(&mtbd_sync_wq);
-   DEBUG(3, "blkmtd: init: kernel task @ %p\n", write_queue_task);
-   DEBUG(2, "blkmtd: init: starting kernel task\n");
-   kernel_thread(write_queue_task, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
-   DEBUG(2, "blkmtd: init: started\n");
-   printk("blkmtd loaded: version = %s using %s erase_size = %dK %s\n", VERSION, device, erase_size, (readonly) ? "(read-only)" : "");
-   return 0;
+  if (add_mtd_device(&mtd_rawdevice->mtd_info)) {
+    err = -EIO;
+    goto init_err;
+  }
 
+  if(!mtd_rawdevice->readonly) {
+    /* Allocate the write queue */
+    write_queue = kmalloc(write_queue_sz * sizeof(mtdblkdev_write_queue_t), GFP_KERNEL);
+    if(!write_queue) {
+      err = -ENOMEM;
+      goto init_err;
+    }
+    /* Set up the erase page */
+    erase_page = alloc_pages(GFP_KERNEL, 0);
+    if(erase_page == NULL) {
+      err = -ENOMEM;
+      goto init_err;
+    }
+    memset(page_address(erase_page), 0xff, PAGE_SIZE);
+    lock_page(erase_page);
+
+    init_waitqueue_head(&thr_wq);
+    init_waitqueue_head(&mtbd_sync_wq);
+    DEBUG(3, "blkmtd: init: kernel task @ %p\n", write_queue_task);
+    DEBUG(2, "blkmtd: init: starting kernel task\n");
+    kernel_thread(write_queue_task, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
+    DEBUG(2, "blkmtd: init: started\n");
+    printk("blkmtd loaded: version = %s using %s erase_size = %dK %s\n",
+	   VERSION, device, erase_size, (readonly) ? "(read-only)" : "");
+  }
+
+#ifdef BLKMTD_PROC_DEBUG
+  /* create proc entry */
+  DEBUG(2, "Creating /proc/blkmtd_debug\n");
+  blkmtd_proc = create_proc_read_entry("blkmtd_debug", 0444,
+				       NULL, blkmtd_proc_read, NULL);
+  if(blkmtd_proc == NULL) {
+    printk("Cant create /proc/blkmtd_debug\n");
+  } else {
+    blkmtd_proc->owner = THIS_MODULE;
+  }
+#endif
+  
+  /* Everything is ok if we got here */
+  return 0;
+  
  init_err:
-   if(!rawdevice) {
-     if(rawdevice->binding) 
-       blkdev_put(rawdevice->binding, BDEV_RAW);
 
-     kfree(rawdevice);
-     rawdevice = NULL;
-   }
-   if(mtd_info) {
-     if(mtd_info->eraseregions)
-       kfree(mtd_info->eraseregions);
-     kfree(mtd_info);
-     mtd_info = NULL;
-   }
-   return err;
+  if(mtd_rawdevice) {
+    if(mtd_rawdevice->mtd_info.eraseregions)
+      kfree(mtd_rawdevice->mtd_info.eraseregions);
+    if(mtd_rawdevice->mtd_info.name)
+      kfree(mtd_rawdevice->mtd_info.name);
+    if(mtd_rawdevice->binding) 
+      blkdev_put(mtd_rawdevice->binding, BDEV_RAW);
+    kfree(mtd_rawdevice);
+  }
+
+  if(write_queue) {
+    kfree(write_queue);
+    write_queue = NULL;
+  }
+
+  if(erase_page) 
+    __free_pages(erase_page, 0);
+  return err;
 }
 
 module_init(init_blkmtd);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/3c501.c linux/drivers/net/3c501.c
--- linux.orig/drivers/net/3c501.c	Mon Jan  7 23:41:09 2002
+++ linux/drivers/net/3c501.c	Fri Dec 21 18:50:47 2001
@@ -87,8 +87,12 @@
  *
  */
 
+#define DRV_NAME	"3c501"
+#define DRV_VERSION	"2001/11/17"
+
+
 static const char version[] =
-    "3c501.c: 2000/02/08 Alan Cox (alan@redhat.com).\n";
+	DRV_NAME ".c: " DRV_VERSION " Alan Cox (alan@redhat.com).\n";
 
 /*
  *	Braindamage remaining:
@@ -108,7 +112,9 @@
 #include <linux/errno.h>
 #include <linux/config.h>	/* for CONFIG_IP_MULTICAST */
 #include <linux/spinlock.h>
+#include <linux/ethtool.h>
 
+#include <asm/uaccess.h>
 #include <asm/bitops.h>
 #include <asm/io.h>
 
@@ -139,12 +145,14 @@
 static int  el1_close(struct net_device *dev);
 static struct net_device_stats *el1_get_stats(struct net_device *dev);
 static void set_multicast_list(struct net_device *dev);
+static int netdev_ioctl (struct net_device *dev, struct ifreq *rq, int cmd);
 
 #define EL1_IO_EXTENT	16
 
 #ifndef EL_DEBUG
 #define EL_DEBUG  0	/* use 0 for production, 1 for devel., >2 for debug */
 #endif			/* Anything above 5 is wordy death! */
+#define debug el_debug
 static int el_debug = EL_DEBUG;
 
 /*
@@ -377,6 +385,7 @@
 	dev->stop = &el1_close;
 	dev->get_stats = &el1_get_stats;
 	dev->set_multicast_list = &set_multicast_list;
+	dev->do_ioctl = netdev_ioctl;
 
 	/*
 	 *	Setup the generic properties
@@ -915,6 +924,86 @@
 	}
 }
 
+/**
+ * netdev_ethtool_ioctl: Handle network interface SIOCETHTOOL ioctls
+ * @dev: network interface on which out-of-band action is to be performed
+ * @useraddr: userspace address to which data is to be read and returned
+ *
+ * Process the various commands of the SIOCETHTOOL interface.
+ */
+
+static int netdev_ethtool_ioctl (struct net_device *dev, void *useraddr)
+{
+	u32 ethcmd;
+
+	/* dev_ioctl() in ../../net/core/dev.c has already checked
+	   capable(CAP_NET_ADMIN), so don't bother with that here.  */
+
+	if (get_user(ethcmd, (u32 *)useraddr))
+		return -EFAULT;
+
+	switch (ethcmd) {
+
+	case ETHTOOL_GDRVINFO: {
+		struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO };
+		strcpy (info.driver, DRV_NAME);
+		strcpy (info.version, DRV_VERSION);
+		sprintf(info.bus_info, "ISA 0x%lx", dev->base_addr);
+		if (copy_to_user (useraddr, &info, sizeof (info)))
+			return -EFAULT;
+		return 0;
+	}
+
+	/* get message-level */
+	case ETHTOOL_GMSGLVL: {
+		struct ethtool_value edata = {ETHTOOL_GMSGLVL};
+		edata.data = debug;
+		if (copy_to_user(useraddr, &edata, sizeof(edata)))
+			return -EFAULT;
+		return 0;
+	}
+	/* set message-level */
+	case ETHTOOL_SMSGLVL: {
+		struct ethtool_value edata;
+		if (copy_from_user(&edata, useraddr, sizeof(edata)))
+			return -EFAULT;
+		debug = edata.data;
+		return 0;
+	}
+
+	default:
+		break;
+	}
+
+	return -EOPNOTSUPP;
+}
+
+/**
+ * netdev_ioctl: Handle network interface ioctls
+ * @dev: network interface on which out-of-band action is to be performed
+ * @rq: user request data
+ * @cmd: command issued by user
+ *
+ * Process the various out-of-band ioctls passed to this driver.
+ */
+
+static int netdev_ioctl (struct net_device *dev, struct ifreq *rq, int cmd)
+{
+	int rc = 0;
+
+	switch (cmd) {
+	case SIOCETHTOOL:
+		rc = netdev_ethtool_ioctl(dev, (void *) rq->ifr_data);
+		break;
+
+	default:
+		rc = -EOPNOTSUPP;
+		break;
+	}
+
+	return rc;
+}
+ 
 #ifdef MODULE
 
 static struct net_device dev_3c501 = {
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/3c503.c linux/drivers/net/3c503.c
--- linux.orig/drivers/net/3c503.c	Mon Jan  7 23:41:09 2002
+++ linux/drivers/net/3c503.c	Fri Dec 21 18:50:47 2001
@@ -29,11 +29,17 @@
     Paul Gortmaker	: add support for the 2nd 8kB of RAM on 16 bit cards.
     Paul Gortmaker	: multiple card support for module users.
     rjohnson@analogic.com : Fix up PIO interface for efficient operation.
+    Jeff Garzik		: ethtool support
 
 */
 
+#define DRV_NAME	"3c503"
+#define DRV_VERSION	"1.10a"
+#define DRV_RELDATE	"11/17/2001"
+
+
 static const char version[] =
-    "3c503.c:v1.10 9/23/93  Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
+    DRV_NAME ".c:v" DRV_VERSION " " DRV_RELDATE "  Donald Becker (becker@scyld.com)\n";
 
 #include <linux/module.h>
 
@@ -45,7 +51,9 @@
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/init.h>
+#include <linux/ethtool.h>
 
+#include <asm/uaccess.h>
 #include <asm/io.h>
 #include <asm/system.h>
 #include <asm/byteorder.h>
@@ -74,6 +82,7 @@
 			   int ring_offset);
 static void el2_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
 			 int ring_page);
+static int netdev_ioctl (struct net_device *dev, struct ifreq *rq, int cmd);
 
 
 /* This routine probes for a memory-mapped 3c503 board by looking for
@@ -301,6 +310,7 @@
 
     dev->open = &el2_open;
     dev->stop = &el2_close;
+    dev->do_ioctl = &netdev_ioctl;
 
     if (dev->mem_start)
 	printk("%s: %s - %dkB RAM, 8kB shared mem window at %#6lx-%#6lx.\n",
@@ -607,6 +617,71 @@
     outb_p(ei_status.interface_num == 0 ? ECNTRL_THIN : ECNTRL_AUI, E33G_CNTRL);
     return;
 }
+
+/**
+ * netdev_ethtool_ioctl: Handle network interface SIOCETHTOOL ioctls
+ * @dev: network interface on which out-of-band action is to be performed
+ * @useraddr: userspace address to which data is to be read and returned
+ *
+ * Process the various commands of the SIOCETHTOOL interface.
+ */
+
+static int netdev_ethtool_ioctl (struct net_device *dev, void *useraddr)
+{
+	u32 ethcmd;
+
+	/* dev_ioctl() in ../../net/core/dev.c has already checked
+	   capable(CAP_NET_ADMIN), so don't bother with that here.  */
+
+	if (get_user(ethcmd, (u32 *)useraddr))
+		return -EFAULT;
+
+	switch (ethcmd) {
+
+	case ETHTOOL_GDRVINFO: {
+		struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO };
+		strcpy (info.driver, DRV_NAME);
+		strcpy (info.version, DRV_VERSION);
+		sprintf(info.bus_info, "ISA 0x%lx", dev->base_addr);
+		if (copy_to_user (useraddr, &info, sizeof (info)))
+			return -EFAULT;
+		return 0;
+	}
+
+	default:
+		break;
+	}
+
+	return -EOPNOTSUPP;
+}
+
+/**
+ * netdev_ioctl: Handle network interface ioctls
+ * @dev: network interface on which out-of-band action is to be performed
+ * @rq: user request data
+ * @cmd: command issued by user
+ *
+ * Process the various out-of-band ioctls passed to this driver.
+ */
+
+static int netdev_ioctl (struct net_device *dev, struct ifreq *rq, int cmd)
+{
+	int rc = 0;
+
+	switch (cmd) {
+	case SIOCETHTOOL:
+		rc = netdev_ethtool_ioctl(dev, (void *) rq->ifr_data);
+		break;
+
+	default:
+		rc = -EOPNOTSUPP;
+		break;
+	}
+
+	return rc;
+}
+ 
+
 #ifdef MODULE
 #define MAX_EL2_CARDS	4	/* Max number of EL2 cards per module */
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/3c505.c linux/drivers/net/3c505.c
--- linux.orig/drivers/net/3c505.c	Mon Jan  7 23:41:09 2002
+++ linux/drivers/net/3c505.c	Fri Dec 21 18:50:47 2001
@@ -35,8 +35,13 @@
  *                      Philip Blundell <Philip.Blundell@pobox.com>
  *              Multicard/soft configurable dma channel/rev 2 hardware support
  *                      by Christopher Collins <ccollins@pcug.org.au>
+ *		Ethtool support (jgarzik), 11/17/2001
  */
 
+#define DRV_NAME	"3c505"
+#define DRV_VERSION	"1.10a"
+
+
 /* Theory of operation:
  *
  * The 3c505 is quite an intelligent board.  All communication with it is done
@@ -103,6 +108,9 @@
 #include <linux/slab.h>
 #include <linux/ioport.h>
 #include <linux/spinlock.h>
+#include <linux/ethtool.h>
+
+#include <asm/uaccess.h>
 #include <asm/bitops.h>
 #include <asm/io.h>
 #include <asm/dma.h>
@@ -148,10 +156,11 @@
  *********************************************************/
 
 #ifdef ELP_DEBUG
-static const int elp_debug = ELP_DEBUG;
+static int elp_debug = ELP_DEBUG;
 #else
-static const int elp_debug;
+static int elp_debug;
 #endif
+#define debug elp_debug
 
 /*
  *  0 = no messages (well, some)
@@ -1260,6 +1269,87 @@
 	}
 }
 
+/**
+ * netdev_ethtool_ioctl: Handle network interface SIOCETHTOOL ioctls
+ * @dev: network interface on which out-of-band action is to be performed
+ * @useraddr: userspace address to which data is to be read and returned
+ *
+ * Process the various commands of the SIOCETHTOOL interface.
+ */
+
+static int netdev_ethtool_ioctl (struct net_device *dev, void *useraddr)
+{
+	u32 ethcmd;
+
+	/* dev_ioctl() in ../../net/core/dev.c has already checked
+	   capable(CAP_NET_ADMIN), so don't bother with that here.  */
+
+	if (get_user(ethcmd, (u32 *)useraddr))
+		return -EFAULT;
+
+	switch (ethcmd) {
+
+	case ETHTOOL_GDRVINFO: {
+		struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO };
+		strcpy (info.driver, DRV_NAME);
+		strcpy (info.version, DRV_VERSION);
+		sprintf(info.bus_info, "ISA 0x%lx", dev->base_addr);
+		if (copy_to_user (useraddr, &info, sizeof (info)))
+			return -EFAULT;
+		return 0;
+	}
+
+	/* get message-level */
+	case ETHTOOL_GMSGLVL: {
+		struct ethtool_value edata = {ETHTOOL_GMSGLVL};
+		edata.data = debug;
+		if (copy_to_user(useraddr, &edata, sizeof(edata)))
+			return -EFAULT;
+		return 0;
+	}
+	/* set message-level */
+	case ETHTOOL_SMSGLVL: {
+		struct ethtool_value edata;
+		if (copy_from_user(&edata, useraddr, sizeof(edata)))
+			return -EFAULT;
+		debug = edata.data;
+		return 0;
+	}
+
+	default:
+		break;
+	}
+
+	return -EOPNOTSUPP;
+}
+
+/**
+ * netdev_ioctl: Handle network interface ioctls
+ * @dev: network interface on which out-of-band action is to be performed
+ * @rq: user request data
+ * @cmd: command issued by user
+ *
+ * Process the various out-of-band ioctls passed to this driver.
+ */
+
+static int netdev_ioctl (struct net_device *dev, struct ifreq *rq, int cmd)
+{
+	int rc = 0;
+
+	switch (cmd) {
+	case SIOCETHTOOL:
+		rc = netdev_ethtool_ioctl(dev, (void *) rq->ifr_data);
+		break;
+
+	default:
+		rc = -EOPNOTSUPP;
+		break;
+	}
+
+	return rc;
+}
+ 
+
 /******************************************************
  *
  * initialise Etherlink Plus board
@@ -1280,6 +1370,7 @@
 	dev->tx_timeout = elp_timeout;			/* local */
 	dev->watchdog_timeo = 10*HZ;
 	dev->set_multicast_list = elp_set_mc_list;	/* local */
+	dev->do_ioctl = netdev_ioctl;			/* local */
 
 	/* Setup the generic properties */
 	ether_setup(dev);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/3c507.c linux/drivers/net/3c507.c
--- linux.orig/drivers/net/3c507.c	Mon Jan  7 23:41:09 2002
+++ linux/drivers/net/3c507.c	Fri Dec 21 18:50:47 2001
@@ -25,8 +25,12 @@
 	The statistics need to be updated correctly.
 */
 
+#define DRV_NAME		"3c507"
+#define DRV_VERSION		"1.10a"
+#define DRV_RELDATE		"11/17/2001"
+
 static const char version[] =
-	"3c507.c:v1.10 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
+	DRV_NAME ".c:v" DRV_VERSION " " DRV_RELDATE " Donald Becker (becker@scyld.com)\n";
 
 
 #include <linux/module.h>
@@ -52,6 +56,9 @@
 #include <linux/in.h>
 #include <linux/string.h>
 #include <linux/spinlock.h>
+#include <linux/ethtool.h>
+
+#include <asm/uaccess.h>
 #include <asm/system.h>
 #include <asm/bitops.h>
 #include <asm/io.h>
@@ -70,6 +77,8 @@
 #define NET_DEBUG 1
 #endif
 static unsigned int net_debug = NET_DEBUG;
+#define debug net_debug
+
 
 /* A zero-terminated list of common I/O addresses to be probed. */
 static unsigned int netcard_portlist[] __initdata =
@@ -296,6 +305,7 @@
 
 static void hardware_send_packet(struct net_device *dev, void *buf, short length);
 static void init_82586_mem(struct net_device *dev);
+static int netdev_ioctl (struct net_device *dev, struct ifreq *rq, int cmd);
 
 
 /* Check for a network adaptor of this type, and return '0' iff one exists.
@@ -427,6 +437,7 @@
 	dev->get_stats	= el16_get_stats;
 	dev->tx_timeout = el16_tx_timeout;
 	dev->watchdog_timeo = TX_TIMEOUT;
+	dev->do_ioctl = netdev_ioctl;
 
 	ether_setup(dev);	/* Generic ethernet behaviour */
 
@@ -864,6 +875,88 @@
 	lp->rx_head = rx_head;
 	lp->rx_tail = rx_tail;
 }
+
+/**
+ * netdev_ethtool_ioctl: Handle network interface SIOCETHTOOL ioctls
+ * @dev: network interface on which out-of-band action is to be performed
+ * @useraddr: userspace address to which data is to be read and returned
+ *
+ * Process the various commands of the SIOCETHTOOL interface.
+ */
+
+static int netdev_ethtool_ioctl (struct net_device *dev, void *useraddr)
+{
+	u32 ethcmd;
+
+	/* dev_ioctl() in ../../net/core/dev.c has already checked
+	   capable(CAP_NET_ADMIN), so don't bother with that here.  */
+
+	if (get_user(ethcmd, (u32 *)useraddr))
+		return -EFAULT;
+
+	switch (ethcmd) {
+
+	case ETHTOOL_GDRVINFO: {
+		struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO };
+		strcpy (info.driver, DRV_NAME);
+		strcpy (info.version, DRV_VERSION);
+		sprintf(info.bus_info, "ISA 0x%lx", dev->base_addr);
+		if (copy_to_user (useraddr, &info, sizeof (info)))
+			return -EFAULT;
+		return 0;
+	}
+
+	/* get message-level */
+	case ETHTOOL_GMSGLVL: {
+		struct ethtool_value edata = {ETHTOOL_GMSGLVL};
+		edata.data = debug;
+		if (copy_to_user(useraddr, &edata, sizeof(edata)))
+			return -EFAULT;
+		return 0;
+	}
+	/* set message-level */
+	case ETHTOOL_SMSGLVL: {
+		struct ethtool_value edata;
+		if (copy_from_user(&edata, useraddr, sizeof(edata)))
+			return -EFAULT;
+		debug = edata.data;
+		return 0;
+	}
+
+	default:
+		break;
+	}
+
+	return -EOPNOTSUPP;
+}
+
+/**
+ * netdev_ioctl: Handle network interface ioctls
+ * @dev: network interface on which out-of-band action is to be performed
+ * @rq: user request data
+ * @cmd: command issued by user
+ *
+ * Process the various out-of-band ioctls passed to this driver.
+ */
+
+static int netdev_ioctl (struct net_device *dev, struct ifreq *rq, int cmd)
+{
+	int rc = 0;
+
+	switch (cmd) {
+	case SIOCETHTOOL:
+		rc = netdev_ethtool_ioctl(dev, (void *) rq->ifr_data);
+		break;
+
+	default:
+		rc = -EOPNOTSUPP;
+		break;
+	}
+
+	return rc;
+}
+ 
+
 #ifdef MODULE
 static struct net_device dev_3c507;
 static int io = 0x300;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/3c509.c linux/drivers/net/3c509.c
--- linux.orig/drivers/net/3c509.c	Mon Jan  7 23:41:09 2002
+++ linux/drivers/net/3c509.c	Fri Dec 21 18:50:47 2001
@@ -43,8 +43,14 @@
 		v1.18 12Mar2001 Andrew Morton <andrewm@uow.edu.au>
 			- Avoid bogus detect of 3c590's (Andrzej Krzysztofowicz)
 			- Reviewed against 1.18 from scyld.com
+		v1.18 17Nov2001 Jeff Garzik <jgarzik@mandrakesoft.com>
+			- ethtool support
 */
 
+#define DRV_NAME	"3c509"
+#define DRV_VERSION	"1.18a"
+#define DRV_RELDATE	"17Nov2001"
+
 /* A few values that may be tweaked. */
 
 /* Time in jiffies before concluding the transmitter is hung. */
@@ -70,12 +76,14 @@
 #include <linux/skbuff.h>
 #include <linux/delay.h>	/* for udelay() */
 #include <linux/spinlock.h>
+#include <linux/ethtool.h>
 
+#include <asm/uaccess.h>
 #include <asm/bitops.h>
 #include <asm/io.h>
 #include <asm/irq.h>
 
-static char versionA[] __initdata = "3c509.c:1.18 12Mar2001 becker@scyld.com\n";
+static char versionA[] __initdata = DRV_NAME ".c:" DRV_VERSION " " DRV_RELDATE "becker@scyld.com\n";
 static char versionB[] __initdata = "http://www.scyld.com/network/3c509.html\n";
 
 #ifdef EL3_DEBUG
@@ -84,6 +92,7 @@
 static int el3_debug = 2;
 #endif
 
+
 /* To minimize the size of the driver source I only define operating
    constants if they are used several times.  You'll need the manual
    anyway if you want to understand driver details. */
@@ -158,6 +167,7 @@
 static int el3_close(struct net_device *dev);
 static void set_multicast_list(struct net_device *dev);
 static void el3_tx_timeout (struct net_device *dev);
+static int netdev_ioctl (struct net_device *dev, struct ifreq *rq, int cmd);
 
 #ifdef CONFIG_MCA
 struct el3_mca_adapters_struct {
@@ -513,6 +523,7 @@
 	dev->set_multicast_list = &set_multicast_list;
 	dev->tx_timeout = el3_tx_timeout;
 	dev->watchdog_timeo = TX_TIMEOUT;
+	dev->do_ioctl = netdev_ioctl;
 
 	/* Fill in the generic fields of the device structure. */
 	ether_setup(dev);
@@ -1003,6 +1014,85 @@
 	return 0;
 }
 
+/**
+ * netdev_ethtool_ioctl: Handle network interface SIOCETHTOOL ioctls
+ * @dev: network interface on which out-of-band action is to be performed
+ * @useraddr: userspace address to which data is to be read and returned
+ *
+ * Process the various commands of the SIOCETHTOOL interface.
+ */
+
+static int netdev_ethtool_ioctl (struct net_device *dev, void *useraddr)
+{
+	u32 ethcmd;
+
+	/* dev_ioctl() in ../../net/core/dev.c has already checked
+	   capable(CAP_NET_ADMIN), so don't bother with that here.  */
+
+	if (get_user(ethcmd, (u32 *)useraddr))
+		return -EFAULT;
+
+	switch (ethcmd) {
+
+	case ETHTOOL_GDRVINFO: {
+		struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO };
+		strcpy (info.driver, DRV_NAME);
+		strcpy (info.version, DRV_VERSION);
+		if (copy_to_user (useraddr, &info, sizeof (info)))
+			return -EFAULT;
+		return 0;
+	}
+
+	/* get message-level */
+	case ETHTOOL_GMSGLVL: {
+		struct ethtool_value edata = {ETHTOOL_GMSGLVL};
+		edata.data = el3_debug;
+		if (copy_to_user(useraddr, &edata, sizeof(edata)))
+			return -EFAULT;
+		return 0;
+	}
+	/* set message-level */
+	case ETHTOOL_SMSGLVL: {
+		struct ethtool_value edata;
+		if (copy_from_user(&edata, useraddr, sizeof(edata)))
+			return -EFAULT;
+		el3_debug = edata.data;
+		return 0;
+	}
+
+	default:
+		break;
+	}
+
+	return -EOPNOTSUPP;
+}
+
+/**
+ * netdev_ioctl: Handle network interface ioctls
+ * @dev: network interface on which out-of-band action is to be performed
+ * @rq: user request data
+ * @cmd: command issued by user
+ *
+ * Process the various out-of-band ioctls passed to this driver.
+ */
+
+static int netdev_ioctl (struct net_device *dev, struct ifreq *rq, int cmd)
+{
+	int rc = 0;
+
+	switch (cmd) {
+	case SIOCETHTOOL:
+		rc = netdev_ethtool_ioctl(dev, (void *) rq->ifr_data);
+		break;
+
+	default:
+		rc = -EOPNOTSUPP;
+		break;
+	}
+
+	return rc;
+}
+ 
 #ifdef MODULE
 /* Parameters that may be passed into the module. */
 static int debug = -1;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/3c515.c linux/drivers/net/3c515.c
--- linux.orig/drivers/net/3c515.c	Mon Jan  7 23:41:09 2002
+++ linux/drivers/net/3c515.c	Fri Dec 21 18:50:47 2001
@@ -16,9 +16,17 @@
 	2/2/00- Added support for kernel-level ISAPnP 
 		by Stephen Frost <sfrost@snowman.net> and Alessandro Zummo
 	Cleaned up for 2.3.x/softnet by Jeff Garzik and Alan Cox.
+	
+	11/17/2001 - Added ethtool support (jgarzik)
+
 */
 
-static char *version = "3c515.c:v0.99-sn 2000/02/12 becker@cesdis.gsfc.nasa.gov and others\n";
+#define DRV_NAME		"3c515"
+#define DRV_VERSION		"0.99t"
+#define DRV_RELDATE		"17-Nov-2001"
+
+static char *version =
+DRV_NAME ".c:v" DRV_VERSION " " DRV_RELDATE " becker@scyld.com and others\n";
 
 #define CORKSCREW 1
 
@@ -63,6 +71,9 @@
 #include <linux/slab.h>
 #include <linux/interrupt.h>
 #include <linux/timer.h>
+#include <linux/ethtool.h>
+
+#include <asm/uaccess.h>
 #include <asm/bitops.h>
 #include <asm/io.h>
 #include <asm/dma.h>
@@ -393,6 +404,7 @@
 static void update_stats(int addr, struct net_device *dev);
 static struct net_device_stats *corkscrew_get_stats(struct net_device *dev);
 static void set_rx_mode(struct net_device *dev);
+static int netdev_ioctl (struct net_device *dev, struct ifreq *rq, int cmd);
 
 
 /* 
@@ -721,6 +733,7 @@
 	dev->stop = &corkscrew_close;
 	dev->get_stats = &corkscrew_get_stats;
 	dev->set_multicast_list = &set_rx_mode;
+	dev->do_ioctl = netdev_ioctl;
 
 	return 0;
 }
@@ -1591,6 +1604,87 @@
 
 	outw(new_mode, ioaddr + EL3_CMD);
 }
+
+/**
+ * netdev_ethtool_ioctl: Handle network interface SIOCETHTOOL ioctls
+ * @dev: network interface on which out-of-band action is to be performed
+ * @useraddr: userspace address to which data is to be read and returned
+ *
+ * Process the various commands of the SIOCETHTOOL interface.
+ */
+
+static int netdev_ethtool_ioctl (struct net_device *dev, void *useraddr)
+{
+	u32 ethcmd;
+
+	/* dev_ioctl() in ../../net/core/dev.c has already checked
+	   capable(CAP_NET_ADMIN), so don't bother with that here.  */
+
+	if (get_user(ethcmd, (u32 *)useraddr))
+		return -EFAULT;
+
+	switch (ethcmd) {
+
+	case ETHTOOL_GDRVINFO: {
+		struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO };
+		strcpy (info.driver, DRV_NAME);
+		strcpy (info.version, DRV_VERSION);
+		sprintf(info.bus_info, "ISA 0x%lx", dev->base_addr);
+		if (copy_to_user (useraddr, &info, sizeof (info)))
+			return -EFAULT;
+		return 0;
+	}
+
+	/* get message-level */
+	case ETHTOOL_GMSGLVL: {
+		struct ethtool_value edata = {ETHTOOL_GMSGLVL};
+		edata.data = corkscrew_debug;
+		if (copy_to_user(useraddr, &edata, sizeof(edata)))
+			return -EFAULT;
+		return 0;
+	}
+	/* set message-level */
+	case ETHTOOL_SMSGLVL: {
+		struct ethtool_value edata;
+		if (copy_from_user(&edata, useraddr, sizeof(edata)))
+			return -EFAULT;
+		corkscrew_debug = edata.data;
+		return 0;
+	}
+
+	default:
+		break;
+	}
+
+	return -EOPNOTSUPP;
+}
+
+/**
+ * netdev_ioctl: Handle network interface ioctls
+ * @dev: network interface on which out-of-band action is to be performed
+ * @rq: user request data
+ * @cmd: command issued by user
+ *
+ * Process the various out-of-band ioctls passed to this driver.
+ */
+
+static int netdev_ioctl (struct net_device *dev, struct ifreq *rq, int cmd)
+{
+	int rc = 0;
+
+	switch (cmd) {
+	case SIOCETHTOOL:
+		rc = netdev_ethtool_ioctl(dev, (void *) rq->ifr_data);
+		break;
+
+	default:
+		rc = -EOPNOTSUPP;
+		break;
+	}
+
+	return rc;
+}
+ 
 
 #ifdef MODULE
 void cleanup_module(void)
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/3c523.c linux/drivers/net/3c523.c
--- linux.orig/drivers/net/3c523.c	Mon Jan  7 23:41:09 2002
+++ linux/drivers/net/3c523.c	Fri Dec 21 18:50:47 2001
@@ -81,10 +81,15 @@
    added option to disable multicast as is causes problems
        Ganesh Sittampalam <ganesh.sittampalam@magdalen.oxford.ac.uk>
        Stuart Adamson <stuart.adamson@compsoc.net>
+   Nov 2001
+   added support for ethtool (jgarzik)
 	
    $Header: /fsys2/home/chrisb/linux-1.3.59-MCA/drivers/net/RCS/3c523.c,v 1.1 1996/02/05 01:53:46 chrisb Exp chrisb $
  */
 
+#define DRV_NAME		"3c523"
+#define DRV_VERSION		"17-Nov-2001"
+
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
@@ -95,6 +100,9 @@
 #include <linux/interrupt.h>
 #include <linux/delay.h>
 #include <linux/mca.h>
+#include <linux/ethtool.h>
+
+#include <asm/uaccess.h>
 #include <asm/processor.h>
 #include <asm/bitops.h>
 #include <asm/io.h>
@@ -182,6 +190,7 @@
 #ifdef ELMC_MULTICAST
 static void set_multicast_list(struct net_device *dev);
 #endif
+static int netdev_ioctl (struct net_device *dev, struct ifreq *rq, int cmd);
 
 /* helper-functions */
 static int init586(struct net_device *dev);
@@ -563,7 +572,8 @@
 #else
 	dev->set_multicast_list = NULL;
 #endif
-
+	dev->do_ioctl = netdev_ioctl;
+	
 	ether_setup(dev);
 
 	/* note that we haven't actually requested the IRQ from the kernel.
@@ -1214,6 +1224,69 @@
 }
 #endif
 
+/**
+ * netdev_ethtool_ioctl: Handle network interface SIOCETHTOOL ioctls
+ * @dev: network interface on which out-of-band action is to be performed
+ * @useraddr: userspace address to which data is to be read and returned
+ *
+ * Process the various commands of the SIOCETHTOOL interface.
+ */
+
+static int netdev_ethtool_ioctl (struct net_device *dev, void *useraddr)
+{
+	u32 ethcmd;
+
+	/* dev_ioctl() in ../../net/core/dev.c has already checked
+	   capable(CAP_NET_ADMIN), so don't bother with that here.  */
+
+	if (get_user(ethcmd, (u32 *)useraddr))
+		return -EFAULT;
+
+	switch (ethcmd) {
+
+	case ETHTOOL_GDRVINFO: {
+		struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO };
+		strcpy (info.driver, DRV_NAME);
+		strcpy (info.version, DRV_VERSION);
+		sprintf(info.bus_info, "MCA 0x%lx", dev->base_addr);
+		if (copy_to_user (useraddr, &info, sizeof (info)))
+			return -EFAULT;
+		return 0;
+	}
+
+	default:
+		break;
+	}
+
+	return -EOPNOTSUPP;
+}
+
+/**
+ * netdev_ioctl: Handle network interface ioctls
+ * @dev: network interface on which out-of-band action is to be performed
+ * @rq: user request data
+ * @cmd: command issued by user
+ *
+ * Process the various out-of-band ioctls passed to this driver.
+ */
+
+static int netdev_ioctl (struct net_device *dev, struct ifreq *rq, int cmd)
+{
+	int rc = 0;
+
+	switch (cmd) {
+	case SIOCETHTOOL:
+		rc = netdev_ethtool_ioctl(dev, (void *) rq->ifr_data);
+		break;
+
+	default:
+		rc = -EOPNOTSUPP;
+		break;
+	}
+
+	return rc;
+}
+ 
 /*************************************************************************/
 
 #ifdef MODULE
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/3c527.c linux/drivers/net/3c527.c
--- linux.orig/drivers/net/3c527.c	Mon Jan  7 23:41:09 2002
+++ linux/drivers/net/3c527.c	Fri Dec 21 18:50:47 2001
@@ -16,8 +16,12 @@
  *
  */
 
+#define DRV_NAME		"3c527"
+#define DRV_VERSION		"0.6a"
+#define DRV_RELDATE		"2001/11/17"
+
 static const char *version =
-	"3c527.c:v0.6 2001/03/03 Richard Proctor (rnp@netlink.co.nz)\n";
+DRV_NAME ".c:v" DRV_VERSION " " DRV_RELDATE " Richard Proctor (rnp@netlink.co.nz)\n";
 
 /**
  * DOC: Traps for the unwary
@@ -90,6 +94,9 @@
 #include <linux/in.h>
 #include <linux/slab.h>
 #include <linux/string.h>
+#include <linux/ethtool.h>
+
+#include <asm/uaccess.h>
 #include <asm/system.h>
 #include <asm/bitops.h>
 #include <asm/io.h>
@@ -108,7 +115,7 @@
  * The name of the card. Is used for messages and in the requests for
  * io regions, irqs and dma channels
  */
-static const char* cardname = "3c527";
+static const char* cardname = DRV_NAME;
 
 /* use 0 for production, 1 for verification, >2 for debug */
 #ifndef NET_DEBUG
@@ -213,6 +220,7 @@
 static struct	net_device_stats *mc32_get_stats(struct net_device *dev);
 static void	mc32_set_multicast_list(struct net_device *dev);
 static void	mc32_reset_multicast_list(struct net_device *dev);
+static int	netdev_ioctl (struct net_device *dev, struct ifreq *rq, int cmd);
 
 /**
  * mc32_probe 	-	Search for supported boards
@@ -502,7 +510,7 @@
 	dev->set_multicast_list = mc32_set_multicast_list;
 	dev->tx_timeout		= mc32_timeout;
 	dev->watchdog_timeo	= HZ*5;	/* Board does all the work */
-
+	dev->do_ioctl		= netdev_ioctl;
 	
 	lp->xceiver_state = HALTED; 
 	
@@ -1644,6 +1652,86 @@
 	do_mc32_set_multicast_list(dev,1);
 }
 
+/**
+ * netdev_ethtool_ioctl: Handle network interface SIOCETHTOOL ioctls
+ * @dev: network interface on which out-of-band action is to be performed
+ * @useraddr: userspace address to which data is to be read and returned
+ *
+ * Process the various commands of the SIOCETHTOOL interface.
+ */
+
+static int netdev_ethtool_ioctl (struct net_device *dev, void *useraddr)
+{
+	u32 ethcmd;
+
+	/* dev_ioctl() in ../../net/core/dev.c has already checked
+	   capable(CAP_NET_ADMIN), so don't bother with that here.  */
+
+	if (get_user(ethcmd, (u32 *)useraddr))
+		return -EFAULT;
+
+	switch (ethcmd) {
+
+	case ETHTOOL_GDRVINFO: {
+		struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO };
+		strcpy (info.driver, DRV_NAME);
+		strcpy (info.version, DRV_VERSION);
+		sprintf(info.bus_info, "MCA 0x%lx", dev->base_addr);
+		if (copy_to_user (useraddr, &info, sizeof (info)))
+			return -EFAULT;
+		return 0;
+	}
+
+	/* get message-level */
+	case ETHTOOL_GMSGLVL: {
+		struct ethtool_value edata = {ETHTOOL_GMSGLVL};
+		edata.data = mc32_debug;
+		if (copy_to_user(useraddr, &edata, sizeof(edata)))
+			return -EFAULT;
+		return 0;
+	}
+	/* set message-level */
+	case ETHTOOL_SMSGLVL: {
+		struct ethtool_value edata;
+		if (copy_from_user(&edata, useraddr, sizeof(edata)))
+			return -EFAULT;
+		mc32_debug = edata.data;
+		return 0;
+	}
+
+	default:
+		break;
+	}
+
+	return -EOPNOTSUPP;
+}
+
+/**
+ * netdev_ioctl: Handle network interface ioctls
+ * @dev: network interface on which out-of-band action is to be performed
+ * @rq: user request data
+ * @cmd: command issued by user
+ *
+ * Process the various out-of-band ioctls passed to this driver.
+ */
+
+static int netdev_ioctl (struct net_device *dev, struct ifreq *rq, int cmd)
+{
+	int rc = 0;
+
+	switch (cmd) {
+	case SIOCETHTOOL:
+		rc = netdev_ethtool_ioctl(dev, (void *) rq->ifr_data);
+		break;
+
+	default:
+		rc = -EOPNOTSUPP;
+		break;
+	}
+
+	return rc;
+}
+ 
 #ifdef MODULE
 
 static struct net_device this_device;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/Config.in linux/drivers/net/Config.in
--- linux.orig/drivers/net/Config.in	Mon Jan  7 23:41:09 2002
+++ linux/drivers/net/Config.in	Wed Dec 26 16:34:02 2001
@@ -34,11 +34,15 @@
    if [ "$CONFIG_PPC" = "y" ]; then
       dep_tristate '  MACE (Power Mac ethernet) support' CONFIG_MACE $CONFIG_ALL_PPC
       if [ "$CONFIG_MACE" != "n" ]; then
-        bool '    Use AAUI port instead of TP by default' CONFIG_MACE_AAUI_PORT
+	 bool '    Use AAUI port instead of TP by default' CONFIG_MACE_AAUI_PORT
       fi
       dep_tristate '  BMAC (G3 ethernet) support' CONFIG_BMAC $CONFIG_ALL_PPC
       dep_tristate '  GMAC (G4/iBook ethernet) support' CONFIG_GMAC $CONFIG_ALL_PPC
-      tristate '  National DP83902AV (Oak ethernet) support' CONFIG_OAKNET
+      if [ "$CONFIG_4xx" = "y" ]; then
+         if [ "$CONFIG_STB03xxx" = "y" -o "$CONFIG_403GCX" = "y" ]; then
+	    tristate '  National DP83902AV (Oak ethernet) support' CONFIG_OAKNET
+	 fi
+      fi
    fi
    if [ "$CONFIG_ZORRO" = "y" ]; then
       tristate '  Ariadne support' CONFIG_ARIADNE
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/acenic.c linux/drivers/net/acenic.c
--- linux.orig/drivers/net/acenic.c	Mon Jan  7 23:41:09 2002
+++ linux/drivers/net/acenic.c	Fri Dec 21 18:25:01 2001
@@ -208,6 +208,8 @@
 	(((u64)(mask) & 0xffffffff00000000) == 0 ? 0 : -EIO)
 #define pci_dma_supported(dev, mask)		\
 	(((u64)(mask) & 0xffffffff00000000) == 0 ? 1 : 0)
+#define DECLARE_PCI_UNMAP_ADDR(ADDR_NAME)
+#define DECLARE_PCI_UNMAP_LEN(LEN_NAME)
 
 #elif (LINUX_VERSION_CODE < 0x02040d)
 
@@ -231,6 +233,8 @@
 }
 #define pci_unmap_page(cookie, dma_addr, size, dir)	\
 	pci_unmap_single(cookie, dma_addr, size, dir)
+#define DECLARE_PCI_UNMAP_ADDR(ADDR_NAME)
+#define DECLARE_PCI_UNMAP_LEN(LEN_NAME)
 #endif
 
 
@@ -839,9 +843,11 @@
 			struct sk_buff *skb = ap->skb->rx_std_skbuff[i].skb;
 
 			if (skb) {
+				struct ring_info *ringp;
 				dma_addr_t mapping;
 
-				mapping = ap->skb->rx_std_skbuff[i].mapping;
+				ringp = &ap->skb->rx_std_skbuff[i];
+				mapping = PCI_UNMAP_ADDR(ringp, mapping);
 				pci_unmap_page(ap->pdev, mapping,
 					       ACE_STD_BUFSIZE - (2 + 16),
 					       PCI_DMA_FROMDEVICE);
@@ -856,9 +862,11 @@
 				struct sk_buff *skb = ap->skb->rx_mini_skbuff[i].skb;
 
 				if (skb) {
+					struct ring_info *ringp;
 					dma_addr_t mapping;
 
-					mapping = ap->skb->rx_mini_skbuff[i].mapping;
+					ringp = &ap->skb->rx_mini_skbuff[i];
+					mapping = PCI_UNMAP_ADDR(ringp,mapping);
 					pci_unmap_page(ap->pdev, mapping,
 						       ACE_MINI_BUFSIZE - (2 + 16),
 						       PCI_DMA_FROMDEVICE);
@@ -872,9 +880,11 @@
 		for (i = 0; i < RX_JUMBO_RING_ENTRIES; i++) {
 			struct sk_buff *skb = ap->skb->rx_jumbo_skbuff[i].skb;
 			if (skb) {
+				struct ring_info *ringp;
 				dma_addr_t mapping;
 
-				mapping = ap->skb->rx_jumbo_skbuff[i].mapping;
+				ringp = &ap->skb->rx_jumbo_skbuff[i];
+				mapping = PCI_UNMAP_ADDR(ringp, mapping);
 				pci_unmap_page(ap->pdev, mapping,
 					       ACE_JUMBO_BUFSIZE - (2 + 16),
 					       PCI_DMA_FROMDEVICE);
@@ -1832,7 +1842,8 @@
 				       ACE_STD_BUFSIZE - (2 + 16),
 				       PCI_DMA_FROMDEVICE);
 		ap->skb->rx_std_skbuff[idx].skb = skb;
-		ap->skb->rx_std_skbuff[idx].mapping = mapping;
+		PCI_UNMAP_ADDR_SET(&ap->skb->rx_std_skbuff[idx],
+				   mapping, mapping);
 
 		rd = &ap->rx_std_ring[idx];
 		set_aceaddr(&rd->addr, mapping);
@@ -1897,7 +1908,8 @@
 				       ACE_MINI_BUFSIZE - (2 + 16),
 				       PCI_DMA_FROMDEVICE);
 		ap->skb->rx_mini_skbuff[idx].skb = skb;
-		ap->skb->rx_mini_skbuff[idx].mapping = mapping;
+		PCI_UNMAP_ADDR_SET(&ap->skb->rx_mini_skbuff[idx],
+				   mapping, mapping);
 
 		rd = &ap->rx_mini_ring[idx];
 		set_aceaddr(&rd->addr, mapping);
@@ -1957,7 +1969,8 @@
 				       ACE_JUMBO_BUFSIZE - (2 + 16),
 				       PCI_DMA_FROMDEVICE);
 		ap->skb->rx_jumbo_skbuff[idx].skb = skb;
-		ap->skb->rx_jumbo_skbuff[idx].mapping = mapping;
+		PCI_UNMAP_ADDR_SET(&ap->skb->rx_jumbo_skbuff[idx],
+				   mapping, mapping);
 
 		rd = &ap->rx_jumbo_ring[idx];
 		set_aceaddr(&rd->addr, mapping);
@@ -2163,7 +2176,9 @@
 
 		skb = rip->skb;
 		rip->skb = NULL;
-		pci_unmap_page(ap->pdev, rip->mapping, mapsize,
+		pci_unmap_page(ap->pdev,
+			       PCI_UNMAP_ADDR(rip, mapping),
+			       mapsize,
 			       PCI_DMA_FROMDEVICE);
 		skb_put(skb, retdesc->size);
 
@@ -2229,12 +2244,13 @@
 
 		info = ap->skb->tx_skbuff + idx;
 		skb = info->skb;
-		mapping = info->mapping;
+		mapping = PCI_UNMAP_ADDR(info, mapping);
 
 		if (mapping) {
-			pci_unmap_page(ap->pdev, mapping, info->maplen,
+			pci_unmap_page(ap->pdev, mapping,
+				       PCI_UNMAP_LEN(info, maplen),
 				       PCI_DMA_TODEVICE);
-			info->mapping = 0;
+			PCI_UNMAP_ADDR_SET(info, mapping, 0);
 		}
 
 		if (skb) {
@@ -2518,13 +2534,14 @@
 
 		info = ap->skb->tx_skbuff + i;
 		skb = info->skb;
-		mapping = info->mapping;
+		mapping = PCI_UNMAP_ADDR(info, mapping);
 
 		if (mapping) {
 			memset(ap->tx_ring + i, 0, sizeof(struct tx_desc));
-			pci_unmap_page(ap->pdev, mapping, info->maplen,
+			pci_unmap_page(ap->pdev, mapping,
+				       PCI_UNMAP_LEN(info, maplen),
 				       PCI_DMA_TODEVICE);
-			info->mapping = 0;
+			PCI_UNMAP_ADDR_SET(info, mapping, 0);
 		}
 		if (skb) {
 			dev_kfree_skb(skb);
@@ -2545,7 +2562,6 @@
 	return 0;
 }
 
-
 static inline dma_addr_t
 ace_map_tx_skb(struct ace_private *ap, struct sk_buff *skb,
 	       struct sk_buff *tail, u32 idx)
@@ -2559,8 +2575,8 @@
 
 	info = ap->skb->tx_skbuff + idx;
 	info->skb = tail;
-	info->mapping = mapping;
-	info->maplen = skb->len;
+	PCI_UNMAP_ADDR_SET(info, mapping, mapping);
+	PCI_UNMAP_LEN_SET(info, maplen, skb->len);
 	return mapping;
 }
 
@@ -2661,8 +2677,8 @@
 			} else {
 				info->skb = NULL;
 			}
-			info->mapping = mapping;
-			info->maplen = frag->size;
+			PCI_UNMAP_ADDR_SET(info, mapping, mapping);
+			PCI_UNMAP_LEN_SET(info, maplen, frag->size);
 			ace_load_tx_bd(desc, mapping, flagsize);
 		}
 	}
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/acenic.h linux/drivers/net/acenic.h
--- linux.orig/drivers/net/acenic.h	Mon Jan  7 23:41:09 2002
+++ linux/drivers/net/acenic.h	Fri Dec 21 18:25:01 2001
@@ -585,7 +585,7 @@
 
 struct ring_info {
 	struct sk_buff		*skb;
-	dma_addr_t		mapping;
+	DECLARE_PCI_UNMAP_ADDR(mapping)
 };
 
 
@@ -596,8 +596,8 @@
  */
 struct tx_ring_info {
 	struct sk_buff		*skb;
-	dma_addr_t		mapping;
-	int			maplen;
+	DECLARE_PCI_UNMAP_ADDR(mapping)
+	DECLARE_PCI_UNMAP_LEN(maplen)
 };
 
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/bmac.c linux/drivers/net/bmac.c
--- linux.orig/drivers/net/bmac.c	Mon Jan  7 23:41:09 2002
+++ linux/drivers/net/bmac.c	Wed Dec 26 16:34:02 2001
@@ -22,7 +22,8 @@
 #include <asm/io.h>
 #include <asm/page.h>
 #include <asm/pgtable.h>
-#include <asm/feature.h>
+#include <asm/machdep.h>
+#include <asm/pmac_feature.h>
 #ifdef CONFIG_PMAC_PBOOK
 #include <linux/adb.h>
 #include <linux/pmu.h>
@@ -155,7 +156,7 @@
 static void bmac_start_chip(struct net_device *dev);
 static void bmac_init_chip(struct net_device *dev);
 static void bmac_init_registers(struct net_device *dev);
-static void bmac_reset_chip(struct net_device *dev);
+static void bmac_enable_and_reset_chip(struct net_device *dev);
 static int bmac_set_address(struct net_device *dev, void *addr);
 static void bmac_misc_intr(int irq, void *dev_id, struct pt_regs *regs);
 static void bmac_txdma_intr(int irq, void *dev_id, struct pt_regs *regs);
@@ -229,21 +230,18 @@
 }
 
 static void
-bmac_reset_chip(struct net_device *dev)
+bmac_enable_and_reset_chip(struct net_device *dev)
 {
 	struct bmac_data *bp = (struct bmac_data *) dev->priv;
 	volatile struct dbdma_regs *rd = bp->rx_dma;
 	volatile struct dbdma_regs *td = bp->tx_dma;
 
-	dbdma_reset(rd);
-	dbdma_reset(td);
+	if (rd)
+		dbdma_reset(rd);
+	if (td)
+		dbdma_reset(td);
 
-	feature_set(bp->node, FEATURE_BMac_IO_enable);
-	udelay(10000);
-	feature_set(bp->node, FEATURE_BMac_reset);
-	udelay(10000);
-	feature_clear(bp->node, FEATURE_BMac_reset);
-	udelay(10000);
+	pmac_call_feature(PMAC_FTR_BMAC_ENABLE, bp->node, 0, 1);
 }
 
 #define MIFDELAY	udelay(10)
@@ -522,10 +520,7 @@
 				}
 			}
 		}
-		feature_set(bp->node, FEATURE_BMac_reset);
-		mdelay(10);
-		feature_clear(bp->node, FEATURE_BMac_IO_enable);
-		mdelay(10);
+		pmac_call_feature(PMAC_FTR_BMAC_ENABLE, bp->node, 0, 0);
 		break;
 	case PBOOK_WAKE:
 		/* see if this is enough */
@@ -1267,7 +1262,7 @@
 	unsigned char *data;
 
 	save_flags(flags); cli();
-	bmac_reset_chip(dev);
+	bmac_enable_and_reset_chip(dev);
 	bmac_init_tx_ring(bp);
 	bmac_init_rx_ring(bp);
 	bmac_init_chip(dev);
@@ -1350,14 +1345,30 @@
 		       bmac->full_name);
 		return;
 	}
+	bp = (struct bmac_data *) dev->priv;
 	SET_MODULE_OWNER(dev);
+	bp->node = bmac;
 
+	if (!request_OF_resource(bmac, 0, " (bmac)")) {
+		printk(KERN_ERR "BMAC: can't request IO resource !\n");
+		goto err_out;
+	}
+	if (!request_OF_resource(bmac, 1, " (bmac tx dma)")) {
+		printk(KERN_ERR "BMAC: can't request TX DMA resource !\n");
+		goto err_out;
+	}
+
+	if (!request_OF_resource(bmac, 2, " (bmac rx dma)")) {
+		printk(KERN_ERR "BMAC: can't request RX DMA resource !\n");
+		goto err_out;
+	}
 	dev->base_addr = (unsigned long)
 		ioremap(bmac->addrs[0].address, bmac->addrs[0].size);
 	if (!dev->base_addr)
 		goto err_out;
 	dev->irq = bmac->intrs[0].line;
 
+	bmac_enable_and_reset_chip(dev);
 	bmwrite(dev, INTDISABLE, DisableAll);
 
 	printk(KERN_INFO "%s: BMAC%s at", dev->name, (is_bmac_plus? "+": ""));
@@ -1369,6 +1380,10 @@
 	XXDEBUG((", base_addr=%#0lx", dev->base_addr));
 	printk("\n");
 
+	/* Enable chip without interrupts for now */
+	bmac_enable_and_reset_chip(dev);
+	bmwrite(dev, INTDISABLE, DisableAll);
+
 	dev->open = bmac_open;
 	dev->stop = bmac_close;
 	dev->hard_start_xmit = bmac_output;
@@ -1380,7 +1395,6 @@
 	if (bmac_verify_checksum(dev) != 0)
 		goto err_out_iounmap;
 
-	bp = (struct bmac_data *) dev->priv;
 	bp->is_bmac_plus = is_bmac_plus;
 	bp->tx_dma = (volatile struct dbdma_regs *)
 		ioremap(bmac->addrs[1].address, bmac->addrs[1].size);
@@ -1399,7 +1413,6 @@
 	bp->queue = (struct sk_buff_head *)(bp->rx_cmds + N_RX_RING + 1);
 	skb_queue_head_init(bp->queue);
 
-	bp->node = bmac;
 	memset((char *) bp->tx_cmds, 0,
 	       (N_TX_RING + N_RX_RING + 2) * sizeof(struct dbdma_cmd));
 	/*     init_timer(&bp->tx_timeout); */
@@ -1421,6 +1434,12 @@
 		goto err_out_irq1;
 	}
 
+	/* Mask chip interrupts and disable chip, will be
+	 * re-enabled on open()
+	 */
+	disable_irq(dev->irq);
+	pmac_call_feature(PMAC_FTR_BMAC_ENABLE, bp->node, 0, 0);
+	
 	bp->next_bmac = bmac_devs;
 	bmac_devs = dev;
 	return;
@@ -1436,6 +1455,12 @@
 err_out_iounmap:
 	iounmap((void *)dev->base_addr);
 err_out:
+	if (bp->node) {
+		release_OF_resource(bp->node, 0);
+		release_OF_resource(bp->node, 1);
+		release_OF_resource(bp->node, 2);
+		pmac_call_feature(PMAC_FTR_BMAC_ENABLE, bp->node, 0, 0);
+	}
 	unregister_netdev(dev);
 	kfree(dev);
 }
@@ -1447,6 +1472,7 @@
 	/* reset the chip */
 	bp->opened = 1;
 	bmac_reset_and_enable(dev);
+	enable_irq(dev->irq);
 	dev->flags |= IFF_RUNNING;
 	return 0;
 }
@@ -1459,6 +1485,7 @@
 	unsigned short config;
 	int i;
 
+	bp->sleeping = 1;
 	dev->flags &= ~(IFF_UP | IFF_RUNNING);
 
 	/* disable rx and tx */
@@ -1492,6 +1519,8 @@
 	XXDEBUG(("bmac: all bufs freed\n"));
 
 	bp->opened = 0;
+	disable_irq(dev->irq);
+	pmac_call_feature(PMAC_FTR_BMAC_ENABLE, bp->node, 0, 0);
 
 	return 0;
 }
@@ -1561,7 +1590,7 @@
 	bmwrite(dev, TXCFG, (config & ~TxMACEnable));
 	out_le32(&td->control, DBDMA_CLEAR(RUN|PAUSE|FLUSH|WAKE|ACTIVE|DEAD));
 	printk(KERN_ERR "bmac: transmit timeout - resetting\n");
-	bmac_reset_chip(dev);
+	bmac_enable_and_reset_chip(dev);
 
 	/* restart rx dma */
 	cp = bus_to_virt(ld_le32(&rd->cmdptr));
@@ -1683,11 +1712,15 @@
 		bp = (struct bmac_data *) dev->priv;
 		bmac_devs = bp->next_bmac;
 
+		unregister_netdev(dev);
+
+		release_OF_resource(bp->node, 0);
+		release_OF_resource(bp->node, 1);
+		release_OF_resource(bp->node, 2);
 		free_irq(dev->irq, dev);
 		free_irq(bp->tx_dma_intr, dev);
 		free_irq(bp->rx_dma_intr, dev);
 
-		unregister_netdev(dev);
 		kfree(dev);
 	} while (bmac_devs != NULL);
 }
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/de4x5.c linux/drivers/net/de4x5.c
--- linux.orig/drivers/net/de4x5.c	Sun Sep 30 19:26:06 2001
+++ linux/drivers/net/de4x5.c	Mon Jan  7 23:28:58 2002
@@ -3652,7 +3652,7 @@
     tmp = virt_to_bus(p->data);
     i = ((tmp + ALIGN) & ~ALIGN) - tmp;
     skb_reserve(p, i);
-    lp->rx_ring[index].buf = tmp + i;
+    lp->rx_ring[index].buf = cpu_to_le32(tmp + i);
 
     ret = lp->rx_skb[index];
     lp->rx_skb[index] = p;
@@ -5623,7 +5623,7 @@
 	if (!capable(CAP_NET_ADMIN)) return -EPERM;
 	omr = inl(DE4X5_OMR);
 	omr &= ~OMR_PR;
-	outb(omr, DE4X5_OMR);
+	outl(omr, DE4X5_OMR);
 	dev->flags &= ~IFF_PROMISC;
 	break;
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/dl2k.c linux/drivers/net/dl2k.c
--- linux.orig/drivers/net/dl2k.c	Mon Jan  7 23:41:09 2002
+++ linux/drivers/net/dl2k.c	Fri Dec 21 18:30:14 2001
@@ -15,18 +15,22 @@
     0.01	2001/05/03	Created DL2000-based linux driver
     0.02	2001/05/21	Added VLAN and hardware checksum support.
     1.00	2001/06/26	Added jumbo frame support.
-    1.01	2001/08/21	Added two parameters, int_count and int_timeout.
+    1.01	2001/08/21	Added two parameters, rx_coalesce and rx_timeout.
     1.02	2001/10/08	Supported fiber media.
     				Added flow control parameters.
-    1.03	2001/10/12	Changed the default media to 1000mbps_fd for the 
-    				fiber devices.
-    1.04	2001/11/08	Fixed a bug which Tx stop when a very busy case.
+    1.03	2001/10/12	Changed the default media to 1000mbps_fd for 
+    				the fiber devices.
+    1.04	2001/11/08	Fixed Tx stopped when tx very busy.
+    1.05	2001/11/22	Fixed Tx stopped when unidirectional tx busy.
+    1.06	2001/12/13	Fixed disconnect bug at 10Mbps mode.
+    				Fixed tx_full flag incorrect.
+				Added tx_coalesce paramter.
 */
 
 #include "dl2k.h"
 
 static char version[] __devinitdata =
-    KERN_INFO "D-Link DL2000-based linux driver v1.04 2001/11/08\n";
+    KERN_INFO "D-Link DL2000-based linux driver v1.06 2001/12/13\n";
 
 #define MAX_UNITS 8
 static int mtu[MAX_UNITS];
@@ -36,11 +40,13 @@
 static int tx_flow[MAX_UNITS];
 static int rx_flow[MAX_UNITS];
 static int copy_thresh;
-static int int_count;		/* Rx frame count each interrupt */
-static int int_timeout;		/* Rx DMA wait time in 64ns increments */
+static int rx_coalesce = 5;	/* Rx frame count each interrupt */
+static int rx_timeout = 750;	/* Rx DMA wait time in 64ns increments */
+static int tx_coalesce = 8;	/* HW xmit count each TxComplete */
 
 MODULE_AUTHOR ("Edward Peng");
 MODULE_DESCRIPTION ("D-Link DL2000-based Gigabit Ethernet Adapter");
+MODULE_LICENSE("GPL");
 MODULE_PARM (mtu, "1-" __MODULE_STRING (MAX_UNITS) "i");
 MODULE_PARM (media, "1-" __MODULE_STRING (MAX_UNITS) "s");
 MODULE_PARM (vlan, "1-" __MODULE_STRING (MAX_UNITS) "i");
@@ -48,13 +54,15 @@
 MODULE_PARM (tx_flow, "1-" __MODULE_STRING (MAX_UNITS) "i");
 MODULE_PARM (rx_flow, "1-" __MODULE_STRING (MAX_UNITS) "i");
 MODULE_PARM (copy_thresh, "i");
-MODULE_PARM (int_count, "i");
-MODULE_PARM (int_timeout, "i");
+MODULE_PARM (rx_coalesce, "i");
+MODULE_PARM (rx_timeout, "i");
+MODULE_PARM (tx_coalesce, "i");
 
 /* Enable the default interrupts */
+#define DEFAULT_INTR (RxDMAComplete | HostError | IntRequested | TxComplete| \
+       UpdateStats | LinkEvent)
 #define EnableInt() \
-writew(RxDMAComplete | HostError | IntRequested | TxComplete| \
-       UpdateStats | LinkEvent, ioaddr + IntEnable)
+writew(DEFAULT_INTR, ioaddr + IntEnable)
 
 static int max_intrloop = 50;
 static int multicast_filter_limit = 0x40;
@@ -163,11 +171,11 @@
 				np->speed = 10;
 				np->full_duplex = 0;
 			} else if (strcmp (media[card_idx], "1000mbps_fd") == 0 ||
-				 strcmp (media[card_idx], "5") == 0) {
+				 strcmp (media[card_idx], "6") == 0) {
 				np->speed=1000;
 				np->full_duplex=1;
 			} else if (strcmp (media[card_idx], "1000mbps_hd") == 0 ||
-				 strcmp (media[card_idx], "6") == 0) {
+				 strcmp (media[card_idx], "5") == 0) {
 				np->speed = 1000;
 				np->full_duplex = 0;
 			} else {
@@ -176,7 +184,7 @@
 		}
 		if (jumbo[card_idx] != 0) {
 			np->jumbo = 1;
-			dev->mtu = 9000;
+			dev->mtu = MAX_JUMBO;
 		} else {
 			np->jumbo = 0;
 			if (mtu[card_idx] > 0 && mtu[card_idx] < PACKET_SIZE)
@@ -184,14 +192,15 @@
 		}
 		np->vlan = (vlan[card_idx] > 0 && vlan[card_idx] < 4096) ?
 		    vlan[card_idx] : 0;
-		if (int_count != 0 && int_timeout != 0) {
-			np->int_count = int_count;
-			np->int_timeout = int_timeout;
+		if (rx_coalesce != 0 && rx_timeout != 0) {
+			np->rx_coalesce = rx_coalesce;
+			np->rx_timeout = rx_timeout;
 			np->coalesce = 1;
 		}
 		np->tx_flow = (tx_flow[card_idx]) ? 1 : 0;
 		np->rx_flow = (rx_flow[card_idx]) ? 1 : 0;
-		
+		if (tx_coalesce < 1)
+			tx_coalesce = 1;
 	}
 	dev->open = &rio_open;
 	dev->hard_start_xmit = &start_xmit;
@@ -202,8 +211,8 @@
 	dev->tx_timeout = &tx_timeout;
 	dev->watchdog_timeo = TX_TIMEOUT;
 	dev->change_mtu = &change_mtu;
-#ifdef TX_HW_CHECKSUM
-	dev->features = NETIF_F_SG | NETIF_F_HW_CSUM;
+#if 0
+	dev->features = NETIF_F_IP_CSUM;
 #endif
 	pci_set_drvdata (pdev, dev);
 
@@ -389,13 +398,12 @@
 	i = request_irq (dev->irq, &rio_interrupt, SA_SHIRQ, dev->name, dev);
 	if (i)
 		return i;
-
 	/* DebugCtrl bit 4, 5, 9 must set */
 	writel (readl (ioaddr + DebugCtrl) | 0x0230, ioaddr + DebugCtrl);
 
 	/* Jumbo frame */
 	if (np->jumbo != 0)
-		writew (9014, ioaddr + MaxFrameSize);
+		writew (MAX_JUMBO+14, ioaddr + MaxFrameSize);
 
 	alloc_list (dev);
 
@@ -405,7 +413,7 @@
 
 	set_multicast (dev);
 	if (np->coalesce) {
-		writel (np->int_count | np->int_timeout << 16,
+		writel (np->rx_coalesce | np->rx_timeout << 16,
 			ioaddr + RxDMAIntCtrl);
 	}
 	/* Set RIO to poll every N*320nsec. */
@@ -442,13 +450,31 @@
 	struct netdev_private *np = dev->priv;
 	long ioaddr = dev->base_addr;
 
-	printk (KERN_WARNING "%s: Transmit timed out, TxStatus %4.4x.\n",
+	printk (KERN_INFO "%s: Tx timed out (%4.4x), is buffer full?\n",
 		dev->name, readl (ioaddr + TxStatus));
+	/* Free used tx skbuffs */
+	for (; np->cur_tx - np->old_tx > 0; np->old_tx++) {
+		int entry = np->old_tx % TX_RING_SIZE;
+		struct sk_buff *skb;
+
+		if (!(np->tx_ring[entry].status & TFDDone))
+			break;
+		skb = np->tx_skbuff[entry];
+		pci_unmap_single (np->pdev,
+				  np->tx_ring[entry].fraginfo,
+				  skb->len, PCI_DMA_TODEVICE);
+		dev_kfree_skb_irq (skb);
+		np->tx_skbuff[entry] = 0;
+	}
 	dev->if_port = 0;
 	dev->trans_start = jiffies;
 	np->stats.tx_errors++;
-	if (!np->tx_full)
+	/* If the ring is no longer full, clear tx_full and 
+	   call netif_wake_queue() */
+	if (np->tx_full && np->cur_tx - np->old_tx < TX_QUEUE_LEN - 1) {
+		np->tx_full = 0;
 		netif_wake_queue (dev);
+	}
 }
 
  /* allocate and initialize Tx and Rx descriptors */
@@ -466,16 +492,19 @@
 	/* Initialize Tx descriptors, TFDListPtr leaves in start_xmit(). */
 	for (i = 0; i < TX_RING_SIZE; i++) {
 		np->tx_skbuff[i] = 0;
-		np->tx_ring[i].status = 0;
+		np->tx_ring[i].status = cpu_to_le64 (TFDDone);
+		np->tx_ring[i].next_desc = cpu_to_le64 (np->tx_ring_dma +
+					      ((i+1)%TX_RING_SIZE) *
+					      sizeof (struct
+					      netdev_desc));
 	}
 
 	/* Initialize Rx descriptors */
 	for (i = 0; i < RX_RING_SIZE; i++) {
 		np->rx_ring[i].next_desc = cpu_to_le64 (np->rx_ring_dma +
-							((i +
-							  1) % RX_RING_SIZE) *
-							sizeof (struct
-								netdev_desc));
+						((i + 1) % RX_RING_SIZE) *
+						sizeof (struct
+						netdev_desc));
 		np->rx_ring[i].status = 0;
 		np->rx_ring[i].fraginfo = 0;
 		np->rx_skbuff[i] = 0;
@@ -523,13 +552,12 @@
 	entry = np->cur_tx % TX_RING_SIZE;
 	np->tx_skbuff[entry] = skb;
 	txdesc = &np->tx_ring[entry];
-	txdesc->next_desc = 0;
 
 	/* Set TFDDone to avoid TxDMA gather this descriptor */
 	txdesc->status = cpu_to_le64 (TFDDone);
 	txdesc->status |=
 	    cpu_to_le64 (entry | WordAlignDisable | (1 << FragCountShift));
-#ifdef TX_HW_CHECKSUM
+#if 0
 	if (skb->ip_summed == CHECKSUM_HW) {
 		txdesc->status |=
 		    cpu_to_le64 (TCPChecksumEnable | UDPChecksumEnable |
@@ -545,21 +573,13 @@
 
 	/* Send one packet each time at 10Mbps mode */
 	/* Tx coalescing loop do not exceed 8 */
-	if (entry % 0x08 == 0 || np->speed == 10)
+	if (entry % tx_coalesce == 0 || np->speed == 10)
 		txdesc->status |= cpu_to_le64 (TxIndicate);
 	txdesc->fraginfo = cpu_to_le64 (pci_map_single (np->pdev, skb->data,
 							skb->len,
 							PCI_DMA_TODEVICE));
 	txdesc->fraginfo |= cpu_to_le64 (skb->len) << 48;
 
-	/* Chain the last descriptor's pointer to this one */
-	if (np->last_tx)
-		np->last_tx->next_desc = cpu_to_le64 (np->tx_ring_dma +
-						      entry *
-						      sizeof (struct
-							      netdev_desc));
-	np->last_tx = txdesc;
-
 	/* Clear TFDDone, then TxDMA start to send this descriptor */
 	txdesc->status &= ~cpu_to_le64 (TFDDone);
 
@@ -571,8 +591,10 @@
 	if (np->cur_tx - np->old_tx < TX_QUEUE_LEN - 1 && np->speed != 10) {
 		/* do nothing */
 	} else {
+		spin_lock_irqsave(&np->lock, flags);
 		np->tx_full = 1;
 		netif_stop_queue (dev);
+		spin_unlock_irqrestore (&np->lock, flags);
 	}
 
 	/* The first TFDListPtr */
@@ -581,15 +603,15 @@
 			dev->base_addr + TFDListPtr0);
 		writel (0, dev->base_addr + TFDListPtr1);
 	}
-
-	spin_lock_irqsave (&np->lock, flags);
+	
 	if (np->old_tx > TX_RING_SIZE) {
+		spin_lock_irqsave (&np->lock, flags);
 		tx_shift = TX_RING_SIZE;
 		np->old_tx -= tx_shift;
 		np->cur_tx -= tx_shift;
+		spin_unlock_irqrestore (&np->lock, flags);
 	}
-	spin_unlock_irqrestore (&np->lock, flags);
-
+	
 	/* NETDEV WATCHDOG timer */
 	dev->trans_start = jiffies;
 	return 0;
@@ -606,33 +628,24 @@
 
 	ioaddr = dev->base_addr;
 	np = dev->priv;
-	spin_lock (&np->lock);
+	spin_lock(&np->lock);
 	while (1) {
-		int_status = readw (ioaddr + IntStatus) &
-		    (HostError | TxComplete | IntRequested |
-		     UpdateStats | LinkEvent | RxDMAComplete);
-		writew (int_status & (HostError | TxComplete | RxComplete |
-				      IntRequested | UpdateStats | LinkEvent |
-				      TxDMAComplete | RxDMAComplete | RFDListEnd
-				      | RxDMAPriority), ioaddr + IntStatus);
+		int_status = readw (ioaddr + IntStatus); 
+		writew (int_status, ioaddr + IntStatus);
+		int_status &= DEFAULT_INTR;
 		if (int_status == 0)
 			break;
 		/* Processing received packets */
 		if (int_status & RxDMAComplete)
 			receive_packet (dev);
 		/* TxComplete interrupt */
-		if (int_status & TxComplete || np->tx_full) {
-			int tx_status = readl (ioaddr + TxStatus);
+		if ((int_status & TxComplete) || np->tx_full) {
+			int tx_status;
+			tx_status = readl (ioaddr + TxStatus);
 			if (tx_status & 0x01)
 				tx_error (dev, tx_status);
-			/* Send one packet each time at 10Mbps mode */
-			if (np->speed == 10) {
-				np->tx_full = 0;
-				netif_wake_queue (dev);
-			}
-
 			/* Free used tx skbuffs */
-			for (; np->cur_tx - np->old_tx > 0; np->old_tx++) {
+			for (;np->cur_tx - np->old_tx > 0; np->old_tx++) {
 				int entry = np->old_tx % TX_RING_SIZE;
 				struct sk_buff *skb;
 
@@ -649,9 +662,12 @@
 		/* If the ring is no longer full, clear tx_full and 
 		   call netif_wake_queue() */
 		if (np->tx_full && np->cur_tx - np->old_tx < TX_QUEUE_LEN - 1) {
-			np->tx_full = 0;
-			netif_wake_queue (dev);
+			if (np->speed != 10 || int_status & TxComplete) {
+				np->tx_full = 0;
+				netif_wake_queue (dev);
+			}
 		}
+
 		/* Handle uncommon events */
 		if (int_status &
 		    (IntRequested | HostError | LinkEvent | UpdateStats))
@@ -666,7 +682,7 @@
 			break;
 		}
 	}
-	spin_unlock (&np->lock);
+	spin_unlock(&np->lock);
 }
 
 static void
@@ -742,7 +758,7 @@
 		np->stats.collisions++;
 #endif
 
-	/* Restart the Tx. */
+	/* Restart the Tx */
 	writel (readw (dev->base_addr + MACCtrl) | TxEnable, ioaddr + MACCtrl);
 }
 
@@ -808,7 +824,7 @@
 				skb_put (skb, pkt_len);
 			}
 			skb->protocol = eth_type_trans (skb, dev);
-#ifdef RX_HW_CHECKSUM
+#if 0
 			/* Checksum done by hw, but csum value unavailable. */
 			if (!(frame_status & (TCPError | UDPError | IPError))) {
 				skb->ip_summed = CHECKSUM_UNNECESSARY;
@@ -899,7 +915,7 @@
 	/* PCI Error, a catastronphic error related to the bus interface 
 	   occurs, set GlobalReset and HostReset to reset. */
 	if (int_status & HostError) {
-		printk (KERN_ERR "%s: PCI Error! IntStatus %4.4x.\n",
+		printk (KERN_ERR "%s: HostError! IntStatus %4.4x.\n",
 			dev->name, int_status);
 		writew (GlobalReset | HostReset, ioaddr + ASICCtrl + 2);
 		mdelay (500);
@@ -914,8 +930,8 @@
 	u16 temp1;
 	u16 temp2;
 	int i;
-	/* All statistics registers need to acknowledge,
-	   else overflow could cause some problem */
+	/* All statistics registers need to be acknowledged,
+	   else statistic overflow could cause problems */
 	np->stats.rx_packets += readl (ioaddr + FramesRcvOk);
 	np->stats.tx_packets += readl (ioaddr + FramesXmtOk);
 	np->stats.rx_bytes += readl (ioaddr + OctetRcvOk);
@@ -961,7 +977,7 @@
 change_mtu (struct net_device *dev, int new_mtu)
 {
 	struct netdev_private *np = dev->priv;
-	int max = (np->jumbo) ? 9000 : 1536;
+	int max = (np->jumbo) ? MAX_JUMBO : 1536;
 
 	if ((new_mtu < 68) || (new_mtu > max)) {
 		return -EINVAL;
@@ -1693,8 +1709,9 @@
  
 Compile command: 
  
-gcc -D__KERNEL__ -DMODULE -I/usr/src/linux/include -Wall -Wstrict-prototypes -O2 -c dl2x.c
+gcc -D__KERNEL__ -DMODULE -I/usr/src/linux/include -Wall -Wstrict-prototypes -O2 -c dl2k.c
 
 Read Documentation/networking/dl2k.txt for details.
 
 */
+
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/dl2k.h linux/drivers/net/dl2k.h
--- linux.orig/drivers/net/dl2k.h	Mon Jan  7 23:41:09 2002
+++ linux/drivers/net/dl2k.h	Fri Dec 21 18:30:14 2001
@@ -33,7 +33,7 @@
 #include <linux/spinlock.h>
 #include <linux/time.h>
 #define TX_RING_SIZE	128
-#define TX_QUEUE_LEN	96	/* Limit ring entries actually used.  */
+#define TX_QUEUE_LEN	120	/* Limit ring entries actually used.  */
 #define RX_RING_SIZE 	128
 #define TX_TOTAL_SIZE	TX_RING_SIZE*sizeof(struct netdev_desc)
 #define RX_TOTAL_SIZE	RX_RING_SIZE*sizeof(struct netdev_desc)
@@ -649,13 +649,13 @@
 	struct pci_dev *pdev;
 	spinlock_t lock;
 	struct net_device_stats stats;
-	unsigned int rx_buf_sz;	/* Based on MTU+slack. */
-	unsigned int speed;	/* Operating speed */
-	unsigned int vlan;	/* VLAN Id */
-	unsigned int chip_id;	/* PCI table chip id */
-	unsigned int int_count; /* Maximum frames each RxDMAComplete intr */
-	unsigned int int_timeout; /* Wait time between RxDMAComplete intr */
-	unsigned int tx_full:1;	/* The Tx queue is full. */
+	unsigned int rx_buf_sz;		/* Based on MTU+slack. */
+	unsigned int speed;		/* Operating speed */
+	unsigned int vlan;		/* VLAN Id */
+	unsigned int chip_id;		/* PCI table chip id */
+	unsigned int rx_coalesce; 	/* Maximum frames each RxDMAComplete intr */
+	unsigned int rx_timeout; 	/* Wait time between RxDMAComplete intr */
+	unsigned int tx_full:1;		/* The Tx queue is full. */
 	unsigned int full_duplex:1;	/* Full-duplex operation requested. */
 	unsigned int an_enable:2;	/* Auto-Negotiated Enable */
 	unsigned int jumbo:1;		/* Jumbo frame enable */
@@ -697,6 +697,7 @@
 MODULE_DEVICE_TABLE (pci, rio_pci_tbl);
 #define TX_TIMEOUT  (4*HZ)
 #define PACKET_SIZE		1536
+#define MAX_JUMBO		8000
 #define RIO_IO_SIZE             340
 #ifdef RIO_DEBUG
 #define DEBUG_TFD_DUMP(x) debug_tfd_dump(x)
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/gmac.c linux/drivers/net/gmac.c
--- linux.orig/drivers/net/gmac.c	Mon Jan  7 23:41:09 2002
+++ linux/drivers/net/gmac.c	Wed Dec 26 16:34:02 2001
@@ -36,7 +36,8 @@
 #include <asm/prom.h>
 #include <asm/io.h>
 #include <asm/pgtable.h>
-#include <asm/feature.h>
+#include <asm/machdep.h>
+#include <asm/pmac_feature.h>
 #include <asm/keylargo.h>
 #include <asm/pci-bridge.h>
 #ifdef CONFIG_PMAC_PBOOK
@@ -296,6 +297,7 @@
 	}
 }
 
+#ifdef CONFIG_PMAC_PBOOK
 /* Power management: stop PHY chip for suspend mode
  * 
  * TODO: This will have to be modified is WOL is to be supported.
@@ -441,6 +443,7 @@
 		GM_OUT(GM_RX_CONF, 0);
 	}
 }
+#endif
 
 static int
 mii_do_reset_phy(struct gmac *gm, int phy_addr)
@@ -585,8 +588,8 @@
 	gm->phy_type = PHY_UNKNOWN;
 
 	/* Hard reset the PHY */
-	feature_gmac_phy_reset(gm->of_node);
-	
+	pmac_call_feature(PMAC_FTR_GMAC_PHY_RESET, gm->of_node, 0, 0);
+		
 	/* Find the PHY */
 	for(i=0; i<=31; i++) {
 		mii_control = mii_read(gm, i, MII_CR);
@@ -683,7 +686,7 @@
 gmac_set_power(struct gmac *gm, int power_up)
 {
 	if (power_up) {
-		feature_set_gmac_power(gm->of_node, 1);
+		pmac_call_feature(PMAC_FTR_GMAC_ENABLE, gm->of_node, 0, 1);
 		if (gm->pci_devfn != 0xff) {
 			u16 cmd;
 			
@@ -708,7 +711,7 @@
 	    			PCI_CACHE_LINE_SIZE, 8);
 		}
 	} else {
-		feature_set_gmac_power(gm->of_node, 0);
+		pmac_call_feature(PMAC_FTR_GMAC_ENABLE, gm->of_node, 0, 0);
 	}
 }
 
@@ -1617,6 +1620,11 @@
 	SET_MODULE_OWNER(dev);
 
 	gm = dev->priv;
+	gm->of_node = gmac;
+	if (!request_OF_resource(gmac, 0, " (gmac)")) {
+		printk(KERN_ERR "GMAC: can't request IO resource !\n");
+		goto out_unreg;
+	}
 	dev->base_addr = gmac->addrs[0].address;
 	gm->regs = (volatile unsigned int *)
 		ioremap(gmac->addrs[0].address, 0x10000);
@@ -1626,7 +1634,6 @@
 	}
 	dev->irq = gmac->intrs[0].line;
 	gm->dev = dev;
-	gm->of_node = gmac;
 
 	spin_lock_init(&gm->lock);
 	
@@ -1667,6 +1674,8 @@
 
 out_unreg:
 	unregister_netdev(dev);
+	if (gm->of_node)
+		release_OF_resource(gm->of_node, 0);
 	kfree(dev);
 out_rxdesc:
 	free_page(rx_descpage);
@@ -1677,6 +1686,7 @@
 MODULE_AUTHOR("Paul Mackerras/Ben Herrenschmidt");
 MODULE_DESCRIPTION("PowerMac GMAC driver.");
 MODULE_LICENSE("GPL");
+EXPORT_NO_SYMBOLS;
 
 static void __exit gmac_cleanup_module(void)
 {
@@ -1694,6 +1704,7 @@
 		iounmap((void *) gm->regs);
 		free_page(gm->tx_desc_page);
 		free_page(gm->rx_desc_page);
+		release_OF_resource(gm->of_node, 0);
 		gmacs = gm->next_gmac;
 		kfree(dev);
 	}
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/mace.c linux/drivers/net/mace.c
--- linux.orig/drivers/net/mace.c	Mon Jan  7 23:41:09 2002
+++ linux/drivers/net/mace.c	Wed Dec 26 16:34:02 2001
@@ -24,9 +24,6 @@
 static struct net_device *mace_devs;
 static int port_aaui = -1;
 
-MODULE_PARM(port_aaui, "i");
-MODULE_PARM_DESC(port_aaui, "MACE uses AAUI port (0-1)");
-
 #define N_RX_RING	8
 #define N_TX_RING	6
 #define MAX_TX_ACTIVE	1
@@ -59,6 +56,7 @@
     struct timer_list tx_timeout;
     int timeout_active;
     int port_aaui;
+    struct device_node* of_node;
     struct net_device *next_mace;
 };
 
@@ -152,9 +150,25 @@
 	SET_MODULE_OWNER(dev);
 
 	mp = dev->priv;
+	mp->of_node = mace;
+	
+	if (!request_OF_resource(mace, 0, " (mace)")) {
+		printk(KERN_ERR "MACE: can't request IO resource !\n");
+		goto err_out;
+	}
+	if (!request_OF_resource(mace, 1, " (mace tx dma)")) {
+		printk(KERN_ERR "MACE: can't request TX DMA resource !\n");
+		goto err_out;
+	}
+
+	if (!request_OF_resource(mace, 2, " (mace tx dma)")) {
+		printk(KERN_ERR "MACE: can't request RX DMA resource !\n");
+		goto err_out;
+	}
+
 	dev->base_addr = mace->addrs[0].address;
 	mp->mace = (volatile struct mace *)
-		ioremap(mace->addrs[0].address, 0x1000);
+				ioremap(mace->addrs[0].address, 0x1000);
 	dev->irq = mace->intrs[0].line;
 
 	printk(KERN_INFO "%s: MACE at", dev->name);
@@ -221,6 +235,16 @@
 
 	mp->next_mace = mace_devs;
 	mace_devs = dev;
+	return;
+	
+err_out:
+	unregister_netdev(dev);
+	if (mp->of_node) {
+		release_OF_resource(mp->of_node, 0);
+		release_OF_resource(mp->of_node, 1);
+		release_OF_resource(mp->of_node, 2);
+	}
+	kfree(dev);
 }
 
 static void dbdma_reset(volatile struct dbdma_regs *dma)
@@ -273,14 +297,20 @@
     __mace_set_address(dev, dev->dev_addr);
 
     /* clear the multicast filter */
-    out_8(&mb->iac, ADDRCHG | LOGADDR);
-    while ((in_8(&mb->iac) & ADDRCHG) != 0)
-	;
-    for (i = 0; i < 8; ++i) {
-	out_8(&mb->ladrf, 0);
+    if (in_8(&mb->chipid_hi) == 0x09) {
+	printk("chip 0x09 mcast workaround...\n");
+	out_8(&mb->iac, LOGADDR);
+    } else {
+	out_8(&mb->iac, ADDRCHG | LOGADDR);
+	while ((in_8(&mb->iac) & ADDRCHG) != 0)
+		;
     }
+    for (i = 0; i < 8; ++i)
+	out_8(&mb->ladrf, 0);
+
     /* done changing address */
-    out_8(&mb->iac, 0);
+    if (in_8(&mb->chipid_hi) != 0x09)
+	out_8(&mb->iac, 0);
 
     if (mp->port_aaui)
     	out_8(&mb->plscc, PORTSEL_AUI + ENPLSIO);
@@ -295,11 +325,17 @@
     int i;
 
     /* load up the hardware address */
-    out_8(&mb->iac, ADDRCHG | PHYADDR);
-    while ((in_8(&mb->iac) & ADDRCHG) != 0)
-	;
+    if (in_8(&mb->chipid_hi) == 0x09) {
+	printk("chip 0x09 hwaddr workaround...\n");
+    	out_8(&mb->iac, PHYADDR);
+    } else {
+	while ((in_8(&mb->iac) & ADDRCHG) != 0)
+	    ;
+    }
     for (i = 0; i < 6; ++i)
 	out_8(&mb->padr, dev->dev_addr[i] = p[i]);
+    if (in_8(&mb->chipid_hi) != 0x09)
+        out_8(&mb->iac, 0);
 }
 
 static int mace_set_address(struct net_device *dev, void *addr)
@@ -312,7 +348,6 @@
 
     __mace_set_address(dev, addr);
 
-    out_8(&mb->iac, 0);
     /* note: setting ADDRCHG clears ENRCV */
     out_8(&mb->maccc, mp->maccc);
 
@@ -557,12 +592,18 @@
 	printk("\n");
 #endif
 
-	out_8(&mb->iac, ADDRCHG | LOGADDR);
-	while ((in_8(&mb->iac) & ADDRCHG) != 0)
-	    ;
-	for (i = 0; i < 8; ++i) {
-	    out_8(&mb->ladrf, multicast_filter[i]);
+	if (in_8(&mb->chipid_hi) == 0x09) {
+	    printk("chip 0x09 mcast workaround...\n");
+	    out_8(&mb->iac, LOGADDR);
+	} else {
+	    out_8(&mb->iac, ADDRCHG | LOGADDR);
+	    while ((in_8(&mb->iac) & ADDRCHG) != 0)
+		;
 	}
+	for (i = 0; i < 8; ++i)
+	    out_8(&mb->ladrf, multicast_filter[i]);
+	if (in_8(&mb->chipid_hi) != 0x09)
+	    out_8(&mb->iac, LOGADDR);
     }
     /* reset maccc */
     out_8(&mb->maccc, mp->maccc);
@@ -913,7 +954,10 @@
 
 MODULE_AUTHOR("Paul Mackerras");
 MODULE_DESCRIPTION("PowerMac MACE driver.");
+MODULE_PARM(port_aaui, "i");
+MODULE_PARM_DESC(port_aaui, "MACE uses AAUI port (0-1)");
 MODULE_LICENSE("GPL");
+EXPORT_NO_SYMBOLS;
 
 static void __exit mace_cleanup (void)
 {
@@ -921,19 +965,23 @@
     struct mace_data *mp;
 
     while ((dev = mace_devs) != 0) {
-	mp = (struct mace_data *) mace_devs->priv;
-	mace_devs = mp->next_mace;
+		mp = (struct mace_data *) mace_devs->priv;
+		mace_devs = mp->next_mace;
 
-	free_irq(dev->irq, dev);
-	free_irq(mp->tx_dma_intr, dev);
-	free_irq(mp->rx_dma_intr, dev);
+		unregister_netdev(dev);
+		free_irq(dev->irq, dev);
+		free_irq(mp->tx_dma_intr, dev);
+		free_irq(mp->rx_dma_intr, dev);
+
+		release_OF_resource(mp->of_node, 0);
+		release_OF_resource(mp->of_node, 1);
+		release_OF_resource(mp->of_node, 2);
 
-	unregister_netdev(dev);
-	kfree(dev);
+		kfree(dev);
     }
     if (dummy_buf != NULL) {
-	kfree(dummy_buf);
-	dummy_buf = NULL;
+		kfree(dummy_buf);
+		dummy_buf = NULL;
     }
 }
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/natsemi.c linux/drivers/net/natsemi.c
--- linux.orig/drivers/net/natsemi.c	Mon Jan  7 23:41:09 2002
+++ linux/drivers/net/natsemi.c	Fri Dec 21 19:17:53 2001
@@ -100,7 +100,10 @@
 		* ETHTOOL_* further support (Tim Hockin)
 
 	version 1.0.13:
-		* ETHTOOL_[GS]EEPROM support (Tim Hockin)
+		* ETHTOOL_GEEPROM support (Tim Hockin)
+
+	version 1.0.14:
+		* Cleanup some messages and autoneg in ethtool (Tim Hockin)
 
 	TODO:
 	* big endian support with CFG:BEM instead of cpu_to_le32
@@ -108,9 +111,40 @@
 	* flow control
 */
 
+#if !defined(__OPTIMIZE__)
+#warning  You must compile this file with the correct options!
+#warning  See the last lines of the source file.
+#error You must compile this driver with "-O".
+#endif
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/ethtool.h>
+#include <linux/delay.h>
+#include <linux/rtnetlink.h>
+#include <linux/mii.h>
+#include <asm/processor.h>	/* Processor type for cache alignment. */
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+
 #define DRV_NAME	"natsemi"
-#define DRV_VERSION	"1.07+LK1.0.13"
-#define DRV_RELDATE	"Oct 19, 2001"
+#define DRV_VERSION	"1.07+LK1.0.14"
+#define DRV_RELDATE	"Nov 27, 2001"
 
 /* Updated to recommendations in pci-skeleton v2.03. */
 
@@ -129,7 +163,12 @@
 /* The user-configurable values.
    These may be modified when a driver module is loaded.*/
 
-static int debug = 1; /* 1 normal messages, 0 quiet .. 7 verbose. */
+#define NATSEMI_DEF_MSG		(NETIF_MSG_DRV		| \
+				 NETIF_MSG_LINK		| \
+				 NETIF_MSG_WOL		| \
+				 NETIF_MSG_RX_ERR	| \
+				 NETIF_MSG_TX_ERR)
+static int debug = NATSEMI_DEF_MSG;
 
 /* Maximum events (Rx packets, etc.) to handle at each interrupt. */
 static int max_interrupt_work = 20;
@@ -180,37 +219,6 @@
 
 #define PKT_BUF_SZ		1536 /* Size of each temporary Rx buffer. */
 
-#if !defined(__OPTIMIZE__)
-#warning  You must compile this file with the correct options!
-#warning  See the last lines of the source file.
-#error You must compile this driver with "-O".
-#endif
-
-#include <linux/config.h>
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/string.h>
-#include <linux/timer.h>
-#include <linux/errno.h>
-#include <linux/ioport.h>
-#include <linux/slab.h>
-#include <linux/interrupt.h>
-#include <linux/pci.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/skbuff.h>
-#include <linux/init.h>
-#include <linux/spinlock.h>
-#include <linux/ethtool.h>
-#include <linux/delay.h>
-#include <linux/rtnetlink.h>
-#include <linux/mii.h>
-#include <asm/processor.h>	/* Processor type for cache alignment. */
-#include <asm/bitops.h>
-#include <asm/io.h>
-#include <asm/irq.h>
-#include <asm/uaccess.h>
-
 /* These identify the driver base version and may not be removed. */
 static char version[] __devinitdata =
 KERN_INFO DRV_NAME ".c:v1.07 1/9/2001  Written by Donald Becker <becker@scyld.com>\n"
@@ -229,7 +237,7 @@
 MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i");
 MODULE_PARM_DESC(max_interrupt_work, "DP8381x maximum events handled per interrupt");
 MODULE_PARM_DESC(mtu, "DP8381x MTU (all boards)");
-MODULE_PARM_DESC(debug, "DP8381x debug level (0-5)");
+MODULE_PARM_DESC(debug, "DP8381x debug bitmask");
 MODULE_PARM_DESC(rx_copybreak, "DP8381x copy breakpoint for copy-only-tiny-frames");
 MODULE_PARM_DESC(options, "DP8381x: Bits 0-3: media type, bit 17: full duplex");
 MODULE_PARM_DESC(full_duplex, "DP8381x full duplex setting(s) (1)");
@@ -414,6 +422,7 @@
 enum ChipConfig_bits {
 	CfgPhyDis		= 0x200,
 	CfgPhyRst		= 0x400,
+	CfgExtPhy		= 0x1000,
 	CfgAnegEnable		= 0x2000,
 	CfgAneg100		= 0x4000,
 	CfgAnegFull		= 0x8000,
@@ -627,6 +636,7 @@
 	u16 advertising; /* NWay media advertisement */
 	unsigned int iosize;
 	spinlock_t lock;
+	u32 msg_enable;
 };
 
 static int eeprom_read(long ioaddr, int location);
@@ -746,6 +756,7 @@
 	pci_set_drvdata(pdev, dev);
 	np->iosize = iosize;
 	spin_lock_init(&np->lock);
+	np->msg_enable = debug;
 
 	/* Reset the chip to erase previous misconfiguration. */
 	natsemi_reload_eeprom(dev);
@@ -760,7 +771,8 @@
 		if (option & 0x200)
 			np->full_duplex = 1;
 		if (option & 15)
-			printk(KERN_INFO "%s: ignoring user supplied media type %d",
+			printk(KERN_INFO 
+				"%s: ignoring user supplied media type %d",
 				dev->name, option & 15);
 	}
 	if (find_cnt < MAX_UNITS  &&  full_duplex[find_cnt] > 0)
@@ -789,14 +801,17 @@
 	}
 	netif_carrier_off(dev);
 
-	printk(KERN_INFO "%s: %s at 0x%lx, ",
-		   dev->name, natsemi_pci_info[chip_idx].name, ioaddr);
-	for (i = 0; i < ETH_ALEN-1; i++)
-			printk("%2.2x:", dev->dev_addr[i]);
-	printk("%2.2x, IRQ %d.\n", dev->dev_addr[i], irq);
+	if (netif_msg_drv(np)) {
+		printk(KERN_INFO "%s: %s at %#08lx, ",
+			   dev->name, natsemi_pci_info[chip_idx].name, ioaddr);
+		for (i = 0; i < ETH_ALEN-1; i++)
+				printk("%02x:", dev->dev_addr[i]);
+		printk("%02x, IRQ %d.\n", dev->dev_addr[i], irq);
+	}
 
 	np->advertising = mdio_read(dev, 1, MII_ADVERTISE);
-	if ((readl(ioaddr + ChipConfig) & 0xe000) != 0xe000) {
+	if ((readl(ioaddr + ChipConfig) & 0xe000) != 0xe000 
+	 && netif_msg_probe(np)) {
 		u32 chip_config = readl(ioaddr + ChipConfig);
 		printk(KERN_INFO "%s: Transceiver default autonegotiation %s "
 			   "10%s %s duplex.\n",
@@ -805,12 +820,18 @@
 			   chip_config & CfgAneg100 ? "0" : "",
 			   chip_config & CfgAnegFull ? "full" : "half");
 	}
-	printk(KERN_INFO "%s: Transceiver status 0x%4.4x advertising %4.4x.\n",
-		   dev->name, mdio_read(dev, 1, MII_BMSR), 
-		   np->advertising);
+	if (netif_msg_probe(np))
+		printk(KERN_INFO 
+			"%s: Transceiver status %#04x advertising %#04x.\n",
+			dev->name, mdio_read(dev, 1, MII_BMSR), 
+			np->advertising);
 
 	/* save the silicon revision for later querying */
 	np->srr = readl(ioaddr + SiliconRev);
+	if (netif_msg_hw(np))
+		printk(KERN_INFO "%s: silicon revision %#04x.\n",
+				dev->name, np->srr);
+
 
 	return 0;
 }
@@ -907,6 +928,7 @@
 	u32 rfcr;
 	u16 pmatch[3];
 	u16 sopass[3];
+	struct netdev_private *np = dev->priv;
 
 	/* 
 	 * Resetting the chip causes some registers to be lost.
@@ -940,10 +962,10 @@
 			break;
 		udelay(5);
 	}
-	if (i==NATSEMI_HW_TIMEOUT && debug) {
+	if (i==NATSEMI_HW_TIMEOUT && netif_msg_hw(np)) {
 		printk(KERN_INFO "%s: reset did not complete in %d usec.\n",
 		   dev->name, i*5);
-	} else if (debug > 2) {
+	} else if (netif_msg_hw(np)) {
 		printk(KERN_DEBUG "%s: reset completed in %d usec.\n",
 		   dev->name, i*5);
 	}
@@ -972,6 +994,7 @@
 
 static void natsemi_reload_eeprom(struct net_device *dev)
 {
+	struct netdev_private *np = dev->priv;
 	int i;
 
 	writel(EepromReload, dev->base_addr + PCIBusCfg);
@@ -980,10 +1003,10 @@
 			break;
 		udelay(5);
 	}
-	if (i==NATSEMI_HW_TIMEOUT && debug) {
+	if (i==NATSEMI_HW_TIMEOUT && netif_msg_hw(np)) {
 		printk(KERN_INFO "%s: EEPROM did not reload in %d usec.\n",
 		   dev->name, i*5);
-	} else if (debug > 2) {
+	} else if (netif_msg_hw(np)) {
 		printk(KERN_DEBUG "%s: EEPROM reloaded in %d usec.\n",
 		   dev->name, i*5);
 	}
@@ -992,6 +1015,7 @@
 static void natsemi_stop_rxtx(struct net_device *dev)
 {
 	long ioaddr = dev->base_addr;
+	struct netdev_private *np = dev->priv;
 	int i;
 
 	writel(RxOff | TxOff, ioaddr + ChipCmd);
@@ -1000,10 +1024,10 @@
 			break;
 		udelay(5);
 	}
-	if (i==NATSEMI_HW_TIMEOUT && debug) {
+	if (i==NATSEMI_HW_TIMEOUT && netif_msg_hw(np)) {
 		printk(KERN_INFO "%s: Tx/Rx process did not stop in %d usec.\n",
 				dev->name, i*5);
-	} else if (debug > 2) {
+	} else if (netif_msg_hw(np)) {
 		printk(KERN_DEBUG "%s: Tx/Rx process stopped in %d usec.\n",
 				dev->name, i*5);
 	}
@@ -1021,7 +1045,7 @@
 	i = request_irq(dev->irq, &intr_handler, SA_SHIRQ, dev->name, dev);
 	if (i) return i;
 
-	if (debug > 1)
+	if (netif_msg_ifup(np))
 		printk(KERN_DEBUG "%s: netdev_open() irq %d.\n",
 			   dev->name, dev->irq);
 	i = alloc_ring(dev);
@@ -1036,8 +1060,8 @@
 
 	netif_start_queue(dev);
 
-	if (debug > 2)
-		printk(KERN_DEBUG "%s: Done netdev_open(), status: %x.\n",
+	if (netif_msg_ifup(np))
+		printk(KERN_DEBUG "%s: Done netdev_open(), status: %#08x.\n",
 			   dev->name, (int)readl(ioaddr + ChipCmd));
 
 	/* Set the timer to check for link beat. */
@@ -1057,19 +1081,18 @@
 	int duplex;
 	int chipcfg = readl(ioaddr + ChipConfig);
 
-	if(!(chipcfg & CfgLink)) {
+	if (!(chipcfg & CfgLink)) {
 		if (netif_carrier_ok(dev)) {
-			if (debug)
-				printk(KERN_INFO "%s: no link. Disabling watchdog.\n",
+			if (netif_msg_link(np))
+				printk(KERN_NOTICE "%s: link down.\n", 
 					dev->name);
 			netif_carrier_off(dev);
 		}
 		return;
 	}
 	if (!netif_carrier_ok(dev)) {
-		if (debug)
-			printk(KERN_INFO "%s: link is back. Enabling watchdog.\n",
-					dev->name);
+		if (netif_msg_link(np))
+			printk(KERN_NOTICE "%s: link up.\n", dev->name);
 		netif_carrier_on(dev);
 	}
 
@@ -1077,10 +1100,11 @@
 
 	/* if duplex is set then bit 28 must be set, too */
 	if (duplex ^ !!(np->rx_config & RxAcceptTx)) {
-		if (debug)
-			printk(KERN_INFO "%s: Setting %s-duplex based on negotiated link"
-				   " capability.\n", dev->name,
-				   duplex ? "full" : "half");
+		if (netif_msg_link(np))
+			printk(KERN_INFO 
+				"%s: Setting %s-duplex based on negotiated "
+				"link capability.\n", dev->name,
+				duplex ? "full" : "half");
 		if (duplex) {
 			np->rx_config |= RxAcceptTx;
 			np->tx_config |= TxCarrierIgn | TxHeartIgn;
@@ -1099,17 +1123,12 @@
 	long ioaddr = dev->base_addr;
 	int i;
 
-	/* save the silicon revision for later */
-	if (debug > 4)
-		printk(KERN_DEBUG "%s: found silicon revision %xh.\n",
-				dev->name, np->srr);
-
 	for (i=0;i<NATSEMI_HW_TIMEOUT;i++) {
 		if (readl(dev->base_addr + ChipConfig) & CfgAnegDone)
 			break;
 		udelay(10);
 	}
-	if (i==NATSEMI_HW_TIMEOUT && debug) {
+	if (i==NATSEMI_HW_TIMEOUT && netif_msg_link(np)) {
 		printk(KERN_INFO 
 			"%s: autonegotiation did not complete in %d usec.\n",
 			dev->name, i*10);
@@ -1174,8 +1193,8 @@
 	 * nothing will be written to memory. */
 	np->SavedClkRun = readl(ioaddr + ClkRun);
 	writel(np->SavedClkRun & ~PMEEnable, ioaddr + ClkRun);
-	if (np->SavedClkRun & PMEStatus) {
-		printk(KERN_NOTICE "%s: Wake-up event %8.8x\n", 
+	if (np->SavedClkRun & PMEStatus && netif_msg_wol(np)) {
+		printk(KERN_NOTICE "%s: Wake-up event %#08x\n", 
 			dev->name, readl(ioaddr + WOLCmd));
 	}
 
@@ -1205,7 +1224,7 @@
 	long ioaddr = dev->base_addr;
 	u16 dspcfg;
 
-	if (debug > 3) {
+	if (netif_msg_timer(np)) {
 		/* DO NOT read the IntrStatus register, 
 		 * a read clears any pending interrupts.
 		 */
@@ -1219,9 +1238,9 @@
 	writew(0, ioaddr+PGSEL);
 	if (dspcfg != DSPCFG_VAL) {
 		if (!netif_queue_stopped(dev)) {
-			printk(KERN_INFO 
-				"%s: possible phy reset: re-initializing\n",
-				dev->name);
+			if (netif_msg_hw(np))
+				printk(KERN_NOTICE "%s: possible phy reset: "
+					"re-initializing\n", dev->name);
 			disable_irq(dev->irq);
 			spin_lock_irq(&np->lock);
 			init_registers(dev);
@@ -1244,18 +1263,18 @@
 {
 	struct netdev_private *np = dev->priv;
 
-	if (debug > 2) {
+	if (netif_msg_pktdata(np)) {
 		int i;
 		printk(KERN_DEBUG "  Tx ring at %p:\n", np->tx_ring);
 		for (i = 0; i < TX_RING_SIZE; i++) {
-			printk(KERN_DEBUG " #%d desc. %8.8x %8.8x %8.8x.\n",
+			printk(KERN_DEBUG " #%d desc. %#08x %#08x %#08x.\n",
 				   i, np->tx_ring[i].next_desc,
 				   np->tx_ring[i].cmd_status, 
 				   np->tx_ring[i].addr);
 		}
 		printk(KERN_DEBUG "  Rx ring %p:\n", np->rx_ring);
 		for (i = 0; i < RX_RING_SIZE; i++) {
-			printk(KERN_DEBUG " #%d desc. %8.8x %8.8x %8.8x.\n",
+			printk(KERN_DEBUG " #%d desc. %#08x %#08x %#08x.\n",
 				   i, np->rx_ring[i].next_desc,
 				   np->rx_ring[i].cmd_status, 
 				   np->rx_ring[i].addr);
@@ -1271,9 +1290,11 @@
 	disable_irq(dev->irq);
 	spin_lock_irq(&np->lock);
 	if (netif_device_present(dev)) {
-		printk(KERN_WARNING "%s: Transmit timed out, status %8.8x,"
-			" resetting...\n", 
-			dev->name, readl(ioaddr + IntrStatus));
+		if (netif_msg_tx_err(np))
+			printk(KERN_WARNING 
+				"%s: Transmit timed out, status %#08x,"
+				" resetting...\n", 
+				dev->name, readl(ioaddr + IntrStatus));
 		dump_ring(dev);
 
 		natsemi_reset(dev);
@@ -1431,7 +1452,7 @@
 
 	dev->trans_start = jiffies;
 
-	if (debug > 4) {
+	if (netif_msg_tx_queued(np)) {
 		printk(KERN_DEBUG "%s: Transmit frame #%d queued in slot %d.\n",
 			   dev->name, np->cur_tx, entry);
 	}
@@ -1445,13 +1466,14 @@
 	for (; np->cur_tx - np->dirty_tx > 0; np->dirty_tx++) {
 		int entry = np->dirty_tx % TX_RING_SIZE;
 		if (np->tx_ring[entry].cmd_status & cpu_to_le32(DescOwn)) {
-			if (debug > 4)
+			if (netif_msg_tx_err(np))
 				printk(KERN_DEBUG "%s: tx frame #%d is busy.\n",
 						dev->name, np->dirty_tx);
 			break;
 		}
-		if (debug > 4)
-			printk(KERN_DEBUG "%s: tx frame #%d finished with status %8.8xh.\n",
+		if (netif_msg_tx_done(np))
+			printk(KERN_DEBUG 
+				"%s: tx frame #%d finished, status %#08x.\n",
 					dev->name, np->dirty_tx,
 					le32_to_cpu(np->tx_ring[entry].cmd_status));
 		if (np->tx_ring[entry].cmd_status & cpu_to_le32(DescPktOK)) {
@@ -1488,21 +1510,18 @@
 static void intr_handler(int irq, void *dev_instance, struct pt_regs *rgs)
 {
 	struct net_device *dev = dev_instance;
-	struct netdev_private *np;
-	long ioaddr;
+	struct netdev_private *np = dev->priv;
+	long ioaddr = dev->base_addr;
 	int boguscnt = max_interrupt_work;
 
-	ioaddr = dev->base_addr;
-	np = dev->priv;
-	
 	if (!netif_device_present(dev))
 		return;
 	do {
 		/* Reading automatically acknowledges all int sources. */
 		u32 intr_status = readl(ioaddr + IntrStatus);
 
-		if (debug > 4)
-			printk(KERN_DEBUG "%s: Interrupt, status %4.4x.\n",
+		if (netif_msg_intr(np))
+			printk(KERN_DEBUG "%s: Interrupt, status %#08x.\n",
 				   dev->name, intr_status);
 
 		if (intr_status == 0)
@@ -1523,13 +1542,13 @@
 
 		if (--boguscnt < 0) {
 			printk(KERN_WARNING "%s: Too much work at interrupt, "
-				   "status=0x%4.4x.\n",
+				   "status=%#08x.\n",
 				   dev->name, intr_status);
 			break;
 		}
 	} while (1);
 
-	if (debug > 4)
+	if (netif_msg_intr(np))
 		printk(KERN_DEBUG "%s: exiting interrupt.\n",
 			   dev->name);
 }
@@ -1545,22 +1564,28 @@
 
 	/* If the driver owns the next entry it's a new packet. Send it up. */
 	while (desc_status < 0) {        /* e.g. & DescOwn */
-		if (debug > 4)
-			printk(KERN_DEBUG "  In netdev_rx() entry %d status was %8.8x.\n",
-				   entry, desc_status);
+		if (netif_msg_rx_status(np))
+			printk(KERN_DEBUG 
+				"  netdev_rx() entry %d status was %#08x.\n",
+				entry, desc_status);
 		if (--boguscnt < 0)
 			break;
-		if ((desc_status & (DescMore|DescPktOK|DescRxLong)) != DescPktOK) {
+		if ((desc_status&(DescMore|DescPktOK|DescRxLong)) != DescPktOK){
 			if (desc_status & DescMore) {
-				printk(KERN_WARNING "%s: Oversized(?) Ethernet frame spanned "
-					   "multiple buffers, entry %#x status %x.\n",
-					   dev->name, np->cur_rx, desc_status);
+				if (netif_msg_rx_err(np))
+					printk(KERN_WARNING 
+						"%s: Oversized(?) Ethernet "
+						"frame spanned multiple "
+						"buffers, entry %#08x "
+						"status %#08x.\n", dev->name, 
+						np->cur_rx, desc_status);
 				np->stats.rx_length_errors++;
 			} else {
 				/* There was a error. */
-				if (debug > 2)
-					printk(KERN_DEBUG "  netdev_rx() Rx error was %8.8x.\n",
-						   desc_status);
+				if (netif_msg_rx_err(np))
+					printk(KERN_DEBUG 
+						"  netdev_rx() Rx error was "
+						"%#08x.\n", desc_status);
 				np->stats.rx_errors++;
 				if (desc_status & (DescRxAbort|DescRxOver)) 
 					np->stats.rx_over_errors++;
@@ -1575,8 +1600,8 @@
 			struct sk_buff *skb;
 			/* Omit CRC size. */
 			int pkt_len = (desc_status & DescSizeMask) - 4;
-			/* Check if the packet is long enough to accept without copying
-			   to a minimally-sized skbuff. */
+			/* Check if the packet is long enough to accept 
+			 * without copying to a minimally-sized skbuff. */
 			if (pkt_len < rx_copybreak
 				&& (skb = dev_alloc_skb(pkt_len + 2)) != NULL) {
 				skb->dev = dev;
@@ -1639,11 +1664,16 @@
 
 	spin_lock(&np->lock);
 	if (intr_status & LinkChange) {
-		printk(KERN_NOTICE 
-			"%s: Link changed: Autonegotiation advertising"
-			" %4.4x  partner %4.4x.\n", dev->name,
-			(int)mdio_read(dev, 1, MII_ADVERTISE), 
-			(int)mdio_read(dev, 1, MII_LPA));
+		u16 adv = mdio_read(dev, 1, MII_ADVERTISE);
+		u16 lpa = mdio_read(dev, 1, MII_LPA);
+		if (mdio_read(dev, 1, MII_BMCR) & BMCR_ANENABLE 
+		 && netif_msg_link(np)) {
+			printk(KERN_INFO 
+				"%s: Autonegotiation advertising"
+				" %#04x  partner %#04x.\n", dev->name,
+				adv, lpa);
+		}
+
 		/* read MII int status to clear the flag */
 		readw(ioaddr + MIntrStatus);
 		check_link(dev);
@@ -1654,18 +1684,19 @@
 	if (intr_status & IntrTxUnderrun) {
 		if ((np->tx_config & TxDrthMask) < 62)
 			np->tx_config += 2;
-		if (debug > 2)
-			printk(KERN_NOTICE "%s: increasing Tx theshold, new tx cfg %8.8xh.\n",
-					dev->name, np->tx_config);
+		if (netif_msg_tx_err(np))
+			printk(KERN_NOTICE 
+				"%s: increased Tx theshold, txcfg %#08x.\n",
+				dev->name, np->tx_config);
 		writel(np->tx_config, ioaddr + TxConfig);
 	}
-	if (intr_status & WOLPkt) {
+	if (intr_status & WOLPkt && netif_msg_wol(np)) {
 		int wol_status = readl(ioaddr + WOLCmd);
-		printk(KERN_NOTICE "%s: Link wake-up event %8.8x\n",
+		printk(KERN_NOTICE "%s: Link wake-up event %#08x\n",
 			   dev->name, wol_status);
 	}
 	if (intr_status & RxStatusFIFOOver) {
-		if (debug >= 2) {
+		if (netif_msg_rx_err(np) && netif_msg_intr(np)) {
 			printk(KERN_NOTICE "%s: Rx status FIFO overrun\n", 
 				dev->name);
 		}
@@ -1673,10 +1704,8 @@
 	}
 	/* Hmmmmm, it's not clear how to recover from PCI faults. */
 	if (intr_status & IntrPCIErr) {
-		if (debug) {
-			printk(KERN_NOTICE "%s: PCI error %08x\n", dev->name,
-				intr_status & IntrPCIErr);
-		}
+		printk(KERN_NOTICE "%s: PCI error %#08x\n", dev->name,
+			intr_status & IntrPCIErr);
 		np->stats.tx_fifo_errors++;
 		np->stats.rx_fifo_errors++;
 	}
@@ -1775,7 +1804,8 @@
 
 	if (dev->flags & IFF_PROMISC) {			/* Set promiscuous. */
 		/* Unconditionally log net taps. */
-		printk(KERN_NOTICE "%s: Promiscuous mode enabled.\n", dev->name);
+		printk(KERN_NOTICE "%s: Promiscuous mode enabled.\n", 
+			dev->name);
 		rx_mode = RxFilterEnable | AcceptBroadcast 
 			| AcceptAllMulticast | AcceptAllPhys | AcceptMyPhys;
 	} else if ((dev->mc_count > multicast_filter_limit)
@@ -1910,7 +1940,7 @@
 	/* get message-level */
 	case ETHTOOL_GMSGLVL: {
 		struct ethtool_value edata = {ETHTOOL_GMSGLVL};
-		edata.data = debug;
+		edata.data = np->msg_enable;
 		if (copy_to_user(useraddr, &edata, sizeof(edata)))
 			return -EFAULT;
 		return 0;
@@ -1920,7 +1950,7 @@
 		struct ethtool_value edata;
 		if (copy_from_user(&edata, useraddr, sizeof(edata)))
 			return -EFAULT;
-		debug = edata.data;
+		np->msg_enable = edata.data;
 		return 0;
 	}
 	/* restart autonegotiation */
@@ -2108,18 +2138,22 @@
 	ecmd->supported = 
 		(SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full |
 		SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full |
-		SUPPORTED_Autoneg | SUPPORTED_TP);
+		SUPPORTED_Autoneg | SUPPORTED_TP | SUPPORTED_MII);
 	
-	/* only supports twisted-pair */
-	ecmd->port = PORT_TP;
+	/* only supports twisted-pair or MII */
+	tmp = readl(dev->base_addr + ChipConfig);
+	if (tmp & CfgExtPhy)
+		ecmd->port = PORT_MII;
+	else
+		ecmd->port = PORT_TP;
 
 	/* only supports internal transceiver */
 	ecmd->transceiver = XCVR_INTERNAL;
 
-	/* this isn't fully supported at higher layers */
+	/* not sure what this is for */
 	ecmd->phy_address = readw(dev->base_addr + PhyCtrl) & PhyAddrMask;
 
-	ecmd->advertising = ADVERTISED_TP;
+	ecmd->advertising = ADVERTISED_TP | ADVERTISED_MII;
 	tmp = mdio_read(dev, 1, MII_ADVERTISE);
 	if (tmp & ADVERTISE_10HALF)
 		ecmd->advertising |= ADVERTISED_10baseT_Half;
@@ -2130,20 +2164,21 @@
 	if (tmp & ADVERTISE_100FULL)
 		ecmd->advertising |= ADVERTISED_100baseT_Full;
 
-	tmp = readl(dev->base_addr + ChipConfig);
-	if (tmp & CfgAnegEnable) {
+	tmp = mdio_read(dev, 1, MII_BMCR);
+	if (tmp & BMCR_ANENABLE) {
 		ecmd->advertising |= ADVERTISED_Autoneg;
 		ecmd->autoneg = AUTONEG_ENABLE;
 	} else {
 		ecmd->autoneg = AUTONEG_DISABLE;
 	}
 
+	tmp = readl(dev->base_addr + ChipConfig);
 	if (tmp & CfgSpeed100) {
 		ecmd->speed = SPEED_100;
 	} else {
 		ecmd->speed = SPEED_10;
 	}
-
+		
 	if (tmp & CfgFullDuplex) {
 		ecmd->duplex = DUPLEX_FULL;
 	} else {
@@ -2164,7 +2199,7 @@
 		return -EINVAL;
 	if (ecmd->duplex != DUPLEX_HALF && ecmd->duplex != DUPLEX_FULL)
 		return -EINVAL;
-	if (ecmd->port != PORT_TP)
+	if (ecmd->port != PORT_TP && ecmd->port != PORT_MII)
 		return -EINVAL;
 	if (ecmd->transceiver != XCVR_INTERNAL)
 		return -EINVAL;
@@ -2174,39 +2209,22 @@
 	
 	/* WHEW! now lets bang some bits */
 	
+	tmp = mdio_read(dev, 1, MII_BMCR);
 	if (ecmd->autoneg == AUTONEG_ENABLE) {
-		/* advertise only what has been requested */
-		tmp = readl(dev->base_addr + ChipConfig);
-		tmp &= ~(CfgAneg100 | CfgAnegFull);
-		tmp |= CfgAnegEnable;
-		if (ecmd->advertising & ADVERTISED_100baseT_Half 
-		 || ecmd->advertising & ADVERTISED_100baseT_Full) {
-			tmp |= CfgAneg100;
-		}
-		if (ecmd->advertising & ADVERTISED_10baseT_Full 
-		 || ecmd->advertising & ADVERTISED_100baseT_Full) {
-			tmp |= CfgAnegFull;
-		}
-		writel(tmp, dev->base_addr + ChipConfig);
-		/* turn on autonegotiation, and force a renegotiate */
-		tmp = mdio_read(dev, 1, MII_BMCR);
-		tmp |= (BMCR_ANENABLE | BMCR_ANRESTART);
-		mdio_write(dev, 1, MII_BMCR, tmp);
+		/* turn on autonegotiation */
+		tmp |= BMCR_ANENABLE;
 		np->advertising = mdio_read(dev, 1, MII_ADVERTISE);
 	} else {
 		/* turn off auto negotiation, set speed and duplexity */
-		tmp = mdio_read(dev, 1, MII_BMCR);
 		tmp &= ~(BMCR_ANENABLE | BMCR_SPEED100 | BMCR_FULLDPLX);
-		if (ecmd->speed == SPEED_100) {
+		if (ecmd->speed == SPEED_100)
 			tmp |= BMCR_SPEED100;
-		}
-		if (ecmd->duplex == DUPLEX_FULL) {
+		if (ecmd->duplex == DUPLEX_FULL)
 			tmp |= BMCR_FULLDPLX;
-		} else {
+		else
 			np->full_duplex = 0;
-		}
-		mdio_write(dev, 1, MII_BMCR, tmp);
 	}
+	mdio_write(dev, 1, MII_BMCR, tmp);
 	return 0;
 }
 
@@ -2241,7 +2259,7 @@
 	/* the interrupt status is clear-on-read - see if we missed any */
 	if (rbuf[4] & rbuf[5]) {
 		printk(KERN_WARNING 
-			"%s: shoot, we dropped an interrupt (0x%x)\n", 
+			"%s: shoot, we dropped an interrupt (%#08x)\n", 
 			dev->name, rbuf[4] & rbuf[5]);
 	}
 
@@ -2308,7 +2326,7 @@
 	long ioaddr = dev->base_addr;
 	struct netdev_private *np = dev->priv;
 
-	if (debug > 1)
+	if (netif_msg_wol(np))
 		printk(KERN_INFO "%s: remaining active for wake-on-lan\n", 
 			dev->name);
 
@@ -2343,12 +2361,15 @@
 	netif_stop_queue(dev);
 	netif_carrier_off(dev);
 
-	if (debug > 1) {
-		printk(KERN_DEBUG "%s: Shutting down ethercard, status was %4.4x.\n",
-			   dev->name, (int)readl(ioaddr + ChipCmd));
-		printk(KERN_DEBUG "%s: Queue pointers were Tx %d / %d,  Rx %d / %d.\n",
-			   dev->name, np->cur_tx, np->dirty_tx, np->cur_rx, np->dirty_rx);
-	}
+	if (netif_msg_ifdown(np))
+		printk(KERN_DEBUG 
+			"%s: Shutting down ethercard, status was %#04x.\n",
+			dev->name, (int)readl(ioaddr + ChipCmd));
+	if (netif_msg_pktdata(np))
+		printk(KERN_DEBUG 
+			"%s: Queue pointers were Tx %d / %d,  Rx %d / %d.\n",
+			dev->name, np->cur_tx, np->dirty_tx, 
+			np->cur_rx, np->dirty_rx);
 
 	del_timer_sync(&np->timer);
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/pcmcia/3c589_cs.c linux/drivers/net/pcmcia/3c589_cs.c
--- linux.orig/drivers/net/pcmcia/3c589_cs.c	Mon Jan  7 23:41:09 2002
+++ linux/drivers/net/pcmcia/3c589_cs.c	Fri Dec 21 18:50:47 2001
@@ -17,6 +17,9 @@
 
 ======================================================================*/
 
+#define DRV_NAME	"3c589_cs"
+#define DRV_VERSION	"1.162"
+
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
@@ -28,6 +31,9 @@
 #include <linux/interrupt.h>
 #include <linux/in.h>
 #include <linux/delay.h>
+#include <linux/ethtool.h>
+
+#include <asm/uaccess.h>
 #include <asm/io.h>
 #include <asm/system.h>
 #include <asm/bitops.h>
@@ -134,7 +140,7 @@
 INT_MODULE_PARM(pc_debug, PCMCIA_DEBUG);
 #define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
 static char *version =
-"3c589_cs.c 1.162 2001/10/13 00:08:50 (David Hinds)";
+DRV_NAME ".c " DRV_VERSION " 2001/10/13 00:08:50 (David Hinds)";
 #else
 #define DEBUG(n, args...)
 #endif
@@ -159,6 +165,7 @@
 static int el3_close(struct net_device *dev);
 static void el3_tx_timeout(struct net_device *dev);
 static void set_multicast_list(struct net_device *dev);
+static int netdev_ioctl (struct net_device *dev, struct ifreq *rq, int cmd);
 
 static dev_info_t dev_info = "3c589_cs";
 
@@ -249,7 +256,8 @@
     dev->tx_timeout = el3_tx_timeout;
     dev->watchdog_timeo = TX_TIMEOUT;
 #endif
-    
+    dev->do_ioctl = netdev_ioctl;
+
     /* Register with Card Services */
     link->next = dev_list;
     dev_list = link;
@@ -638,6 +646,71 @@
 	 ioaddr + EL3_CMD);
     outw(SetIntrEnb | IntLatch | TxAvailable | RxComplete | StatsFull
 	 | AdapterFailure, ioaddr + EL3_CMD);
+}
+
+static int netdev_ethtool_ioctl (struct net_device *dev, void *useraddr)
+{
+	u32 ethcmd;
+
+	/* dev_ioctl() in ../../net/core/dev.c has already checked
+	   capable(CAP_NET_ADMIN), so don't bother with that here.  */
+
+	if (get_user(ethcmd, (u32 *)useraddr))
+		return -EFAULT;
+
+	switch (ethcmd) {
+
+	case ETHTOOL_GDRVINFO: {
+		struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO };
+		strcpy (info.driver, DRV_NAME);
+		strcpy (info.version, DRV_VERSION);
+		sprintf(info.bus_info, "PCMCIA 0x%lx", dev->base_addr);
+		if (copy_to_user (useraddr, &info, sizeof (info)))
+			return -EFAULT;
+		return 0;
+	}
+
+#ifdef PCMCIA_DEBUG
+	/* get message-level */
+	case ETHTOOL_GMSGLVL: {
+		struct ethtool_value edata = {ETHTOOL_GMSGLVL};
+		edata.data = pc_debug;
+		if (copy_to_user(useraddr, &edata, sizeof(edata)))
+			return -EFAULT;
+		return 0;
+	}
+	/* set message-level */
+	case ETHTOOL_SMSGLVL: {
+		struct ethtool_value edata;
+		if (copy_from_user(&edata, useraddr, sizeof(edata)))
+			return -EFAULT;
+		pc_debug = edata.data;
+		return 0;
+	}
+#endif
+
+	default:
+		break;
+	}
+
+	return -EOPNOTSUPP;
+}
+
+static int netdev_ioctl (struct net_device *dev, struct ifreq *rq, int cmd)
+{
+	int rc;
+
+	switch (cmd) {
+	case SIOCETHTOOL:
+		rc = netdev_ethtool_ioctl(dev, (void *) rq->ifr_data);
+		break;
+
+	default:
+		rc = -EOPNOTSUPP;
+		break;
+	}
+
+	return rc;
 }
 
 static int el3_config(struct net_device *dev, struct ifmap *map)
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/pcmcia/aironet4500_cs.c linux/drivers/net/pcmcia/aironet4500_cs.c
--- linux.orig/drivers/net/pcmcia/aironet4500_cs.c	Mon Jan  7 23:41:09 2002
+++ linux/drivers/net/pcmcia/aironet4500_cs.c	Fri Dec 21 18:50:47 2001
@@ -10,8 +10,11 @@
  *
  */
 
+#define DRV_NAME	"aironet4500_cs"
+#define DRV_VERSION	"0.1"
+
 static const char *awc_version =
-"aironet4500_cs.c v0.1 1/1/99 Elmer Joandi, elmer@ylenurme.ee.\n";
+DRV_NAME ".c v" DRV_VERSION " 1/1/99 Elmer Joandi, elmer@ylenurme.ee.\n";
 
 
 #include <linux/module.h>
@@ -24,6 +27,9 @@
 #include <linux/timer.h>
 #include <linux/interrupt.h>
 #include <linux/in.h>
+#include <linux/ethtool.h>
+
+#include <asm/uaccess.h>
 #include <asm/io.h>
 #include <asm/system.h>
 #include <asm/bitops.h>
@@ -159,6 +165,66 @@
 	return ret;
 }
 
+static int netdev_ethtool_ioctl (struct net_device *dev, void *useraddr)
+{
+	u32 ethcmd;
+
+	/* dev_ioctl() in ../../net/core/dev.c has already checked
+	   capable(CAP_NET_ADMIN), so don't bother with that here.  */
+
+	if (get_user(ethcmd, (u32 *)useraddr))
+		return -EFAULT;
+
+	switch (ethcmd) {
+
+	case ETHTOOL_GDRVINFO: {
+		struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO };
+		strcpy (info.driver, DRV_NAME);
+		strcpy (info.version, DRV_VERSION);
+		sprintf(info.bus_info, "PCMCIA 0x%lx", dev->base_addr);
+		if (copy_to_user (useraddr, &info, sizeof (info)))
+			return -EFAULT;
+		return 0;
+	}
+
+#ifdef PCMCIA_DEBUG
+	/* get message-level */
+	case ETHTOOL_GMSGLVL: {
+		struct ethtool_value edata = {ETHTOOL_GMSGLVL};
+		edata.data = pc_debug;
+		if (copy_to_user(useraddr, &edata, sizeof(edata)))
+			return -EFAULT;
+		return 0;
+	}
+	/* set message-level */
+	case ETHTOOL_SMSGLVL: {
+		struct ethtool_value edata;
+		if (copy_from_user(&edata, useraddr, sizeof(edata)))
+			return -EFAULT;
+		pc_debug = edata.data;
+		return 0;
+	}
+#endif
+
+	default:
+		break;
+	}
+
+	return -EOPNOTSUPP;
+}
+
+static int awc_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+	switch (cmd) {
+	case SIOCETHTOOL:
+		return netdev_ethtool_ioctl(dev, (void *) rq->ifr_data);
+
+	default:
+		return -EOPNOTSUPP;
+	}
+	return 0;
+}
+
 /*
 	awc_attach() creates an "instance" of the driver, allocating
 	local data structures for one device.  The device is registered
@@ -230,7 +296,8 @@
 //	dev->set_config = 		&awc_config_misiganes,aga mitte awc_config;
 	dev->get_stats = 		&awc_get_stats;
 //	dev->set_multicast_list = 	&awc_set_multicast_list;
-
+	dev->do_ioctl =			&awc_ioctl;
+	
 	strcpy(dev->name, ((struct awc_private *)dev->priv)->node.dev_name);
 
 	ether_setup(dev);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/pcmcia/fmvj18x_cs.c linux/drivers/net/pcmcia/fmvj18x_cs.c
--- linux.orig/drivers/net/pcmcia/fmvj18x_cs.c	Mon Jan  7 23:41:09 2002
+++ linux/drivers/net/pcmcia/fmvj18x_cs.c	Fri Dec 21 18:50:47 2001
@@ -28,6 +28,9 @@
    
 ======================================================================*/
 
+#define DRV_NAME	"fmvj18x_cs"
+#define DRV_VERSION	"2.6"
+
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
@@ -39,6 +42,9 @@
 #include <linux/interrupt.h>
 #include <linux/in.h>
 #include <linux/delay.h>
+#include <linux/ethtool.h>
+
+#include <asm/uaccess.h>
 #include <asm/io.h>
 #include <asm/system.h>
 
@@ -73,7 +79,7 @@
 #ifdef PCMCIA_DEBUG
 INT_MODULE_PARM(pc_debug, PCMCIA_DEBUG);
 #define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
-static char *version = "fmvj18x_cs.c 2.6 2001/09/17";
+static char *version = DRV_NAME ".c " DRV_VERSION " 2001/09/17";
 #else
 #define DEBUG(n, args...)
 #endif
@@ -103,6 +109,7 @@
 static struct net_device_stats *fjn_get_stats(struct net_device *dev);
 static void set_rx_mode(struct net_device *dev);
 static void fjn_tx_timeout(struct net_device *dev);
+static int fjn_ioctl(struct net_device *, struct ifreq *, int);
 
 static dev_info_t dev_info = "fmvj18x_cs";
 static dev_link_t *dev_list;
@@ -315,6 +322,7 @@
     dev->tx_timeout = fjn_tx_timeout;
     dev->watchdog_timeo = TX_TIMEOUT;
 #endif
+    dev->do_ioctl = fjn_ioctl;
     
     /* Register with Card Services */
     link->next = dev_list;
@@ -1101,6 +1109,65 @@
 } /* fjn_rx */
 
 /*====================================================================*/
+
+static int netdev_ethtool_ioctl (struct net_device *dev, void *useraddr)
+{
+	u32 ethcmd;
+
+	/* dev_ioctl() in ../../net/core/dev.c has already checked
+	   capable(CAP_NET_ADMIN), so don't bother with that here.  */
+
+	if (get_user(ethcmd, (u32 *)useraddr))
+		return -EFAULT;
+
+	switch (ethcmd) {
+
+	case ETHTOOL_GDRVINFO: {
+		struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO };
+		strcpy (info.driver, DRV_NAME);
+		strcpy (info.version, DRV_VERSION);
+		sprintf(info.bus_info, "PCMCIA 0x%lx", dev->base_addr);
+		if (copy_to_user (useraddr, &info, sizeof (info)))
+			return -EFAULT;
+		return 0;
+	}
+
+#ifdef PCMCIA_DEBUG
+	/* get message-level */
+	case ETHTOOL_GMSGLVL: {
+		struct ethtool_value edata = {ETHTOOL_GMSGLVL};
+		edata.data = pc_debug;
+		if (copy_to_user(useraddr, &edata, sizeof(edata)))
+			return -EFAULT;
+		return 0;
+	}
+	/* set message-level */
+	case ETHTOOL_SMSGLVL: {
+		struct ethtool_value edata;
+		if (copy_from_user(&edata, useraddr, sizeof(edata)))
+			return -EFAULT;
+		pc_debug = edata.data;
+		return 0;
+	}
+#endif
+
+	default:
+		break;
+	}
+
+	return -EOPNOTSUPP;
+}
+
+static int fjn_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+	switch (cmd) {
+	case SIOCETHTOOL:
+		return netdev_ethtool_ioctl(dev, (void *) rq->ifr_data);
+
+	default:
+		return -EOPNOTSUPP;
+	}
+}
 
 static int fjn_config(struct net_device *dev, struct ifmap *map){
     return 0;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/pcmcia/netwave_cs.c linux/drivers/net/pcmcia/netwave_cs.c
--- linux.orig/drivers/net/pcmcia/netwave_cs.c	Mon Jan  7 23:41:09 2002
+++ linux/drivers/net/pcmcia/netwave_cs.c	Fri Dec 21 18:50:47 2001
@@ -37,6 +37,9 @@
 /* To have statistics (just packets sent) define this */
 #undef NETWAVE_STATS
 
+#define DRV_NAME	"netwave_cs"
+#define DRV_VERSION	"0.3.0"
+
 #include <linux/config.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
@@ -51,6 +54,9 @@
 #include <linux/slab.h>
 #include <linux/string.h>
 #include <linux/timer.h>
+#include <linux/ethtool.h>
+
+#include <asm/uaccess.h>
 #include <asm/system.h>
 #include <asm/bitops.h>
 #include <asm/io.h>
@@ -163,7 +169,7 @@
 MODULE_PARM(pc_debug, "i");
 #define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
 static char *version =
-"netwave_cs.c 0.3.0 Thu Jul 17 14:36:02 1997 (John Markus Bjørndalen)\n";
+DRV_NAME ".c " DRV_VERSION " Thu Jul 17 14:36:02 1997 (John Markus Bjørndalen)\n";
 #else
 #define DEBUG(n, args...)
 #endif
@@ -597,6 +603,53 @@
     }
 } /* netwave_flush_stale_links */
 
+static int netdev_ethtool_ioctl (struct net_device *dev, void *useraddr)
+{
+	u32 ethcmd;
+
+	/* dev_ioctl() in ../../net/core/dev.c has already checked
+	   capable(CAP_NET_ADMIN), so don't bother with that here.  */
+
+	if (get_user(ethcmd, (u32 *)useraddr))
+		return -EFAULT;
+
+	switch (ethcmd) {
+
+	case ETHTOOL_GDRVINFO: {
+		struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO };
+		strcpy (info.driver, DRV_NAME);
+		strcpy (info.version, DRV_VERSION);
+		sprintf(info.bus_info, "PCMCIA 0x%lx", dev->base_addr);
+		if (copy_to_user (useraddr, &info, sizeof (info)))
+			return -EFAULT;
+		return 0;
+	}
+
+#ifdef PCMCIA_DEBUG
+	/* get message-level */
+	case ETHTOOL_GMSGLVL: {
+		struct ethtool_value edata = {ETHTOOL_GMSGLVL};
+		edata.data = pc_debug;
+		if (copy_to_user(useraddr, &edata, sizeof(edata)))
+			return -EFAULT;
+		return 0;
+	}
+	/* set message-level */
+	case ETHTOOL_SMSGLVL: {
+		struct ethtool_value edata;
+		if (copy_from_user(&edata, useraddr, sizeof(edata)))
+			return -EFAULT;
+		pc_debug = edata.data;
+		return 0;
+	}
+#endif
+
+	default:
+		break;
+	}
+
+	return -EOPNOTSUPP;
+}
 /*
  * Function netwave_ioctl (dev, rq, cmd)
  *
@@ -619,6 +672,9 @@
 	
     DEBUG(0, "%s: ->netwave_ioctl(cmd=0x%X)\n", dev->name, cmd);
 	
+    if (cmd == SIOCETHTOOL)
+    	return netdev_ethtool_ioctl(dev, (void *) rq->ifr_data);
+
     /* Disable interrupts & save flags */
     save_flags(flags);
     cli();
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/pcmcia/nmclan_cs.c linux/drivers/net/pcmcia/nmclan_cs.c
--- linux.orig/drivers/net/pcmcia/nmclan_cs.c	Mon Jan  7 23:41:09 2002
+++ linux/drivers/net/pcmcia/nmclan_cs.c	Fri Dec 21 18:50:47 2001
@@ -106,6 +106,10 @@
 
 ---------------------------------------------------------------------------- */
 
+#define DRV_NAME	"nmclan_cs"
+#define DRV_VERSION	"0.16"
+
+
 /* ----------------------------------------------------------------------------
 Conditional Compilation Options
 ---------------------------------------------------------------------------- */
@@ -130,6 +134,9 @@
 #include <linux/interrupt.h>
 #include <linux/in.h>
 #include <linux/delay.h>
+#include <linux/ethtool.h>
+
+#include <asm/uaccess.h>
 #include <asm/io.h>
 #include <asm/system.h>
 #include <asm/bitops.h>
@@ -375,7 +382,7 @@
 static char rcsid[] =
 "nmclan_cs.c,v 0.16 1995/07/01 06:42:17 rpao Exp rpao";
 static char *version =
-"nmclan_cs 0.16 (Roger C. Pao)";
+DRV_NAME " " DRV_VERSION " (Roger C. Pao)";
 #endif
 
 static dev_info_t dev_info="nmclan_cs";
@@ -1001,6 +1008,66 @@
 
   return 0;
 } /* mace_close */
+
+static int netdev_ethtool_ioctl (struct net_device *dev, void *useraddr)
+{
+	u32 ethcmd;
+
+	/* dev_ioctl() in ../../net/core/dev.c has already checked
+	   capable(CAP_NET_ADMIN), so don't bother with that here.  */
+
+	if (get_user(ethcmd, (u32 *)useraddr))
+		return -EFAULT;
+
+	switch (ethcmd) {
+
+	case ETHTOOL_GDRVINFO: {
+		struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO };
+		strcpy (info.driver, DRV_NAME);
+		strcpy (info.version, DRV_VERSION);
+		sprintf(info.bus_info, "PCMCIA 0x%lx", dev->base_addr);
+		if (copy_to_user (useraddr, &info, sizeof (info)))
+			return -EFAULT;
+		return 0;
+	}
+
+#ifdef PCMCIA_DEBUG
+	/* get message-level */
+	case ETHTOOL_GMSGLVL: {
+		struct ethtool_value edata = {ETHTOOL_GMSGLVL};
+		edata.data = pc_debug;
+		if (copy_to_user(useraddr, &edata, sizeof(edata)))
+			return -EFAULT;
+		return 0;
+	}
+	/* set message-level */
+	case ETHTOOL_SMSGLVL: {
+		struct ethtool_value edata;
+		if (copy_from_user(&edata, useraddr, sizeof(edata)))
+			return -EFAULT;
+		pc_debug = edata.data;
+		return 0;
+	}
+#endif
+
+	default:
+		break;
+	}
+
+	return -EOPNOTSUPP;
+}
+
+static int smc91c92_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+	switch (cmd) {
+	case SIOCETHTOOL:
+		return netdev_ethtool_ioctl(dev, (void *) rq->ifr_data);
+
+	default:
+		return -EOPNOTSUPP;
+	}
+	return 0;
+}
 
 /* ----------------------------------------------------------------------------
 mace_start_xmit
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/pcnet32.c linux/drivers/net/pcnet32.c
--- linux.orig/drivers/net/pcnet32.c	Mon Jan  7 23:41:09 2002
+++ linux/drivers/net/pcnet32.c	Wed Dec 26 16:34:02 2001
@@ -648,6 +648,13 @@
 #if defined(__i386__)
 	    printk(KERN_WARNING "%s: Probably a Compaq, using the PROM address of", dev->name);
 	    memcpy(dev->dev_addr, promaddr, 6);
+#elif defined(__powerpc__)
+	    if (!is_valid_ether_addr(dev->dev_addr)
+		&& is_valid_ether_addr(promaddr)) {
+		    printk("\n" KERN_WARNING "%s: using PROM address:",
+			   dev->name);
+		    memcpy(dev->dev_addr, promaddr, 6);
+	    }
 #endif
 	}	    	    
     }
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/sis900.c linux/drivers/net/sis900.c
--- linux.orig/drivers/net/sis900.c	Mon Jan  7 23:41:09 2002
+++ linux/drivers/net/sis900.c	Fri Dec 21 18:31:17 2001
@@ -1,6 +1,6 @@
 /* sis900.c: A SiS 900/7016 PCI Fast Ethernet driver for Linux.
    Copyright 1999 Silicon Integrated System Corporation 
-   Revision:	1.08.01	Aug. 25 2001
+   Revision:	1.08.02	Nov. 30 2001
    
    Modified from the driver which is originally written by Donald Becker.
    
@@ -18,6 +18,7 @@
    preliminary Rev. 1.0 Jan. 18, 1998
    http://www.sis.com.tw/support/databook.htm
 
+   Rev 1.08.02 Nov. 30 2001 Hui-Fen Hsu workaround for EDB & bug fix for dhcp problem
    Rev 1.08.01 Aug. 25 2001 Hui-Fen Hsu update for 630ET & workaround for ICS1893 PHY
    Rev 1.08.00 Jun. 11 2001 Hui-Fen Hsu workaround for RTL8201 PHY and some bug fix
    Rev 1.07.11 Apr.  2 2001 Hui-Fen Hsu updates PCI drivers to use the new pci_set_dma_mask for kernel 2.4.3
@@ -55,18 +56,23 @@
 #include <linux/netdevice.h>
 #include <linux/init.h>
 #include <linux/mii.h>
-
 #include <linux/etherdevice.h>
 #include <linux/skbuff.h>
+#include <linux/delay.h>
+#include <linux/ethtool.h>
+
 #include <asm/processor.h>      /* Processor type for cache alignment. */
 #include <asm/bitops.h>
 #include <asm/io.h>
-#include <linux/delay.h>
+#include <asm/uaccess.h>	/* User space memory access functions */
 
 #include "sis900.h"
 
+#define SIS900_MODULE_NAME "sis900"
+#define SIS900_DRV_VERSION "v1.08.02 11/30/2001"
+
 static char version[] __devinitdata =
-KERN_INFO "sis900.c: v1.08.01  9/25/2001\n";
+KERN_INFO "sis900.c: " SIS900_DRV_VERSION "\n";
 
 static int max_interrupt_work = 40;
 static int multicast_filter_limit = 128;
@@ -870,6 +876,9 @@
 
 	netif_start_queue(net_dev);
 
+	/* Workaround for EDB */
+	sis900_set_mode(ioaddr, HW_SPEED_10_MBPS, FDX_CAPABLE_HALF_SELECTED);
+
 	/* Enable all known interrupts by setting the interrupt mask. */
 	outl((RxSOVR|RxORN|RxERR|RxOK|TxURN|TxERR|TxIDLE), ioaddr + imr);
 	outl(RxENA | inl(ioaddr + cr), ioaddr + cr);
@@ -1126,6 +1135,7 @@
 			sis900_set_mode(net_dev->base_addr, speed, duplex);
 			pci_read_config_byte(sis_priv->pci_dev, PCI_CLASS_REVISION, &revision);
 			sis630_set_eq(net_dev, revision);
+			netif_start_queue(net_dev);
 		}
 
 		sis_priv->timer.expires = jiffies + HZ;
@@ -1409,6 +1419,12 @@
 	unsigned int  entry;
 	unsigned long flags;
 
+	/* Don't transmit data before the complete of auto-negotiation */
+	if(!sis_priv->autong_complete){
+		netif_stop_queue(net_dev);
+		return 1;
+	}
+
 	spin_lock_irqsave(&sis_priv->lock, flags);
 
 	/* Calculate the next Tx descriptor entry. */
@@ -2091,8 +2107,6 @@
 	pci_release_regions(pci_dev);
 	pci_set_drvdata(pci_dev, NULL);
 }
-
-#define SIS900_MODULE_NAME "sis900"
 
 static struct pci_driver sis900_pci_driver = {
 	name:		SIS900_MODULE_NAME,
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/starfire.c linux/drivers/net/starfire.c
--- linux.orig/drivers/net/starfire.c	Mon Jan  7 23:41:09 2002
+++ linux/drivers/net/starfire.c	Fri Dec 21 19:09:00 2001
@@ -93,13 +93,16 @@
 	- Fixed initialization timing problems
 	- Fixed interrupt mask definitions
 
+	LK1.3.5 (jgarzik)
+	- ethtool NWAY_RST, GLINK, [GS]MSGLVL support
+
 TODO:
 	- implement tx_timeout() properly
 */
 
 #define DRV_NAME	"starfire"
-#define DRV_VERSION	"1.03+LK1.3.4"
-#define DRV_RELDATE	"August 14, 2001"
+#define DRV_VERSION	"1.03+LK1.3.5"
+#define DRV_RELDATE	"November 17, 2001"
 
 #include <linux/version.h>
 #include <linux/module.h>
@@ -1789,6 +1792,47 @@
 		return 0;
 	}
 
+	/* restart autonegotiation */
+	case ETHTOOL_NWAY_RST: {
+		int tmp;
+		int r = -EINVAL;
+		/* if autoneg is off, it's an error */
+		tmp = mdio_read(dev, np->phys[0], MII_BMCR);
+		if (tmp & BMCR_ANENABLE) {
+			tmp |= (BMCR_ANRESTART);
+			mdio_write(dev, np->phys[0], MII_BMCR, tmp);
+			r = 0;
+		}
+		return r;
+	}
+	/* get link status */
+	case ETHTOOL_GLINK: {
+		struct ethtool_value edata = {ETHTOOL_GLINK};
+		if (mdio_read(dev, np->phys[0], MII_BMSR) & BMSR_LSTATUS)
+			edata.data = 1;
+		else
+			edata.data = 0;
+		if (copy_to_user(useraddr, &edata, sizeof(edata)))
+			return -EFAULT;
+		return 0;
+	}
+
+	/* get message-level */
+	case ETHTOOL_GMSGLVL: {
+		struct ethtool_value edata = {ETHTOOL_GMSGLVL};
+		edata.data = debug;
+		if (copy_to_user(useraddr, &edata, sizeof(edata)))
+			return -EFAULT;
+		return 0;
+	}
+	/* set message-level */
+	case ETHTOOL_SMSGLVL: {
+		struct ethtool_value edata;
+		if (copy_from_user(&edata, useraddr, sizeof(edata)))
+			return -EFAULT;
+		debug = edata.data;
+		return 0;
+	}
 	default:
 		return -EOPNOTSUPP;
 	}
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/sundance.c linux/drivers/net/sundance.c
--- linux.orig/drivers/net/sundance.c	Mon Jan  7 23:41:09 2002
+++ linux/drivers/net/sundance.c	Fri Dec 21 18:56:21 2001
@@ -16,11 +16,16 @@
 
 	Support and updates available at
 	http://www.scyld.com/network/sundance.html
+
+
+	Version 1.01a (jgarzik):
+	- Replace some MII-related magic numbers with constants
+
 */
 
 #define DRV_NAME	"sundance"
-#define DRV_VERSION	"1.01"
-#define DRV_RELDATE	"4/09/00"
+#define DRV_VERSION	"1.01a"
+#define DRV_RELDATE	"11-Nov-2001"
 
 
 /* The user-configurable values.
@@ -442,7 +447,7 @@
 	int irq;
 	int i;
 	long ioaddr;
-	u16 mii_reg0;
+	u16 mii_ctl;
 	void *ring_space;
 	dma_addr_t ring_dma;
 
@@ -581,15 +586,15 @@
 		}
 	}
 	/* Reset PHY */
-	mdio_write (dev, np->phys[0], 0, 0x8000);
+	mdio_write (dev, np->phys[0], MII_BMCR, BMCR_RESET);
 	mdelay (300);
-	mdio_write (dev, np->phys[0], 0, 0x1200);
+	mdio_write (dev, np->phys[0], MII_BMCR, BMCR_ANENABLE|BMCR_ANRESTART);
 	/* Force media type */
 	if (!np->an_enable) {
-		mii_reg0 = 0;
-		mii_reg0 |= (np->speed == 100) ? 0x2000 : 0;
-		mii_reg0 |= (np->full_duplex) ? 0x0100 : 0;
-		mdio_write (dev, np->phys[0], 0, mii_reg0);
+		mii_ctl = 0;
+		mii_ctl |= (np->speed == 100) ? BMCR_SPEED100 : 0;
+		mii_ctl |= (np->full_duplex) ? BMCR_FULLDPLX : 0;
+		mdio_write (dev, np->phys[0], MII_BMCR, mii_ctl);
 		printk (KERN_INFO "Override speed=%d, %s duplex\n",
 			np->speed, np->full_duplex ? "Full" : "Half");
 
@@ -797,12 +802,12 @@
 {
 	struct netdev_private *np = dev->priv;
 	long ioaddr = dev->base_addr;
-	int mii_reg5 = mdio_read(dev, np->phys[0], 5);
-	int negotiated = mii_reg5 & np->advertising;
+	int mii_lpa = mdio_read(dev, np->phys[0], MII_LPA);
+	int negotiated = mii_lpa & np->advertising;
 	int duplex;
 	
 	/* Force media */
-	if (!np->an_enable || mii_reg5 == 0xffff) {
+	if (!np->an_enable || mii_lpa == 0xffff) {
 		if (np->full_duplex)
 			writew (readw (ioaddr + MACCtrl0) | EnbFullDuplex,
 				ioaddr + MACCtrl0);
@@ -1183,7 +1188,7 @@
 {
 	long ioaddr = dev->base_addr;
 	struct netdev_private *np = dev->priv;
-	u16 mii_reg0, mii_reg4, mii_reg5;
+	u16 mii_ctl, mii_advertise, mii_lpa;
 	int speed;
 
 	if (intr_status & IntrDrvRqst) {
@@ -1199,27 +1204,27 @@
 	}
 	if (intr_status & LinkChange) {
 		if (np->an_enable) {
-			mii_reg4 = mdio_read (dev, np->phys[0], 4);
-			mii_reg5= mdio_read (dev, np->phys[0], 5);
-			mii_reg4 &= mii_reg5;
+			mii_advertise = mdio_read (dev, np->phys[0], MII_ADVERTISE);
+			mii_lpa= mdio_read (dev, np->phys[0], MII_LPA);
+			mii_advertise &= mii_lpa;
 			printk (KERN_INFO "%s: Link changed: ", dev->name);
-			if (mii_reg4 & 0x0100)
+			if (mii_advertise & ADVERTISE_100FULL)
 				printk ("100Mbps, full duplex\n");
-			else if (mii_reg4 & 0x0080)
+			else if (mii_advertise & ADVERTISE_100HALF)
 				printk ("100Mbps, half duplex\n");
-			else if (mii_reg4 & 0x0040)
+			else if (mii_advertise & ADVERTISE_10FULL)
 				printk ("10Mbps, full duplex\n");
-			else if (mii_reg4 & 0x0020)
+			else if (mii_advertise & ADVERTISE_10HALF)
 				printk ("10Mbps, half duplex\n");
 			else
 				printk ("\n");
 
 		} else {
-			mii_reg0 = mdio_read (dev, np->phys[0], 0);
-			speed = (mii_reg0 & 0x2000) ? 100 : 10;
+			mii_ctl = mdio_read (dev, np->phys[0], MII_BMCR);
+			speed = (mii_ctl & BMCR_SPEED100) ? 100 : 10;
 			printk (KERN_INFO "%s: Link changed: %dMbps ,",
 				dev->name, speed);
-			printk ("%s duplex.\n", (mii_reg0 & 0x0100) ?
+			printk ("%s duplex.\n", (mii_ctl & BMCR_FULLDPLX) ?
 				"full" : "half");
 		}
 		check_duplex (dev);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/wireless/airo.c linux/drivers/net/wireless/airo.c
--- linux.orig/drivers/net/wireless/airo.c	Mon Jan  7 23:41:09 2002
+++ linux/drivers/net/wireless/airo.c	Wed Dec 26 16:16:25 2001
@@ -47,7 +47,7 @@
 #include <asm/uaccess.h>
 
 #ifdef CONFIG_PCI
-static struct pci_device_id card_ids[] = __devinitdata {
+static struct pci_device_id card_ids[] __devinitdata = {
 	{ 0x14b9, 1, PCI_ANY_ID, PCI_ANY_ID, },
 	{ 0x14b9, 0x4500, PCI_ANY_ID, PCI_ANY_ID },
 	{ 0x14b9, 0x4800, PCI_ANY_ID, PCI_ANY_ID, },
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/parport/ChangeLog linux/drivers/parport/ChangeLog
--- linux.orig/drivers/parport/ChangeLog	Fri Dec 21 17:41:55 2001
+++ linux/drivers/parport/ChangeLog	Mon Jan  7 13:38:04 2002
@@ -1,3 +1,51 @@
+2002-01-04  Tim Waugh  <twaugh@redhat.com>
+
+	* share.c (parport_claim_or_block): Sleep interruptibly to prevent
+	a possible deadlock.
+
+2001-12-07  Damian Gruszka  <damian.gruszka@VisionSystems.de>
+
+	* parport_pc.c (ECR_WRITE): Define.  If there are forbidden bits
+	in the ECR register for some chips, this will be a useful place to
+	put that knowledge.
+	(change_mode): Use ECR_WRITE.
+	(parport_pc_restore_state): Likewise.
+	(parport_ECPPS2_supported): Likewise.
+	(parport_ECPEPP_supported): Likewise.
+	(irq_probe_EPP): Likewise.
+	(programmable_irq_support): Likewise.
+	(programmable_dma_support): Likewise.
+	(parport_pc_probe_port): Likewise.
+
+	(frob_set_mode): New function.  Set the mode bits of the ECR.
+	(get_fifo_residue): Use frob_set_mode.
+	(parport_pc_ecpepp_read_data): Likewise.
+	(parport_pc_ecpepp_write_data): Likewise.
+	(parport_pc_ecpepp_read_addr): Likewise.
+	(parport_pc_ecpepp_write_addr): Likewise.
+	(parport_pc_compat_write_block_pio): Likewise.
+	(parport_pc_ecp_write_block_pio): Likewise.
+	(parport_ECR_present): Likewise.
+	(parport_ECP_supported): Likewise.
+	(parport_EPP_supported): Likewise.
+	(parport_ECPEPP_supported): Likewise.
+	(programmable_irq_support): Likewise.
+	(irq_probe_ECP): Likewise.
+	(programmable_dma_support): Likewise.
+
+	(parport_pc_enable_irq): Only enable interrupts if we know which
+	IRQ line they will come from.
+	(parport_pc_init_state): Set nErrIntrEn at initialisation.
+	(parport_pc_restore_state): Only write writable bits of CTR.
+	(parport_irq_probe): If no IRQ is found, take ackIntEn out of the
+	writable bit set.
+
+2001-12-07  Tim Waugh  <twaugh@redhat.com>
+
+	* parport_pc.c (parport_pc_fifo_write_block_pio): Correct typo.
+	(parport_pc_init_state): Only set ackIntEn if we know which IRQ
+	line the interrupts will come from.
+
 2001-12-07  Tim Waugh  <twaugh@redhat.com>
 
 	* ieee1284_ops.c (parport_ieee1284_epp_write_addr,
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/parport/parport_pc.c linux/drivers/parport/parport_pc.c
--- linux.orig/drivers/parport/parport_pc.c	Fri Dec 21 17:41:55 2001
+++ linux/drivers/parport/parport_pc.c	Mon Jan  7 13:38:04 2002
@@ -73,6 +73,8 @@
 #define ECR_VND 05
 #define ECR_TST 06
 #define ECR_CNF 07
+#define ECR_MODE_MASK 0xe0
+#define ECR_WRITE(p,v) frob_econtrol((p),0xff,(v))
 
 #undef DEBUG
 
@@ -100,13 +102,22 @@
 static void frob_econtrol (struct parport *pb, unsigned char m,
 			   unsigned char v)
 {
-	unsigned char ectr = inb (ECONTROL (pb));
+	unsigned char ectr = 0;
+
+	if (m != 0xff)
+		ectr = inb (ECONTROL (pb));
+
 	DPRINTK (KERN_DEBUG "frob_econtrol(%02x,%02x): %02x -> %02x\n",
 		m, v, ectr, (ectr & ~m) ^ v);
 
 	outb ((ectr & ~m) ^ v, ECONTROL (pb));
 }
 
+static void __inline__ frob_set_mode (struct parport *p, int mode)
+{
+	frob_econtrol (p, ECR_MODE_MASK, mode << 5);
+}
+
 #ifdef CONFIG_PARPORT_PC_FIFO
 /* Safely change the mode bits in the ECR 
    Returns:
@@ -166,13 +177,13 @@
 		/* We have to go through mode 001 */
 		oecr &= ~(7 << 5);
 		oecr |= ECR_PS2 << 5;
-		outb (oecr, ecr);
+		ECR_WRITE (p, oecr);
 	}
 
 	/* Set the mode. */
 	oecr &= ~(7 << 5);
 	oecr |= m << 5;
-	outb (oecr, ecr);
+	ECR_WRITE (p, oecr);
 	return 0;
 }
 
@@ -197,10 +208,10 @@
 		residue);
 
 	/* Reset the FIFO. */
-	frob_econtrol (p, 0xe0, ECR_PS2 << 5);
+	frob_set_mode (p, ECR_PS2);
 
 	/* Now change to config mode and clean up. FIXME */
-	frob_econtrol (p, 0xe0, ECR_CNF << 5);
+	frob_set_mode (p, ECR_CNF);
 	cnfga = inb (CONFIGA (p));
 	printk (KERN_DEBUG "%s: cnfgA contains 0x%02x\n", p->name, cnfga);
 
@@ -213,7 +224,7 @@
 	 * PWord != 1 byte. */
 
 	/* Back to PS2 mode. */
-	frob_econtrol (p, 0xe0, ECR_PS2 << 5);
+	frob_set_mode (p, ECR_PS2);
 
 	DPRINTK (KERN_DEBUG "*** get_fifo_residue: done residue collecting (ecr = 0x%2.2x)\n", inb (ECONTROL (p)));
 	return residue;
@@ -331,7 +342,8 @@
 
 void parport_pc_enable_irq(struct parport *p)
 {
-	__parport_pc_frob_control (p, 0x10, 0x10);
+	if (p->irq != PARPORT_IRQ_NONE)
+		__parport_pc_frob_control (p, 0x10, 0x10);
 }
 
 void parport_pc_data_forward (struct parport *p)
@@ -346,8 +358,14 @@
 
 void parport_pc_init_state(struct pardevice *dev, struct parport_state *s)
 {
-	s->u.pc.ctr = 0xc | (dev->irq_func ? 0x10 : 0x0);
-	s->u.pc.ecr = 0x24;
+	s->u.pc.ctr = 0xc;
+	if (dev->irq_func &&
+	    dev->port->irq != PARPORT_IRQ_NONE)
+		/* Set ackIntEn */
+		s->u.pc.ctr |= 0x10;
+
+	s->u.pc.ecr = 0x34; /* NetMos chip can cause problems 0x24;
+			     * D.Gruszka VScom */
 }
 
 void parport_pc_save_state(struct parport *p, struct parport_state *s)
@@ -361,10 +379,11 @@
 void parport_pc_restore_state(struct parport *p, struct parport_state *s)
 {
 	struct parport_pc_private *priv = p->physport->private_data;
-	outb (s->u.pc.ctr, CONTROL (p));
-	priv->ctr = s->u.pc.ctr;
+	register unsigned char c = s->u.pc.ctr & priv->ctr_writable;
+	outb (c, CONTROL (p));
+	priv->ctr = c;
 	if (priv->ecr)
-		outb (s->u.pc.ecr, ECONTROL (p));
+		ECR_WRITE (p, s->u.pc.ecr);
 }
 
 #ifdef CONFIG_PARPORT_1284
@@ -516,11 +535,11 @@
 {
 	size_t got;
 
-	frob_econtrol (port, 0xe0, ECR_EPP << 5);
+	frob_set_mode (port, ECR_EPP);
 	parport_pc_data_reverse (port);
 	parport_pc_write_control (port, 0x4);
 	got = parport_pc_epp_read_data (port, buf, length, flags);
-	frob_econtrol (port, 0xe0, ECR_PS2 << 5);
+	frob_set_mode (port, ECR_PS2);
 
 	return got;
 }
@@ -531,11 +550,11 @@
 {
 	size_t written;
 
-	frob_econtrol (port, 0xe0, ECR_EPP << 5);
+	frob_set_mode (port, ECR_EPP);
 	parport_pc_write_control (port, 0x4);
 	parport_pc_data_forward (port);
 	written = parport_pc_epp_write_data (port, buf, length, flags);
-	frob_econtrol (port, 0xe0, ECR_PS2 << 5);
+	frob_set_mode (port, ECR_PS2);
 
 	return written;
 }
@@ -545,11 +564,11 @@
 {
 	size_t got;
 
-	frob_econtrol (port, 0xe0, ECR_EPP << 5);
+	frob_set_mode (port, ECR_EPP);
 	parport_pc_data_reverse (port);
 	parport_pc_write_control (port, 0x4);
 	got = parport_pc_epp_read_addr (port, buf, length, flags);
-	frob_econtrol (port, 0xe0, ECR_PS2 << 5);
+	frob_set_mode (port, ECR_PS2);
 
 	return got;
 }
@@ -560,11 +579,11 @@
 {
 	size_t written;
 
-	frob_econtrol (port, 0xe0, ECR_EPP << 5);
+	frob_set_mode (port, ECR_EPP);
 	parport_pc_write_control (port, 0x4);
 	parport_pc_data_forward (port);
 	written = parport_pc_epp_write_addr (port, buf, length, flags);
-	frob_econtrol (port, 0xe0, ECR_PS2 << 5);
+	frob_set_mode (port, ECR_PS2);
 
 	return written;
 }
@@ -663,7 +682,7 @@
 		left--;
         }
 
-dump_parport_state ("leave fifo_write_block_dma", port);
+dump_parport_state ("leave fifo_write_block_pio", port);
 	return length - left;
 }
 
@@ -852,7 +871,7 @@
 		}
 
 		/* Reset the FIFO and return to PS2 mode. */
-		frob_econtrol (port, 0xe0, ECR_PS2 << 5);
+		frob_set_mode (port, ECR_PS2);
 	}
 
 	r = parport_wait_peripheral (port,
@@ -949,7 +968,7 @@
 		}
 
 		/* Reset the FIFO and return to PS2 mode. */
-		frob_econtrol (port, 0xe0, ECR_PS2 << 5);
+		frob_set_mode (port, ECR_PS2);
 
 		/* Host transfer recovery. */
 		parport_pc_data_reverse (port); /* Must be in PS2 mode */
@@ -1727,7 +1746,7 @@
 	outb (0xc, CONTROL (pb));
 	
 	/* Go to mode 000 */
-	frob_econtrol (pb, 0xe0, ECR_SPP << 5);
+	frob_set_mode (pb, ECR_SPP);
 
 	return 1;
 
@@ -1797,8 +1816,8 @@
 		return 0;
 
 	/* Find out FIFO depth */
-	outb (ECR_SPP << 5, ECONTROL (pb)); /* Reset FIFO */
-	outb (ECR_TST << 5, ECONTROL (pb)); /* TEST FIFO */
+	frob_set_mode (pb, ECR_SPP); /* Reset FIFO */
+	frob_set_mode (pb, ECR_TST); /* TEST FIFO */
 	for (i=0; i < 1024 && !(inb (ECONTROL (pb)) & 0x02); i++)
 		outb (0xaa, FIFO (pb));
 
@@ -1807,7 +1826,7 @@
 	 * it doesn't support ECP or FIFO MODE
 	 */
 	if (i == 1024) {
-		outb (ECR_SPP << 5, ECONTROL (pb));
+		frob_set_mode (pb, ECR_SPP);
 		return 0;
 	}
 
@@ -1837,9 +1856,9 @@
 	priv->writeIntrThreshold = i;
 
 	/* Find out readIntrThreshold */
-	frob_econtrol (pb, 0xe0, ECR_PS2 << 5); /* Reset FIFO and enable PS2 */
+	frob_set_mode (pb, ECR_PS2); /* Reset FIFO and enable PS2 */
 	parport_pc_data_reverse (pb); /* Must be in PS2 mode */
-	frob_econtrol (pb, 0xe0, ECR_TST << 5); /* Test FIFO */
+	frob_set_mode (pb, ECR_TST); /* Test FIFO */
 	frob_econtrol (pb, 1<<2, 1<<2);
 	frob_econtrol (pb, 1<<2, 0);
 	for (i = 1; i <= priv->fifo_depth; i++) {
@@ -1858,7 +1877,7 @@
 
 	priv->readIntrThreshold = i;
 
-	outb (ECR_SPP << 5, ECONTROL (pb)); /* Reset FIFO */
+	frob_set_mode (pb, ECR_SPP); /* Reset FIFO */
 	outb (0xf4, ECONTROL (pb)); /* Configuration mode */
 	config = inb (CONFIGA (pb));
 	pword = (config >> 4) & 0x7;
@@ -1904,7 +1923,7 @@
 	}
 
 	/* Go back to mode 000 */
-	frob_econtrol (pb, 0xe0, ECR_SPP << 5);
+	frob_set_mode (pb, ECR_SPP);
 
 	return 1;
 }
@@ -1920,11 +1939,9 @@
 		return 0;
 
 	oecr = inb (ECONTROL (pb));
-	outb (ECR_PS2 << 5, ECONTROL (pb));
-	
+	frob_set_mode (pb, ECR_PS2);
 	result = parport_PS2_supported(pb);
-
-	outb (oecr, ECONTROL (pb));
+	ECR_WRITE (pb, oecr);
 	return result;
 }
 
@@ -1955,8 +1972,8 @@
 	/* Check for Intel bug. */
 	if (priv->ecr) {
 		unsigned char i;
-		for (i = 0x00; i < 0x80; i += 0x20) {
-			outb (i, ECONTROL (pb));
+		for (i = 0; i < 4; i++) {
+			frob_set_mode (pb, i);
 			if (clear_epp_timeout (pb)) {
 				/* Phony EPP in ECP. */
 				return 0;
@@ -1987,11 +2004,11 @@
 
 	oecr = inb (ECONTROL (pb));
 	/* Search for SMC style EPP+ECP mode */
-	outb (0x80, ECONTROL (pb));
+	frob_set_mode (pb, ECR_EPP);
 	outb (0x04, CONTROL (pb));
 	result = parport_EPP_supported(pb);
 
-	outb (oecr, ECONTROL (pb));
+	ECR_WRITE (pb, oecr);
 
 	if (result) {
 		/* Set up access functions to use ECP+EPP hardware. */
@@ -2028,12 +2045,12 @@
 		PARPORT_IRQ_NONE, 7, 9, 10, 11, 14, 15, 5
 	};
 
-	outb (ECR_CNF << 5, ECONTROL (pb)); /* Configuration MODE */
+	frob_set_mode (pb, ECR_CNF); /* Configuration MODE */
 
 	intrLine = (inb (CONFIGB (pb)) >> 3) & 0x07;
 	irq = lookup[intrLine];
 
-	outb (oecr, ECONTROL (pb));
+	ECR_WRITE (pb, oecr);
 	return irq;
 }
 
@@ -2045,16 +2062,16 @@
 	sti();
 	irqs = probe_irq_on();
 		
-	outb (ECR_SPP << 5, ECONTROL (pb)); /* Reset FIFO */
-	outb ((ECR_TST << 5) | 0x04, ECONTROL (pb));
-	outb (ECR_TST << 5, ECONTROL (pb));
+	frob_set_mode (pb, ECR_SPP); /* Reset FIFO */
+	frob_econtrol (pb, ECR_MODE_MASK | 0x04, (ECR_TST << 5) | 0x04);
+	frob_set_mode (pb, ECR_TST);
 
 	/* If Full FIFO sure that writeIntrThreshold is generated */
 	for (i=0; i < 1024 && !(inb (ECONTROL (pb)) & 0x02) ; i++) 
 		outb (0xaa, FIFO (pb));
 		
 	pb->irq = probe_irq_off(irqs);
-	outb (ECR_SPP << 5, ECONTROL (pb));
+	frob_set_mode (pb, ECR_SPP);
 
 	if (pb->irq <= 0)
 		pb->irq = PARPORT_IRQ_NONE;
@@ -2096,7 +2113,7 @@
 
 	pb->irq = probe_irq_off (irqs);
 	if (pb->modes & PARPORT_MODE_PCECR)
-		outb (oecr, ECONTROL (pb));
+		ECR_WRITE (pb, oecr);
 	parport_pc_write_control(pb, 0xc);
 
 	if (pb->irq <= 0)
@@ -2121,7 +2138,9 @@
  */
 static int __devinit parport_irq_probe(struct parport *pb)
 {
-	const struct parport_pc_private *priv = pb->private_data;
+	struct parport_pc_private *priv = pb->private_data;
+
+	priv->ctr_writable |= 0x10;
 
 	if (priv->ecr) {
 		pb->irq = programmable_irq_support(pb);
@@ -2147,6 +2166,9 @@
 	if (pb->irq == PARPORT_IRQ_NONE)
 		pb->irq = get_superio_irq(pb);
 
+	if (pb->irq == PARPORT_IRQ_NONE)
+		priv->ctr_writable &= ~0x10;
+
 	return pb->irq;
 }
 
@@ -2158,7 +2180,7 @@
 	unsigned char oecr = inb (ECONTROL (p));
 	int dma;
 
-	frob_econtrol (p, 0xe0, ECR_CNF << 5);
+	frob_set_mode (p, ECR_CNF);
 	
 	dma = inb (CONFIGB(p)) & 0x07;
 	/* 000: Indicates jumpered 8-bit DMA if read-only.
@@ -2166,7 +2188,7 @@
 	if ((dma & 0x03) == 0)
 		dma = PARPORT_DMA_NONE;
 
-	outb (oecr, ECONTROL (p));
+	ECR_WRITE (p, oecr);
 	return dma;
 }
 
@@ -2212,7 +2234,7 @@
 	}
 	memcpy (ops, &parport_pc_ops, sizeof (struct parport_operations));
 	priv->ctr = 0xc;
-	priv->ctr_writable = 0xff;
+	priv->ctr_writable = ~0x10;
 	priv->ecr = 0;
 	priv->fifo_depth = 0;
 	priv->dma_buf = 0;
@@ -2374,7 +2396,7 @@
 		 * Put the ECP detected port in PS2 mode.
 		 * Do this also for ports that have ECR but don't do ECP.
 		 */
-		outb (0x34, ECONTROL (p));
+		ECR_WRITE (p, 0x34);
 
 	parport_pc_write_data(p, 0);
 	parport_pc_data_forward (p);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/parport/share.c linux/drivers/parport/share.c
--- linux.orig/drivers/parport/share.c	Mon Oct  8 18:54:10 2001
+++ linux/drivers/parport/share.c	Mon Jan  7 13:38:04 2002
@@ -1011,7 +1011,11 @@
 		/* If dev->waiting is clear now, an interrupt
 		   gave us the port and we would deadlock if we slept.  */
 		if (dev->waiting) {
-			sleep_on(&dev->wait_q);
+			interruptible_sleep_on (&dev->wait_q);
+			if (signal_pending (current)) {
+				restore_flags (flags);
+				return -EINTR;
+			}
 			r = 1;
 		} else {
 			r = 0;
@@ -1084,7 +1088,7 @@
 		if (pd->waiting & 2) { /* sleeping in claim_or_block */
 			parport_claim(pd);
 			if (waitqueue_active(&pd->wait_q))
-				wake_up(&pd->wait_q);
+				wake_up_interruptible(&pd->wait_q);
 			return;
 		} else if (pd->wakeup) {
 			pd->wakeup(pd->private);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/s390/Makefile linux/drivers/s390/Makefile
--- linux.orig/drivers/s390/Makefile	Mon Jan  7 23:41:09 2002
+++ linux/drivers/s390/Makefile	Fri Dec 21 16:25:30 2001
@@ -7,7 +7,7 @@
 subdir-y := block char misc net
 subdir-m := $(subdir-y)
 
-obj-y := s390io.o s390mach.o s390dyn.o idals.o ccwcache.o
+obj-y := s390io.o s390mach.o s390dyn.o idals.o ccwcache.o sysinfo.o
 export-objs += ccwcache.o idals.o s390dyn.o s390io.o
 
 obj-y += $(foreach dir,$(subdir-y),$(dir)/s390-$(dir).o)
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/s390/block/dasd.c linux/drivers/s390/block/dasd.c
--- linux.orig/drivers/s390/block/dasd.c	Mon Jan  7 23:41:09 2002
+++ linux/drivers/s390/block/dasd.c	Fri Dec 21 16:25:30 2001
@@ -113,6 +113,7 @@
 EXPORT_SYMBOL (dasd_default_erp_postaction);
 EXPORT_SYMBOL (dasd_sleep_on_req);
 EXPORT_SYMBOL (dasd_set_normalized_cda);
+EXPORT_SYMBOL (dasd_device_from_kdev);
 
 /* SECTION: Constant definitions to be used within this file */
 
@@ -668,12 +669,18 @@
         /* init devfs array */
 	major_info->gendisk.de_arr = (devfs_handle_t *)
 	    kmalloc (DASD_PER_MAJOR * sizeof (devfs_handle_t), GFP_KERNEL);
+	if(major_info->gendisk.de_arr == NULL)
+		goto out_gd_de_arr;
+
 	memset (major_info->gendisk.de_arr, 0,
 		DASD_PER_MAJOR * sizeof (devfs_handle_t));
 
         /* init flags */
 	major_info->gendisk.flags = (char *)
 	    kmalloc (DASD_PER_MAJOR * sizeof (char), GFP_KERNEL);
+	if(major_info->gendisk.flags == NULL)
+		goto out_gd_flags;
+
 	memset (major_info->gendisk.flags, 0, DASD_PER_MAJOR * sizeof (char));
 
         /* register blockdevice */
@@ -792,10 +799,13 @@
 		major_info->flags &= ~DASD_MAJOR_INFO_REGISTERED;
 	}
 
-      out_reg_blkdev:
-        kfree (major_info->gendisk.flags);
-        kfree (major_info->gendisk.de_arr);
+out_reg_blkdev:
+	kfree (major_info->gendisk.flags);
+
+out_gd_flags:
+	kfree (major_info->gendisk.de_arr);
 
+out_gd_de_arr:
 	/* Delete the new major info from dasd_major_info if needed */
 	if (!(major_info->flags & DASD_MAJOR_INFO_IS_STATIC)) {
 		kfree (major_info);
@@ -861,7 +871,7 @@
  * finds the device structure corresponding to the kdev supplied as argument
  * in the major_info structures and returns it or NULL when not found
  */
-static inline dasd_device_t *
+dasd_device_t *
 dasd_device_from_kdev (kdev_t kdev)
 {
 	major_info_t *major_info = NULL;
@@ -1873,15 +1883,15 @@
 	dasd_era_t era = dasd_era_none; /* default is everything is okay */
 	devstat_t *stat = (devstat_t *)ds;
 
+        if (stat == NULL) {
+                BUG();
+	}
         DASD_DRIVER_DEBUG_EVENT (6, dasd_int_handler,
                                  "Interrupt: IRQ %02x, stat %02x, devno %04x",
                                  irq,
                                  stat->dstat,
                                  stat->devno);
         asm volatile ("STCK %0":"=m" (now));
-        if (stat == NULL) {
-                BUG();
-	}
 
         /* first of all check for state change pending interrupt */
         if ((stat->dstat & DEV_STAT_ATTENTION ) && 
@@ -2313,6 +2323,18 @@
 			rc = put_user(ver, (int *) data);
 			break;
         }
+	case BLKGETSIZE:{	/* Return device size */
+			long blocks = major_info->gendisk.sizes 
+                                      [MINOR (inp->i_rdev)] << 1;
+			rc = put_user(blocks, (long *) data);
+			break;
+		}
+	case BLKGETSIZE64:{
+			u64 blocks = major_info->gendisk.sizes 
+                                      [MINOR (inp->i_rdev)];
+			rc = put_user(blocks << 10, (u64 *) data);
+			break;
+		}
 	case BLKRRPART:{
 			if (!capable (CAP_SYS_ADMIN)) {
 				rc = -EACCES;
@@ -2508,8 +2530,6 @@
 		break;
 		}
 #endif /* 0 */
-	case BLKGETSIZE:
-	case BLKGETSIZE64:
 	case BLKSSZGET:
 	case BLKROSET:
 	case BLKROGET:
@@ -2647,7 +2667,6 @@
                 invalidate_buffers (inp->i_rdev);
                 if ( device->discipline->owner )
                         __MOD_DEC_USE_COUNT(device->discipline->owner);
-                MOD_DEC_USE_COUNT;
 	} else if ( count == -1 ) { /* paranoia only */
                 atomic_set (&device->open_count,0);
                 printk (KERN_WARNING PRINTK_HEADER
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/s390/block/dasd_3990_erp.c linux/drivers/s390/block/dasd_3990_erp.c
--- linux.orig/drivers/s390/block/dasd_3990_erp.c	Mon Jan  7 23:41:09 2002
+++ linux/drivers/s390/block/dasd_3990_erp.c	Fri Dec 21 16:25:30 2001
@@ -237,8 +237,9 @@
 	/* check for 'No Record Found' */
 	if (sense[1] & SNS1_NO_REC_FOUND) {
                 
-                DASD_MESSAGE (KERN_ERR, device, "%s",
-                              "EXAMINE 24: No Record Found detected "
+                DASD_MESSAGE (KERN_ERR, device,
+                              "EXAMINE 24: No Record Found detected %s",
+                              cqr == device->init_cqr ? " " :
                               "- fatal error");
 
                 return dasd_era_fatal;
@@ -305,8 +306,9 @@
                        devstat_t *stat)
 {
 
-	char       *sense = stat->ii.sense.data;
-        dasd_era_t era    = dasd_era_recover;
+	char          *sense  = stat->ii.sense.data;
+        dasd_era_t     era    = dasd_era_recover;
+        dasd_device_t *device = cqr->device;
 
 	/* check for successful execution first */
 	if (stat->cstat == 0x00                                 &&
@@ -327,7 +329,8 @@
 	} 
 
         /* log the erp chain if fatal error occurred */
-        if (era == dasd_era_fatal) {
+        if ((era == dasd_era_fatal  ) &&
+            (cqr != device->init_cqr)   ){
 
                 log_erp_chain (cqr, 
                                0, 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/s390/block/dasd_eckd.c linux/drivers/s390/block/dasd_eckd.c
--- linux.orig/drivers/s390/block/dasd_eckd.c	Mon Jan  7 23:41:09 2002
+++ linux/drivers/s390/block/dasd_eckd.c	Fri Dec 21 16:25:30 2001
@@ -585,7 +585,7 @@
 	/* Free the cqr and cleanup device->sizes */
         if ( status != CQR_STATUS_DONE ) {
                 DASD_MESSAGE (KERN_WARNING,device,"%s",
-                              "volume analysis returned fatal error");
+                              "volume analysis returned unformatted disk");
                 return -EMEDIUMTYPE;
         }
 	/* Check Track 0 for Compatible Disk Layout */
@@ -1097,8 +1097,14 @@
                         ccw->flags |= CCW_FLAG_CC;
                         ccw->cmd_code = locate4k_set ? rw_cmd :
                                 dasd_eckd_cdl_cmd (device, recid, req->cmd);
-                        ccw->count = locate4k_set ? byt_per_blk :
-                                dasd_eckd_cdl_reclen (device, recid);
+                        ccw->count = byt_per_blk;
+                        if (!locate4k_set) {
+                                ccw->count = dasd_eckd_cdl_reclen (device,recid);
+                                if (ccw->count < byt_per_blk) {
+                                    memset (bh->b_data + size + ccw->count,
+                                            0xE5, byt_per_blk - ccw->count);
+                                }
+                        }
                         if (dasd_set_normalized_cda (ccw, __pa (bh->b_data+size), rw_cp, device)) {
                                 goto clear_rw_cp;
                         }
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/s390/block/dasd_int.h linux/drivers/s390/block/dasd_int.h
--- linux.orig/drivers/s390/block/dasd_int.h	Mon Jan  7 23:41:09 2002
+++ linux/drivers/s390/block/dasd_int.h	Fri Dec 21 16:25:30 2001
@@ -259,7 +259,6 @@
 typedef int (*dasd_info_fn_t) (struct dasd_device_t *, dasd_information_t *);
 typedef int (*dasd_use_count_fn_t) (int);
 
-
 /*
  * the dasd_discipline_t is
  * sth like a table of virtual functions, if you think of dasd_eckd
@@ -364,6 +363,7 @@
 void dasd_schedule_bh (dasd_device_t *);
 int dasd_sleep_on_req(ccw_req_t*);
 int  dasd_set_normalized_cda ( ccw1_t * cp, unsigned long address, ccw_req_t* request, dasd_device_t* device );
+dasd_device_t * dasd_device_from_kdev (kdev_t kdev);
 
 extern debug_info_t *dasd_debug_area;
 extern int (*genhd_dasd_name) (char *, int, int, struct gendisk *);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/s390/misc/chandev.c linux/drivers/s390/misc/chandev.c
--- linux.orig/drivers/s390/misc/chandev.c	Mon Jan  7 23:41:09 2002
+++ linux/drivers/s390/misc/chandev.c	Fri Dec 21 16:25:30 2001
@@ -1057,6 +1057,9 @@
 	chandev_add_model(chandev_type_osad,0x3088,0x62,-1,-1,0,default_msck_bits,FALSE,FALSE);
 	/* claw */
 	chandev_add_model(chandev_type_claw,0x3088,0x61,-1,-1,0,default_msck_bits,FALSE,FALSE);
+
+	/* ficon attached ctc */
+	chandev_add_model(chandev_type_escon,0x3088,0x1E,-1,-1,0,default_msck_bits,FALSE,FALSE);
 }
 
 
@@ -3092,7 +3095,7 @@
 				chandevs_detected=TRUE;
 				if(pass)
 				{
-					chandev_printf(chan_exit,"0x%04x 0x%04x 0x%02x  0x%04x 0x%02x  0x%04x 0x%02x 0x%02x 0x%016Lx  %-5s %-5s\n",
+					chandev_printf(chan_exit,"0x%04x 0x%04x 0x%02x  0x%04x 0x%02x  0x%04x 0x%02x 0x%02x 0x%016Lx  %-5s%-5s\n",
 						       curr_irq,curr_devinfo.devno,
 						       ( curr_force ? curr_force->chan_type : 
 						       ( curr_model ? curr_model->chan_type : 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/s390/net/ctcmain.c linux/drivers/s390/net/ctcmain.c
--- linux.orig/drivers/s390/net/ctcmain.c	Mon Jan  7 23:41:09 2002
+++ linux/drivers/s390/net/ctcmain.c	Fri Dec 21 16:25:30 2001
@@ -1,5 +1,5 @@
 /*
- * $Id: ctcmain.c,v 1.51 2001/09/24 10:38:02 mschwide Exp $
+ * $Id: ctcmain.c,v 1.55 2001/12/03 14:28:45 felfert Exp $
  *
  * CTC / ESCON network driver
  *
@@ -35,7 +35,7 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
- * RELEASE-TAG: CTC/ESCON network driver $Revision: 1.51 $
+ * RELEASE-TAG: CTC/ESCON network driver $Revision: 1.55 $
  *
  */
 
@@ -96,7 +96,9 @@
 #ifdef MODULE
 MODULE_AUTHOR("(C) 2000 IBM Corp. by Fritz Elfert (felfert@millenux.com)");
 MODULE_DESCRIPTION("Linux for S/390 CTC/Escon Driver");
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,12))
 MODULE_LICENSE("GPL");
+#endif
 #ifndef CTC_CHANDEV
 MODULE_PARM(ctc, "s");
 MODULE_PARM_DESC(ctc,
@@ -368,7 +370,7 @@
 static __inline__ void ctc_clear_busy(net_device *dev)
 {
 	clear_bit(0, &(((ctc_priv *)dev->priv)->tbusy));
-	netif_start_queue(dev);
+	netif_wake_queue(dev);
 }
 
 static __inline__ int ctc_test_and_set_busy(net_device *dev)
@@ -385,7 +387,7 @@
  */
 static void print_banner(void) {
 	static int printed = 0;
-	char vbuf[] = "$Revision: 1.51 $";
+	char vbuf[] = "$Revision: 1.55 $";
 	char *version = vbuf;
 
 	if (printed)
@@ -396,7 +398,16 @@
 			*p = '\0';
 	} else
 		version = " ??? ";
-	printk(KERN_INFO "CTC driver Version%s initialized\n", version);
+	printk(KERN_INFO
+	       "CTC driver Version%swith"
+#ifndef CTC_CHANDEV
+	       "out"
+#endif
+	       " CHANDEV support"
+#ifdef DEBUG
+	       " (DEBUG-VERSION, " __DATE__ __TIME__ ")"
+#endif
+	       " initialized\n", version);
 	printed = 1;
 }
 
@@ -754,12 +765,13 @@
 		pskb->protocol = ntohs(header->type);
 		header->length -= LL_HEADER_LENGTH;
 		if ((header->length == 0) ||
-		    (header->length > skb_tailroom(pskb))) {
+		    (header->length > skb_tailroom(pskb)) ||
+		    (header->length > len)) {
 			printk(KERN_WARNING
 			       "%s Illegal packet size %d "
-			       "received (MTU=%d), "
+			       "received (MTU=%d blocklen=%d), "
 			       "dropping\n", dev->name, header->length,
-			       dev->mtu);
+			       dev->mtu, len);
 #ifdef DEBUG
 			ctc_dump_skb(pskb, -6);
 #endif
@@ -929,7 +941,8 @@
 		if (ch->trans_skb != NULL)
 			dev_kfree_skb(ch->trans_skb);
 		clear_normalized_cda(&ch->ccw[1]);
-		ch->trans_skb = dev_alloc_skb(ch->max_bufsize);
+		ch->trans_skb = __dev_alloc_skb(ch->max_bufsize,
+						GFP_ATOMIC|GFP_DMA);
 		if (ch->trans_skb == NULL) {
 			if (warn)
 				printk(KERN_WARNING
@@ -1026,7 +1039,6 @@
 			ch->prof.maxmulti = ch->collect_len + 2;
 		if (ch->prof.maxcqueue < skb_queue_len(&ch->collect_queue))
 			ch->prof.maxcqueue = skb_queue_len(&ch->collect_queue);
-		ch->ccw[1].count = ch->collect_len + 2;
 		*((__u16 *)skb_put(ch->trans_skb, 2)) = ch->collect_len + 2;
 		i = 0;
 		while ((skb = skb_dequeue(&ch->collect_queue))) {
@@ -1039,11 +1051,10 @@
 			i++;
 		}
 		ch->collect_len = 0;
+		spin_unlock(&ch->collect_lock);
+		ch->ccw[1].count = ch->trans_skb->len;
 		fsm_addtimer(&ch->timer, CTC_TIMEOUT_5SEC, CH_EVENT_TIMER, ch);
 		ch->prof.send_stamp = xtime;
-#ifdef DEBUG
-	printk(KERN_DEBUG "ccw[1].cda = %08x\n", ch->ccw[1].cda);
-#endif
 		rc = do_IO(ch->irq, &ch->ccw[0], (intparm_t)ch, 0xff, 0);
 		ch->prof.doios_multi++;
 		if (rc != 0) {
@@ -1052,10 +1063,11 @@
 			fsm_deltimer(&ch->timer);
 			ccw_check_return_code(ch, rc);
 		}
-	} else
+	} else {
+		spin_unlock(&ch->collect_lock);
 		fsm_newstate(fi, CH_STATE_TXIDLE);
+	}
 	ctc_clear_busy(dev);
-	spin_unlock(&ch->collect_lock);
 }
 
 /**
@@ -1146,9 +1158,6 @@
 	if (ctc_checkalloc_buffer(ch, 1))
 		return;
 	ch->ccw[1].count = ch->max_bufsize;
-#ifdef DEBUG
-	printk(KERN_DEBUG "ccw[1].cda = %08x\n", ch->ccw[1].cda);
-#endif
 	rc = do_IO(ch->irq, &ch->ccw[0], (intparm_t)ch, 0xff, 0);
 	if (rc != 0)
 		ccw_check_return_code(ch, rc);
@@ -1203,9 +1212,6 @@
 	*((__u16 *)ch->trans_skb->data) = CTC_INITIAL_BLOCKLEN;
 	ch->ccw[1].count = 2; /* Transfer only length */
 
-#ifdef DEBUG
-	printk(KERN_DEBUG "ccw[1].cda = %08x\n", ch->ccw[1].cda);
-#endif
 	fsm_newstate(fi, (CHANNEL_DIRECTION(ch->flags) == READ)
 		     ? CH_STATE_RXINIT : CH_STATE_TXINIT);
 	rc = do_IO(ch->irq, &ch->ccw[0], (intparm_t)ch, 0xff, 0);
@@ -1254,9 +1260,6 @@
 			return;
 		ch->ccw[1].count = ch->max_bufsize;
 		fsm_newstate(fi, CH_STATE_RXIDLE);
-#ifdef DEBUG
-	printk(KERN_DEBUG "ccw[1].cda = %08x\n", ch->ccw[1].cda);
-#endif
 		rc = do_IO(ch->irq, &ch->ccw[0], (intparm_t)ch, 0xff, 0);
 		if (rc != 0) {
 			fsm_newstate(fi, CH_STATE_RXINIT);
@@ -1716,9 +1719,6 @@
 			fsm_addtimer(&ch->timer, 1000, CH_EVENT_TIMER, ch);
 			if (event == CH_EVENT_TIMER)
 				s390irq_spin_lock_irqsave(ch->irq, saveflags);
-#ifdef DEBUG
-	printk(KERN_DEBUG "ccw[4].cda = %08x\n", ch->ccw[4].cda);
-#endif
 			rc = do_IO(ch->irq, &ch->ccw[3], (intparm_t)ch, 0xff, 0);
 			if (event == CH_EVENT_TIMER)
 				s390irq_spin_unlock_irqrestore(ch->irq,
@@ -2479,6 +2479,8 @@
 	} else {
 		__u16 block_len;
 		int ccw_idx;
+		struct sk_buff *nskb;
+		unsigned long hi;
 
 		/**
 		 * Protect skb against beeing free'd by upper
@@ -2493,6 +2495,28 @@
 		       LL_HEADER_LENGTH);
 		block_len = skb->len + 2;
 		*((__u16 *)skb_push(skb, 2)) = block_len;
+
+		/**
+		 * IDAL support in CTC is broken, so we have to
+		 * care about skb's above 2G ourselves.
+		 */
+		hi = ((unsigned long)skb->tail + LL_HEADER_LENGTH) >> 31;
+		if (hi) {
+			nskb = alloc_skb(skb->len, GFP_ATOMIC | GFP_DMA);
+			if (!nskb) {
+				atomic_dec(&skb->users);
+				skb_pull(skb, LL_HEADER_LENGTH + 2);
+				return -ENOMEM;
+			} else {
+				memcpy(skb_put(nskb, skb->len),
+				       skb->data, skb->len);
+				atomic_inc(&nskb->users);
+				atomic_dec(&skb->users);
+				dev_kfree_skb_irq(skb);
+				skb = nskb;
+			}
+		}
+
 		ch->ccw[4].count = block_len;
 		if (set_normalized_cda(&ch->ccw[4], virt_to_phys(skb->data))) {
 			/**
@@ -2505,6 +2529,7 @@
 				 * Remove our header. It gets added
 				 * again on retransmit.
 				 */
+				atomic_dec(&skb->users);
 				skb_pull(skb, LL_HEADER_LENGTH + 2);
 				return -EBUSY;
 			}
@@ -2522,18 +2547,11 @@
 			ccw_idx = 3;
 		}
 		ch->retry = 0;
-#ifdef DEBUG
-		ctc_dump_skb(skb, 0);
-#endif
 		fsm_newstate(ch->fsm, CH_STATE_TX);
 		fsm_addtimer(&ch->timer, CTC_TIMEOUT_5SEC,
 			     CH_EVENT_TIMER, ch);
 		s390irq_spin_lock_irqsave(ch->irq, saveflags);
 		ch->prof.send_stamp = xtime;
-#ifdef DEBUG
-	printk(KERN_DEBUG "ccw[%d].cda = %08x\n", ccw_idx+1,
-	       ch->ccw[ccw_idx+1].cda);
-#endif
 		rc = do_IO(ch->irq, &ch->ccw[ccw_idx], (intparm_t)ch, 0xff, 0);
 		s390irq_spin_unlock_irqrestore(ch->irq, saveflags);
 		if (ccw_idx == 3)
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/s390/net/fsm.h linux/drivers/s390/net/fsm.h
--- linux.orig/drivers/s390/net/fsm.h	Mon Jan  7 23:41:09 2002
+++ linux/drivers/s390/net/fsm.h	Fri Dec 21 16:25:30 2001
@@ -1,4 +1,4 @@
-/* $Id: fsm.h,v 1.3 2001/06/18 16:49:19 felfert Exp $
+/* $Id: fsm.h,v 1.4 2001/09/24 10:38:02 mschwide Exp $
  */
 #ifndef _FSM_H_
 #define _FSM_H_
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/s390/net/iucv.c linux/drivers/s390/net/iucv.c
--- linux.orig/drivers/s390/net/iucv.c	Mon Jan  7 23:41:09 2002
+++ linux/drivers/s390/net/iucv.c	Fri Dec 21 16:25:30 2001
@@ -1,4 +1,4 @@
-/*
+/* 
  * $Id$
  *
  * IUCV network driver
@@ -478,7 +478,7 @@
 b2f0(__u32 code, void *parm)
 {
 	iucv_debug("iparml before b2f0 call:");
-	iucv_dumpit(parm, sizeof(iucv_param.param));
+	iucv_dumpit(parm, sizeof(iucv_param));
 
 	asm volatile (
 		"LRA   1,0(%1)\n\t"
@@ -490,7 +490,7 @@
 		);
 
 	iucv_debug("iparml after b2f0 call:");
-	iucv_dumpit(parm, sizeof(iucv_param.param));
+	iucv_dumpit(parm, sizeof(iucv_param));
 
 	return (unsigned long)*((__u8 *)(parm + 3));
 }
@@ -2126,7 +2126,7 @@
 					iucv_sever (int_buf->ippathid,
 						    no_listener);
 					iucv_debug("add_pathid failed, rc = %d",
-						   (int)add_pathid_result);
+						   rc);
 				} else {
 					interrupt = h->interrupt_table;
 					if (interrupt->ConnectionPending) {
@@ -2150,8 +2150,8 @@
 						h->pgm_data);
 				else
 					iucv_debug("ConnectionComplete not called");
-			}
-			
+			} else
+				iucv_sever(int_buf->ippathid, no_listener);
 			break;
 			
 		case 0x03:		/* connection severed */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/s390/net/netiucv.c linux/drivers/s390/net/netiucv.c
--- linux.orig/drivers/s390/net/netiucv.c	Mon Jan  7 23:41:09 2002
+++ linux/drivers/s390/net/netiucv.c	Fri Dec 21 16:25:30 2001
@@ -1,5 +1,5 @@
 /*
- * $Id: netiucv.c,v 1.12 2001/09/24 10:38:02 mschwide Exp $
+ * $Id: netiucv.c,v 1.16 2001/12/03 14:28:45 felfert Exp $
  *
  * IUCV network driver
  *
@@ -28,7 +28,7 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
- * RELEASE-TAG: IUCV network driver $Revision: 1.12 $
+ * RELEASE-TAG: IUCV network driver $Revision: 1.16 $
  *
  */
 
@@ -189,7 +189,7 @@
 static __inline__ void netiucv_clear_busy(net_device *dev)
 {
 	clear_bit(0, &(((netiucv_priv *)dev->priv)->tbusy));
-	netif_start_queue(dev);
+	netif_wake_queue(dev);
 }
 
 static __inline__ int netiucv_test_and_set_busy(net_device *dev)
@@ -548,7 +548,7 @@
 		header->next -= NETIUCV_HDRLEN;
 		if (skb_tailroom(pskb) < header->next) {
 			printk(KERN_WARNING
-			       "%s: Ilegal next field in iucv header: %d > %d\n",
+			       "%s: Illegal next field in iucv header: %d > %d\n",
 			       dev->name, header->next, skb_tailroom(pskb));
 			return;
 		}
@@ -618,7 +618,8 @@
 	iucv_connection *conn = ev->conn;
 	iucv_MessageComplete *eib = (iucv_MessageComplete *)ev->data;
 	netiucv_priv *privptr = NULL;
-	struct sk_buff *skb = (struct sk_buff *)eib->ipmsgtag;
+			         /* Shut up, gcc! skb is always below 2G. */
+	struct sk_buff *skb = (struct sk_buff *)(unsigned long)eib->ipmsgtag;
 	__u32 txbytes = 0;
 	__u32 txpackets = 0;
 	__u32 stat_maxcq = 0;
@@ -638,10 +639,9 @@
 				(skb->len - NETIUCV_HDRLEN - NETIUCV_HDRLEN);
 		}
 		dev_kfree_skb_any(skb);
-	} else {
-		conn->tx_buff->data = conn->tx_buff->tail = conn->tx_buff->head;
-		conn->tx_buff->len = 0;
 	}
+	conn->tx_buff->data = conn->tx_buff->tail = conn->tx_buff->head;
+	conn->tx_buff->len = 0;
 	spin_lock_irqsave(&conn->collect_lock, saveflags);
 	while ((skb = skb_dequeue(&conn->collect_queue))) {
 		header.next = conn->tx_buff->len + skb->len + NETIUCV_HDRLEN;
@@ -768,7 +768,8 @@
 		case CONN_STATE_TX:
 			printk(KERN_INFO "%s: Remote dropped connection\n",
 			       netdev->name);
-			iucv_unregister_program(conn->handle);
+			if (conn->handle)
+				iucv_unregister_program(conn->handle);
 			conn->handle = 0;
 			fsm_newstate(fi, CONN_STATE_STOPPED);
 			fsm_event(privptr->fsm, DEV_EVENT_CONDOWN, netdev);
@@ -881,7 +882,8 @@
 #endif
 	fsm_newstate(fi, CONN_STATE_STOPPED);
 	netiucv_purge_skb_queue(&conn->collect_queue);
-	iucv_unregister_program(conn->handle);
+	if (conn->handle)
+		iucv_unregister_program(conn->handle);
 	conn->handle = 0;
 	fsm_event(privptr->fsm, DEV_EVENT_CONDOWN, netdev);
 }
@@ -1127,7 +1129,10 @@
 		fsm_addtimer(&conn->timer, NETIUCV_TIMEOUT_5SEC,
 			     CONN_EVENT_TIMER, conn);
 		conn->prof.send_stamp = xtime;
-		rc = iucv_send(conn->pathid, NULL, 0, 0, (__u32)nskb, 0,
+		
+		rc = iucv_send(conn->pathid, NULL, 0, 0,
+			       /* Shut up, gcc! nskb is always below 2G. */
+			       (__u32)(((unsigned long)nskb)&0xffffffff), 0,
 			       nskb->data, nskb->len);
 		conn->prof.doios_single++;
 		conn->prof.txlen += skb->len;
@@ -1167,6 +1172,7 @@
 static int
 netiucv_open(net_device *dev) {
 	MOD_INC_USE_COUNT;
+	SET_DEVICE_START(dev, 1);
 	fsm_event(((netiucv_priv *)dev->priv)->fsm, DEV_EVENT_START, dev);
 	return 0;
 }
@@ -1573,9 +1579,7 @@
 	if (file->f_pos == 0) {
 		p += sprintf(p, "Device FSM state: %s\n",
 			     fsm_getstate_str(privptr->fsm));
-		p += sprintf(p, "RX channel FSM state: %s\n",
-			     fsm_getstate_str(privptr->conn->fsm));
-		p += sprintf(p, "TX channel FSM state: %s\n",
+		p += sprintf(p, "Connection FSM state: %s\n",
 			     fsm_getstate_str(privptr->conn->fsm));
 		p += sprintf(p, "Max. TX buffer used: %ld\n",
 			     privptr->conn->prof.maxmulti);
@@ -1970,7 +1974,6 @@
 	dev->addr_len            = 0;
 	dev->type                = ARPHRD_SLIP;
 	dev->tx_queue_len        = NETIUCV_QUEUELEN_DEFAULT;
-	SET_DEVICE_START(dev, 1);
 	dev_init_buffers(dev);
 	dev->flags	         = IFF_POINTOPOINT | IFF_NOARP;
 	return dev;
@@ -2002,7 +2005,7 @@
 static void
 netiucv_banner(void)
 {
-	char vbuf[] = "$Revision: 1.12 $";
+	char vbuf[] = "$Revision: 1.16 $";
 	char *version = vbuf;
 
 	if ((version = strchr(version, ':'))) {
@@ -2118,5 +2121,7 @@
 #ifdef MODULE
 module_init(netiucv_init);
 module_exit(netiucv_exit);
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,12))
 MODULE_LICENSE("GPL");
+#endif
 #endif
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/s390/s390io.c linux/drivers/s390/s390io.c
--- linux.orig/drivers/s390/s390io.c	Mon Jan  7 23:41:09 2002
+++ linux/drivers/s390/s390io.c	Fri Dec 21 16:25:30 2001
@@ -6,6 +6,7 @@
  *    Copyright (C) 1999, 2000 IBM Deutschland Entwicklung GmbH,
  *                             IBM Corporation
  *    Author(s): Ingo Adlung (adlung@de.ibm.com)
+ *               Cornelia Huck (cohuck@de.ibm.com) 
  *    ChangeLog: 01/07/2001 Blacklist cleanup (djbarrow@de.ibm.com,barrow_dj@yahoo.com)
  *               01/04/2001 Holger Smolinski (smolinsk@de.ibm.com)
  *                          Fixed lost interrupts and do_adapter_IO
@@ -24,6 +25,9 @@
  *               xx/xx/xxxx some bugfixes & cleanups
  *               08/02/2001 Cornelia Huck not already known devices can be blacklisted
  *                                        by piping to /proc/cio_ignore
+ *               09/xx/2001 couple more fixes
+ *               10/29/2001 Cornelia Huck Blacklisting reworked again
+ *               10/29/2001 Cornelia Huck improved utilization of debug feature
  */
 
 #include <linux/module.h>
@@ -108,7 +112,9 @@
 static void s390_process_subchannels( void);
 static void s390_device_recognition_all( void);
 static void s390_device_recognition_irq( int irq);
+#ifdef CONFIG_PROC_FS
 static void s390_redo_validation(void);
+#endif
 static int  s390_validate_subchannel( int irq, int enable);
 static int  s390_SenseID( int irq, senseid_t *sid, __u8 lpm);
 static int  s390_SetPGID( int irq, __u8 lpm, pgid_t *pgid);
@@ -117,7 +123,9 @@
 static int  enable_subchannel( unsigned int irq);
 static int  disable_subchannel( unsigned int irq);
 
+#ifdef CONFIG_PROC_FS
 static int chan_proc_init( void );
+#endif 
 
 static inline void do_adapter_IO( __u32 intparm );
 
@@ -131,108 +139,68 @@
 
 asmlinkage void do_IRQ( struct pt_regs regs );
 
+#ifdef CONFIG_PROC_FS
 #define MAX_CIO_PROCFS_ENTRIES 0x300
 /* magic number; we want to have some room to spare */
 
 int cio_procfs_device_create(int devno);
 int cio_procfs_device_remove(int devno);
 int cio_procfs_device_purge(void);
+#endif
 
 int cio_notoper_msg = 1;
+#ifdef CONFIG_PROC_FS
 int cio_proc_devinfo = 0; /* switch off the /proc/deviceinfo/ stuff by default
 			     until problems are dealt with */
+#endif
 
 unsigned long s390_irq_count[NR_CPUS]; /* trace how many irqs have occured per cpu... */
 int cio_count_irqs = 1;       /* toggle use here... */
 
 /* 
  * "Blacklisting" of certain devices:
- * Device numbers given in the commandline as blacklist=... won't be known to Linux
+ * Device numbers given in the commandline as cio_ignore=... won't be known to Linux
  * These can be single devices or ranges of devices
  *
- * Introduced by Cornelia Huck <cohuck@de.ibm.com>
- * Most of it shamelessly taken from dasd.c 
+ * 10/23/01 reworked to get rid of lists
  */
 
-typedef struct dev_blacklist_range_t {
-     struct dev_blacklist_range_t *next;  /* next range in list */
-     unsigned int from;                   /* beginning of range */
-     unsigned int to;                     /* end of range */
-     int  kmalloced;	
+static unsigned long bl_dev[2048] = {0,};
     
-} dev_blacklist_range_t;
-
-static dev_blacklist_range_t *dev_blacklist_range_head = NULL; /* Anchor for list of ranges */
-static dev_blacklist_range_t *dev_blacklist_unused_head = NULL;
-
 static spinlock_t blacklist_lock = SPIN_LOCK_UNLOCKED;
-static int nr_blacklisted_ranges = 0;
-
-/* Handling of the blacklist ranges */
-
-static inline void blacklist_range_destroy( dev_blacklist_range_t *range,int locked )
-{
-	 long flags;
-	 
-	 if(!locked)
-		 spin_lock_irqsave( &blacklist_lock, flags ); 
-	 if(!remove_from_list((list **)&dev_blacklist_range_head,(list *)range))
-		 BUG();
-	 nr_blacklisted_ranges--;
-	 if(range->kmalloced)
-		 kfree(range);
-	 else
-		 add_to_list((list **)&dev_blacklist_unused_head,(list *)range); 
-	 if(!locked)
-		 spin_unlock_irqrestore( &blacklist_lock, flags );
-}
-
-
+static int highest_ignored = 0;
+static int nr_ignored = 0;
 
 /* 
  * Function: blacklist_range_add
- * Creates a range from the specified arguments and appends it to the list of
- * blacklisted devices
+ * Blacklist the devices from-to
  */
 
-static inline dev_blacklist_range_t *blacklist_range_add( int from, int to,int locked)
+static inline void blacklist_range_add( int from, int to,int locked)
 {
  
-     dev_blacklist_range_t *range = NULL;
      unsigned long flags;
+     int i;
 
      if (to && (from>to)) {
 	     printk(KERN_WARNING "Invalid blacklist range %x to %x, skipping\n", from, to);
-	     return NULL;
+	     return;
      }
      if(!locked)
 	     spin_lock_irqsave( &blacklist_lock, flags );
-     if(dev_blacklist_unused_head)
-	     range=(dev_blacklist_range_t *)
-		     remove_listhead((list **)&dev_blacklist_unused_head);
-     else if (init_IRQ_complete) {
-	     if((range = ( dev_blacklist_range_t *) 
-		 kmalloc( sizeof( dev_blacklist_range_t ), GFP_KERNEL)))
-		     range->kmalloced=1;
-     } else {
-	     if((range = ( dev_blacklist_range_t *) 
-		 alloc_bootmem( sizeof( dev_blacklist_range_t ) )))
-		     range->kmalloced=0;
-     }
-     if (range)
-     {
-	     add_to_list((list **)&dev_blacklist_range_head,(list *)range);
-	     range->from = from;
-	     if (to == 0) {           /* only a single device is given */
-		     range->to = from;
-	     } else {
-		     range->to = to;
-	     }
-	     nr_blacklisted_ranges++;
+
+     if (!to) 
+	     to = from;
+     for (i=from;i<=to;i++) {
+	     set_bit(i,&bl_dev);
+	     nr_ignored++;
      }
+
+     if (to>=highest_ignored)
+	     highest_ignored = to;
+
      if(!locked)
 	     spin_unlock_irqrestore( &blacklist_lock, flags );
-     return range;
 }
 
 /* 
@@ -242,14 +210,19 @@
 
 static inline void blacklist_range_remove( int from, int to )
 {
-     dev_blacklist_range_t *temp;
      long flags;
+	int i;
 
      spin_lock_irqsave( &blacklist_lock, flags );
-     for ( temp = dev_blacklist_range_head; 
-	   (temp->from != from) && (temp->to != to);
-	   temp = temp->next );
-     blacklist_range_destroy( temp,1 );
+     
+	for (i=from;i<=to;i++) {
+		clear_bit(i,&bl_dev);
+		nr_ignored--;
+	}
+
+	if (to == highest_ignored) 
+		for (highest_ignored=from;(highest_ignored>0) && (!test_bit(highest_ignored,&bl_dev));highest_ignored--);
+
      spin_unlock_irqrestore( &blacklist_lock, flags );
 }
 
@@ -257,12 +230,12 @@
 
 /* 
  * Variable to hold the blacklisted devices given by the parameter line
- * blacklist=...
+ * cio_ignore=...
  */
 char *blacklist[256] = {NULL, };
 
 /*
- * Get the blacklist=... items from the parameter line
+ * Get the cio_ignore=... items from the parameter line
  */
 
 static void blacklist_split_parm_string (char *str)
@@ -282,7 +255,7 @@
 		}
 		blacklist[count] = alloc_bootmem (len * sizeof (char) );
 		if (blacklist == NULL) {
-			printk (KERN_WARNING "can't store blacklist= parameter no %d\n", count + 1);
+			printk (KERN_WARNING "can't store cio_ignore= parameter no %d\n", count + 1);
 			break;
 		}
 		memset (blacklist[count], 0, len * sizeof (char));
@@ -319,7 +292,7 @@
 
 /*
  * Function: blacklist_parse
- * Parse the parameters given to blacklist=... 
+ * Parse the parameters given to cio_ignore=... 
  * Add the blacklisted devices to the blacklist chain
  */
 
@@ -338,11 +311,9 @@
 	       temp++;
 	       to = blacklist_strtoul( temp, &temp );
 	  }
-	  if (!blacklist_range_add( from, to,0 )) {
-	       printk( KERN_WARNING "Blacklisting range from %X to %X failed!\n", from, to);
-	  }
+	  blacklist_range_add( from, to, 0 );
 #ifdef CONFIG_DEBUG_IO
-	  printk( "Blacklisted range from %X to %X\n", from, to );
+	  printk( KERN_INFO "Blacklisted range from %X to %X\n", from, to );
 #endif
 	  str++;
      }
@@ -356,7 +327,7 @@
 void __init blacklist_init( void )
 {
 #ifdef CONFIG_DEBUG_IO     
-     printk( "Reading blacklist...\n");
+     printk( KERN_DEBUG "Reading blacklist...\n");
 #endif
      if (cio_debug_initialized)
 	     debug_sprintf_event(cio_debug_msg_id, 6,
@@ -383,7 +354,7 @@
 {
         int dummy;
 #ifdef CONFIG_DEBUG_IO
-	printk( "Reading blacklist parameters...\n" );
+	printk( KERN_DEBUG "Reading blacklist parameters...\n" );
 #endif
 	if (cio_debug_initialized)
 		debug_sprintf_event(cio_debug_msg_id, 6,
@@ -406,24 +377,14 @@
 
 static inline int is_blacklisted( int devno )
 {
-     dev_blacklist_range_t *temp;
      long flags;
      int retval=0;
 
-     if (dev_blacklist_range_head == NULL) {  
-	  /* no blacklist */
-	  return 0;
-     }
-
      spin_lock_irqsave( &blacklist_lock, flags ); 
-     temp = dev_blacklist_range_head;
-     while (temp) {
-	  if ((temp->from <= devno) && (temp->to >= devno)) {
-		  retval=1;                      /* Deviceno is blacklisted */
-		  break;
-	  }
-	  temp = temp->next;
-     }
+
+     if (test_bit(devno,&bl_dev)) 
+		     retval=1;
+
      spin_unlock_irqrestore( &blacklist_lock, flags );
      return retval;
 }
@@ -435,17 +396,20 @@
 
 void blacklist_free_all_ranges(void) 
 {
-	dev_blacklist_range_t *tmp = dev_blacklist_range_head;
 	unsigned long flags;
+	int i;
 
 	spin_lock_irqsave( &blacklist_lock, flags ); 
-	while (tmp) {
-		blacklist_range_destroy(tmp,1);
-		tmp = dev_blacklist_range_head;
-	}
+
+	for (i=0;i<=highest_ignored;i++)
+		clear_bit(i,&bl_dev);
+	highest_ignored = 0;
+	nr_ignored = 0;
+
 	spin_unlock_irqrestore( &blacklist_lock, flags );
 }
 
+#ifdef CONFIG_PROC_FS
 /*
  * Function: blacklist_parse_proc_parameters
  * parse the stuff which is piped to /proc/cio_ignore
@@ -459,8 +423,6 @@
 	char *param;
 	int from = 0;
 	int to = 0;
-	int changed = 0;
-	dev_blacklist_range_t *range, *temp;
 	long flags;
 	int err = 0;
 
@@ -492,33 +454,9 @@
 				} else {
 					to = from;
 				}
-				spin_lock_irqsave( &blacklist_lock, flags ); 
-				range = dev_blacklist_range_head;
-				while (range != NULL) {
-					temp = range->next;
-					if ((from <= range->from) && (to >= range->to)) {
-						blacklist_range_destroy(range,1);
-						changed = 1;
-					} else if ((from <= range->from) && (to>=range->from) && (to < range->to)) {
-						blacklist_range_add(to+1, range->to,1);
-						blacklist_range_destroy(range,1);
-						changed = 1;
-					} else if ((from > range->from) && (from<=range->to) && (to >= range->to)) {
-						blacklist_range_add(range->from, from-1,1);
-						blacklist_range_destroy(range,1);
-						changed = 1;
-					} else if ((from > range->from) && (to < range->to)) {
-						blacklist_range_add(range->from, from-1,1);
-						blacklist_range_add(to+1, range->to,1);
-						blacklist_range_destroy(range,1);
-						changed = 1;
-					}
-					range = temp;
-				}
-				spin_unlock_irqrestore( &blacklist_lock, flags );
+				blacklist_range_remove( from, to );
 				kfree(param);
 			}
-			if (changed)
 				s390_redo_validation();
 		}
 	} else if (strstr(tmp, "add ")) {
@@ -567,12 +505,6 @@
 				}
 			}
 
-			/*
-			 * Note: We allow for overlapping ranges here, 
-			 * since the user might specify overlapping ranges
-			 * and we walk through all ranges when freeing anyway.
-			 */
-
 			if (!err) 
 				blacklist_range_add(from, to, 1);
 			
@@ -581,11 +513,11 @@
 		}
 
 	} else {
-		printk("cio_ignore: Parse error; try using 'free all|<devno-range>,<devno-range>,...'\n");
-		printk("or 'add <devno-range>,<devno-range>,...'\n");
+		printk( KERN_WARNING "cio_ignore: Parse error; try using 'free all|<devno-range>,<devno-range>,...'\n");
+		printk( KERN_WARNING "or 'add <devno-range>,<devno-range>,...'\n");
 	}
 }
-
+#endif
 /* End of blacklist handling */
 
 
@@ -642,7 +574,7 @@
 	}
 	else
 	{
-		printk( "cio_setup : invalid cio_msg parameter '%s'", parm);
+		printk( KERN_ERR "cio_setup : invalid cio_msg parameter '%s'", parm);
 
 	} /* endif */
 
@@ -658,7 +590,7 @@
 	} else if (!strcmp(parm, "no")) {
 		cio_notoper_msg = 0;
 	} else {
-		printk("cio_notoper_setup: invalid cio_notoper_msg parameter '%s'", parm);
+		printk( KERN_ERR "cio_notoper_setup: invalid cio_notoper_msg parameter '%s'", parm);
 	}
 
 	return 1;
@@ -666,6 +598,7 @@
 	
 __setup("cio_notoper_msg=", cio_notoper_setup);
 
+#ifdef CONFIG_PROC_FS
 static int __init cio_proc_devinfo_setup(char *parm)
 {
 	if (!strcmp(parm, "yes")) {
@@ -673,13 +606,14 @@
 	} else if (!strcmp(parm, "no")) {
 		cio_proc_devinfo = 0;
 	} else {
-		printk("cio_proc_devinfo_setup: invalid parameter '%s'\n",parm);
+		printk( KERN_ERR "cio_proc_devinfo_setup: invalid parameter '%s'\n",parm);
 	}
 
 	return 1;
 }
 
 __setup("cio_proc_devinfo=", cio_proc_devinfo_setup);
+#endif
 
 /*
  * register for adapter interrupts
@@ -695,6 +629,7 @@
 int  s390_register_adapter_interrupt( adapter_int_handler_t handler )
 {
 	int ret = 0;
+	char dbf_txt[15];
 	
 	if (cio_debug_initialized)
 		debug_text_event(cio_debug_trace_id, 4, "rgaint");
@@ -710,6 +645,11 @@
   	
 	spin_unlock( &adapter_lock ); 	
 
+	if (cio_debug_initialized) {
+		sprintf(dbf_txt,"ret:%d",ret);
+		debug_text_event(cio_debug_trace_id, 4, dbf_txt);
+	}
+
 	return( ret);
 }
 
@@ -717,6 +657,7 @@
 int  s390_unregister_adapter_interrupt( adapter_int_handler_t handler )
 {
 	int ret = 0;
+	char dbf_txt[15];
 	
 	if (cio_debug_initialized)
 		debug_text_event(cio_debug_trace_id, 4, "urgaint");
@@ -732,6 +673,11 @@
   	
 	spin_unlock( &adapter_lock ); 	
 
+	if (cio_debug_initialized) {
+		sprintf(dbf_txt,"ret:%d",ret);
+		debug_text_event(cio_debug_trace_id, 4, dbf_txt);
+	}
+
 	return( ret);
 }
 
@@ -777,8 +723,7 @@
 
 
 	if (cio_debug_initialized) {
-		debug_text_event(cio_debug_trace_id, 4, "reqsp");
-		sprintf(dbf_txt, "%x", irq);
+		sprintf(dbf_txt, "reqsp%x", irq);
 		debug_text_event(cio_debug_trace_id, 4, dbf_txt);
 	}
 
@@ -835,6 +780,9 @@
 		ioinfo[irq]->nopfunc         = not_oper_handler;  	
 	}
 
+	if (cio_debug_initialized)
+		debug_int_event(cio_debug_trace_id, 4, retval);
+
 	return retval;
 }
 
@@ -868,18 +816,13 @@
 	unsigned long flags;
 	int          ret;
 
-	unsigned int count = 0;
 	char dbf_txt[15];
 
 	if ( irq >= __MAX_SUBCHANNELS || ioinfo[irq] == INVALID_STORAGE_AREA )
-	{
 		return;
 
-	} /* endif */
-
 	if (cio_debug_initialized) {
-		debug_text_event(cio_debug_trace_id, 2, "free");
-		sprintf(dbf_txt, "%x", irq);
+		sprintf(dbf_txt, "free%x", irq);
 		debug_text_event(cio_debug_trace_id, 2, dbf_txt);
 	}
 
@@ -887,10 +830,7 @@
 
 #ifdef  CONFIG_KERNEL_DEBUG
 	if ( irq != cons_dev )
-	{
-		printk("Trying to free IRQ%d\n",irq);
-
-	} /* endif */
+		printk( KERN_DEBUG "Trying to free IRQ%d\n",irq);
 #endif
 	if (cio_debug_initialized)
 		debug_sprintf_event(cio_debug_msg_id, 2, "Trying to free IRQ %d\n", irq);
@@ -899,10 +839,8 @@
 	 * disable the device and reset all IRQ info if
 	 *  the IRQ is actually owned by the handler ...
 	 */
-	if ( ioinfo[irq]->ui.flags.ready )
-	{
-		if ( dev_id == ioinfo[irq]->irq_desc.dev_id  )
-		{
+	if ( ioinfo[irq]->ui.flags.ready ) {
+		if ( dev_id == ioinfo[irq]->irq_desc.dev_id ) {
 			/* start deregister */
 			ioinfo[irq]->ui.flags.unready = 1;
 
@@ -915,15 +853,9 @@
 				 0xC8C1D3E3,
 				 DOIO_WAIT_FOR_INTERRUPT );
 
-			do
-			{
 				ret = disable_subchannel( irq);
 
-				count++;
-
-				if ( ret == -EBUSY )
-				{
-					int iret;
+			if ( ret == -EBUSY ) {
 
 					/*
 					 * kill it !
@@ -932,45 +864,31 @@
 					 *  an async request, twice halt, then
 					 *  clear.
 					 */
-					if ( count < 2 )
-					{              	
-						iret = halt_IO( irq,
+				ret = halt_IO( irq,
 						                0xC8C1D3E3,
 						                DOIO_WAIT_FOR_INTERRUPT );
    	
-						if ( iret == -EBUSY )
-						{
+				if ( ret == -EBUSY ) {
 							halt_IO( irq, 0xC8C1D3E3, 0);
 							s390irq_spin_unlock_irqrestore( irq, flags);
 							udelay( 200000 ); /* 200 ms */
 							s390irq_spin_lock_irqsave( irq, flags);
 
 						} /* endif */
-					}
-					else
-					{
-						iret = clear_IO( irq,
-						                 0x40C3D3D9,
-						                 DOIO_WAIT_FOR_INTERRUPT );
+				
+				ret = disable_subchannel(irq);
+
+				if (ret == -EBUSY) {
    	
-						if ( iret == -EBUSY )
-						{
-							clear_IO( irq, 0xC8C1D3E3, 0);
+					clear_IO( irq, 0x40C3D3D9,0 );
 							s390irq_spin_unlock_irqrestore( irq, flags);
 							udelay( 1000000 ); /* 1000 ms */
 							s390irq_spin_lock_irqsave( irq, flags);
 
-						} /* endif */
-
-					} /* endif */
-
-					if ( count == 2 )
-					{
 						/* give it a very last try ... */
 						disable_subchannel( irq);
 
-						if ( ioinfo[irq]->ui.flags.busy )
-					        {
+					if ( ioinfo[irq]->ui.flags.busy ) {
 							printk( KERN_CRIT"free_irq(%04X) "
 							       "- device %04X busy, retry "
 							       "count exceeded\n",
@@ -983,39 +901,30 @@
 
 						} /* endif */
 						
-						break; /* sigh, let's give up ... */
-
 					} /* endif */
 
 				} /* endif */
 		
-			} while ( ret == -EBUSY );
-
 			ioinfo[irq]->ui.flags.ready   = 0;
 			ioinfo[irq]->ui.flags.unready = 0; /* deregister ended */
 
 			ioinfo[irq]->nopfunc = NULL;
 
 			s390irq_spin_unlock_irqrestore( irq, flags);
-		}
-		else
-		{
+		} else {
 			s390irq_spin_unlock_irqrestore( irq, flags);
 
-			printk( "free_irq(%04X) : error, "
+			printk( KERN_ERR "free_irq(%04X) : error, "
 			        "dev_id does not match !\n", irq);
 			if (cio_debug_initialized)
 				debug_sprintf_event(cio_debug_msg_id, 0, 
 						    "free_irq(%04X) : error, dev_id does not match !\n", irq);
 
 		} /* endif */
-
-	}
-	else
-	{
+	} else {
 		s390irq_spin_unlock_irqrestore( irq, flags);
 
-		printk( "free_irq(%04X) : error, "
+		printk( KERN_ERR "free_irq(%04X) : error, "
 		        "no action block ... !\n", irq);
 		if (cio_debug_initialized)
 			debug_sprintf_event(cio_debug_msg_id, 0,
@@ -1040,8 +949,7 @@
 		return -ENODEV;
 
 	if (cio_debug_initialized) {
-		debug_text_event(cio_debug_trace_id, 4, "disirq");
-		sprintf(dbf_txt, "%x", irq);
+		sprintf(dbf_txt, "disirq%x", irq);
 		debug_text_event(cio_debug_trace_id, 4, dbf_txt);
 	}
 
@@ -1051,6 +959,9 @@
 
 	synchronize_irq();
 
+	if (cio_debug_initialized) 
+		debug_int_event(cio_debug_trace_id, 4, ret);
+
 	return( ret);
 }
 
@@ -1066,8 +977,7 @@
 		return -ENODEV;
 
 	if (cio_debug_initialized) {
-		debug_text_event(cio_debug_trace_id, 4, "enirq");
-		sprintf(dbf_txt, "%x", irq);
+		sprintf(dbf_txt, "enirq%x", irq);
 		debug_text_event(cio_debug_trace_id, 4, dbf_txt);
 	}
 
@@ -1075,6 +985,9 @@
 	ret = enable_subchannel(irq);
 	s390irq_spin_unlock_irqrestore(irq, flags);
 
+	if (cio_debug_initialized) 
+		debug_int_event(cio_debug_trace_id, 4, ret);
+
 	return(ret);
 }
 
@@ -1091,8 +1004,7 @@
 	SANITY_CHECK(irq);
 
 	if (cio_debug_initialized) {
-		debug_text_event(cio_debug_trace_id, 2, "ensch");
-		sprintf(dbf_txt, "%x", irq);
+		sprintf(dbf_txt, "ensch%x", irq);
 		debug_text_event(cio_debug_trace_id, 2, dbf_txt);
 	}
 
@@ -1168,6 +1080,11 @@
 
 	}  /* endif */
 
+	if (cio_debug_initialized) {
+		sprintf(dbf_txt,"ret:%d",ret);
+		debug_text_event(cio_debug_trace_id, 2, dbf_txt);
+	}
+
 	return( ret );
 }
 
@@ -1195,8 +1112,7 @@
 	else
 	{
 		if (cio_debug_initialized) {
-			debug_text_event(cio_debug_trace_id, 2, "dissch");
-			sprintf(dbf_txt, "%x", irq);
+			sprintf(dbf_txt, "dissch%x", irq);
 			debug_text_event(cio_debug_trace_id, 2, dbf_txt);
 		}
 		
@@ -1274,6 +1190,11 @@
 
 	} /* endif */
 
+	if (cio_debug_initialized) {
+		sprintf(dbf_txt, "ret:%d",ret);
+		debug_text_event(cio_debug_trace_id, 2, dbf_txt);
+	}
+
 	return( ret);
 }
 
@@ -1364,8 +1285,7 @@
 	} /* endif */
 
 	if (cio_debug_initialized) {
-		debug_text_event(cio_debug_trace_id, 4, "stIO");
-		sprintf(dbf_txt, "%x", irq);
+		sprintf(dbf_txt, "stIO%x", irq);
 		debug_text_event(cio_debug_trace_id, 4, dbf_txt);
 	}
 
@@ -1427,6 +1347,11 @@
 	 */
 	ccode = ssch( irq, &(ioinfo[irq]->orb) );
 
+	if (cio_debug_initialized) {
+		sprintf(dbf_txt, "ccode:%d", ccode);
+		debug_text_event(cio_debug_trace_id, 4, dbf_txt);
+	}
+
 	switch ( ccode ) {
 	case 0:
 
@@ -1849,8 +1774,7 @@
 	} /* endif */
 
 	if (cio_debug_initialized) {
-		debug_text_event(cio_debug_trace_id, 4, "doIO");
-		sprintf(dbf_txt, "%x", irq);
+		sprintf(dbf_txt, "doIO%x", irq);
 		debug_text_event(cio_debug_trace_id, 4, dbf_txt);
 	}
 
@@ -1908,8 +1832,7 @@
 	SANITY_CHECK(irq);
 
 	if (cio_debug_initialized) {
-		debug_text_event(cio_debug_trace_id, 4, "resIO");
-		sprintf(dbf_txt, "%x", irq);
+		sprintf(dbf_txt, "resIO%x", irq);
 		debug_text_event(cio_debug_trace_id, 4, dbf_txt);
 	}
 
@@ -1922,6 +1845,11 @@
 
 		ccode = rsch( irq);
 
+		if (cio_debug_initialized) {
+			sprintf(dbf_txt, "ccode:%d",ccode);
+			debug_text_event(cio_debug_trace_id, 4, dbf_txt);
+		}
+
 		switch (ccode) {
 		case 0 :
 			break;
@@ -1988,21 +1916,10 @@
 	{
 		ret = 0;
 	}
-#if 0
-	/*
-	 * We don't allow for halt_io with a sync do_IO() requests pending.
-	 */
-	else if (    ioinfo[irq]->ui.flags.syncio
-	          && (flag & DOIO_WAIT_FOR_INTERRUPT))
-	{
-		ret = -EBUSY;
-	}
-#endif
 	else
 	{
 		if (cio_debug_initialized) {
-			debug_text_event(cio_debug_trace_id, 2, "haltIO");
-			sprintf(dbf_txt, "%x", irq);
+			sprintf(dbf_txt, "haltIO%x", irq);
 			debug_text_event(cio_debug_trace_id, 2, dbf_txt);
 		}
 		/*
@@ -2028,6 +1945,11 @@
 		 */
 		ccode = hsch( irq );
 
+		if (cio_debug_initialized) {
+			sprintf(dbf_txt, "ccode:%d",ccode);
+			debug_text_event(cio_debug_trace_id, 2, dbf_txt);
+		}		
+
 		switch ( ccode ) {
 		case 0:
 
@@ -2253,22 +2175,10 @@
 	{
 		ret = 0;
 	}
-#if 0
-	/*
-	 * We don't allow for clear_io with a sync do_IO() requests pending.
-	 *  Concurrent I/O is possible in SMP environments only, but the
-	 *  sync. I/O request can be gated to one CPU at a time only.
-	 */
-	else if ( ioinfo[irq]->ui.flags.syncio )
-	{
-		ret = -EBUSY;
-	}
-#endif
 	else
 	{
 		if (cio_debug_initialized) {
-			debug_text_event(cio_debug_trace_id, 2, "clearIO");
-			sprintf(dbf_txt, "%x", irq);
+			sprintf(dbf_txt, "clearIO%x", irq);
 			debug_text_event(cio_debug_trace_id, 2, dbf_txt);
 		}
 		/*
@@ -2294,6 +2204,11 @@
 		 */
 		ccode = csch( irq );
 
+		if (cio_debug_initialized) {
+			sprintf(dbf_txt, "ccode:%d",ccode);
+			debug_text_event(cio_debug_trace_id, 2, dbf_txt);
+		}
+
 		switch ( ccode ) {
 		case 0:
 
@@ -2548,10 +2463,8 @@
 	}
 	
 	
-
 	if (cio_debug_initialized) {
-		debug_text_event(cio_debug_trace_id, 3, "procIRQ");
-		sprintf(dbf_txt, "%x", irq);
+		sprintf(dbf_txt, "procIRQ%x", irq);
 		debug_text_event(cio_debug_trace_id, 3, dbf_txt);
 	}
 
@@ -2612,6 +2525,11 @@
 	 */
 	ccode = tsch( irq, &(dp->ii.irb) );
 
+	if (cio_debug_initialized) {
+		sprintf(dbf_txt, "ccode:%d", ccode);
+		debug_text_event(cio_debug_trace_id, 3, dbf_txt);
+	}
+
 	//
 	// We must only accumulate the status if the device is busy already
 	//
@@ -2675,7 +2593,7 @@
 
 #ifdef CONFIG_DEBUG_IO
 		if ( irq != cons_dev )
-			printk( "s390_process_IRQ( %04X ) : "
+			printk( KERN_DEBUG "s390_process_IRQ( %04X ) : "
 			        "residual count from irb after tsch() %d\n",
 			        irq, dp->rescnt );
 #endif
@@ -2698,7 +2616,7 @@
 			 | SCHN_STAT_INTF_CTRL_CHK )))
 	{
 		if (irq != cons_dev)
-			printk( "Channel-Check or Interface-Control-Check "
+			printk( KERN_WARNING "Channel-Check or Interface-Control-Check "
 				"received\n"
 				" ... device %04X on subchannel %04X, dev_stat "
 				": %02X sch_stat : %02X\n",
@@ -2748,7 +2666,7 @@
 
 #ifdef CONFIG_DEBUG_IO
 		if ( irq != cons_dev )
-			printk( "s390_process_IRQ( %04X ) : "
+			printk( KERN_DEBUG "s390_process_IRQ( %04X ) : "
 			        "concurrent sense bytes avail %d\n",
 			        irq, dp->scnt );
 #endif
@@ -2800,10 +2718,9 @@
 			
 		ioinfo[irq]->stctl |= stctl;
 
-		ending_status =    ( stctl & SCSW_STCTL_SEC_STATUS                          )
-			|| ( stctl == (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND)         )
-			|| ( (fctl == SCSW_FCTL_HALT_FUNC)  && (stctl == SCSW_STCTL_STATUS_PEND) )
-			|| ( (fctl == SCSW_FCTL_CLEAR_FUNC) && (stctl == SCSW_STCTL_STATUS_PEND) );
+		ending_status =    ( stctl & SCSW_STCTL_SEC_STATUS )
+			        || ( stctl == (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND) )
+			        || ( stctl == SCSW_STCTL_STATUS_PEND);
 
 		/*
 		 * Check for unsolicited interrupts - for debug purposes only
@@ -2821,7 +2738,7 @@
 		{
 #ifdef CONFIG_DEBUG_IO
 			if (irq != cons_dev)
-				printk( "Unsolicited interrupt received for device %04X on subchannel %04X\n"
+				printk( KERN_INFO "Unsolicited interrupt received for device %04X on subchannel %04X\n"
 					" ... device status : %02X subchannel status : %02X\n",
 					dp->devno,
 					irq,
@@ -2998,7 +2915,7 @@
 
 #ifdef CONFIG_DEBUG_IO
 				if ( irq != cons_dev )
-					printk( "s390_process_IRQ( %04X ) : "
+					printk( KERN_DEBUG "s390_process_IRQ( %04X ) : "
 						"BASIC SENSE bytes avail %d\n",
 						irq, sense_count );
 #endif
@@ -3274,8 +3191,7 @@
 	else
 	{
 		if (cio_debug_initialized) {
-			debug_text_event(cio_debug_trace_id, 4, "scons");
-			sprintf(dbf_txt, "%x", irq);
+			sprintf(dbf_txt, "scons%x", irq);
 			debug_text_event(cio_debug_trace_id, 4, dbf_txt);
 		}
 		
@@ -3336,8 +3252,7 @@
 	else
 	{
 		if (cio_debug_initialized) {
-			debug_text_event(cio_debug_trace_id, 4, "rcons");
-			sprintf(dbf_txt, "%x", irq);
+			sprintf(dbf_txt, "rcons%x", irq);
 			debug_text_event(cio_debug_trace_id, 4, dbf_txt);
 		}
 
@@ -3393,8 +3308,7 @@
 	{
 
 		if (cio_debug_initialized) {
-			debug_text_event(cio_debug_trace_id, 4, "wcons");
-			sprintf(dbf_txt, "%x", irq);
+			sprintf(dbf_txt, "wcons%x", irq);
 			debug_text_event(cio_debug_trace_id, 4, dbf_txt);
 		}
 
@@ -3456,8 +3370,7 @@
 	char dbf_txt[15];
 
 	if (cio_debug_initialized) {
-		debug_text_event(cio_debug_trace_id, 4, "enisc");
-		sprintf(dbf_txt, "%x", irq);
+		sprintf(dbf_txt, "enisc%x", irq);
 		debug_text_event(cio_debug_trace_id, 4, dbf_txt);
 	}
 
@@ -3573,8 +3486,7 @@
 	char dbf_txt[15];
 
 	if (cio_debug_initialized) {
-		debug_text_event(cio_debug_trace_id, 4, "disisc");
-		sprintf(dbf_txt, "%x", irq);
+		sprintf(dbf_txt, "disisc%x", irq);
 		debug_text_event(cio_debug_trace_id, 4, dbf_txt);
 	}
 
@@ -3973,7 +3885,7 @@
 
 	if ( error )
 	{
-		printk( "DIAG X'210' for "
+		printk( KERN_ERR "DIAG X'210' for "
 		        "device %04X returned "
 		        "(cc = %d): vdev class : %02X, "
 			"vdev type : %04X \n ...  rdev class : %02X, rdev type : %04X, rdev model: %02X\n",
@@ -4054,8 +3966,7 @@
 	} /* endif */
 
 	if (cio_debug_initialized) {
-		debug_text_event(cio_debug_trace_id, 4, "rddevch");
-		sprintf(dbf_txt, "%x", irq);
+		sprintf(dbf_txt, "rddevch%x", irq);
 		debug_text_event(cio_debug_trace_id, 4, dbf_txt);
 	}
 
@@ -4194,8 +4105,7 @@
 	} /* endif */
 
 	if (cio_debug_initialized) {
-		debug_text_event(cio_debug_trace_id, 4, "rdconf");
-		sprintf(dbf_txt, "%x", irq);
+		sprintf(dbf_txt, "rdconf%x", irq);
 		debug_text_event(cio_debug_trace_id, 4, dbf_txt);
 	}
 
@@ -4637,8 +4547,7 @@
 	char dbf_txt[15];
 
 	if (cio_debug_initialized) {
-		debug_text_event(cio_debug_trace_id, 4, "devrec");
-		sprintf(dbf_txt, "%x", irq);
+		sprintf(dbf_txt, "devrec%x", irq);
 		debug_text_event(cio_debug_trace_id, 4, dbf_txt);
 	}
 
@@ -4779,10 +4688,12 @@
 						
 					} 
 				}
+#ifdef CONFIG_PROC_FS
 				if (cio_proc_devinfo) 
 					if (irq < MAX_CIO_PROCFS_ENTRIES) {
 						cio_procfs_device_create(ioinfo[irq]->devno);
 				}
+#endif
 			}
 		}
 		irq++;
@@ -4811,7 +4722,7 @@
 
 	highest_subchannel = (--irq);
 
-	printk( "Highest subchannel number detected (hex) : %04X\n",
+	printk( KERN_INFO "Highest subchannel number detected (hex) : %04X\n",
 	        highest_subchannel);
 	if (cio_debug_initialized)
 		debug_sprintf_event(cio_debug_msg_id, 0, 
@@ -4837,8 +4748,7 @@
 	char dbf_txt[15];
 
 	if (cio_debug_initialized) {
-		debug_text_event(cio_debug_trace_id, 4, "valsch");
-		sprintf(dbf_txt, "%x", irq);
+		sprintf(dbf_txt, "valsch%x", irq);
 		debug_text_event(cio_debug_trace_id, 4, dbf_txt);
 	}
 
@@ -4876,7 +4786,7 @@
 		 */
 		if ( p_schib->pmcw.st )
 		{
-			printk( "Subchannel %04X reports "
+			printk( KERN_INFO "Subchannel %04X reports "
 			        "non-I/O subchannel type %04X\n",
 			        irq,
 			        p_schib->pmcw.st);
@@ -4898,7 +4808,7 @@
 			   * there is no device and return ENODEV.
 			   */
 #ifdef CONFIG_DEBUG_IO
-			  printk( "Blacklisted device detected at devno %04X\n", p_schib->pmcw.dev );
+			  printk( KERN_DEBUG "Blacklisted device detected at devno %04X\n", p_schib->pmcw.dev );
 #endif
 			  if (cio_debug_initialized)
 				  debug_sprintf_event(cio_debug_msg_id, 0,
@@ -5109,7 +5019,7 @@
 
 							if ( ccode2 != 0 )
 							{
-								printk( " ... msch() (2) failed with CC = %X\n",
+								printk( KERN_ERR " ... msch() (2) failed with CC = %X\n",
 								        ccode2 );
 								if (cio_debug_initialized)
 									debug_sprintf_event(cio_debug_msg_id, 0,
@@ -5128,7 +5038,7 @@
 						}
 						else
 						{
-							printk( " ... msch() (1) failed with CC = %X\n",
+							printk( KERN_ERR " ... msch() (1) failed with CC = %X\n",
 							        ccode2);
 							if (cio_debug_initialized)
 								debug_sprintf_event(cio_debug_msg_id, 0,
@@ -5148,7 +5058,7 @@
 
 				if ( (ccode2 != 0) && (ccode2 != 3) && (!retry) )
 				{
-					printk( " ... msch() retry count for "
+					printk( KERN_ERR " ... msch() retry count for "
 					        "subchannel %04X exceeded, CC = %d\n",
 					        irq,
 					        ccode2);
@@ -5226,8 +5136,7 @@
 	} /* endif */
 
 	if (cio_debug_initialized) {
-		debug_text_event(cio_debug_trace_id, 4, "snsID");
-		sprintf(dbf_txt, "%x", irq);
+		sprintf(dbf_txt, "snsID%x", irq);
 		debug_text_event(cio_debug_trace_id, 4, dbf_txt);
 	}
 
@@ -5344,7 +5253,7 @@
 						if ( pdevstat->flag & DEVSTAT_STATUS_PENDING )
 						{
 #ifdef CONFIG_DEBUG_IO
-							printk( "SenseID : device %04X on "
+							printk( KERN_DEBUG "SenseID : device %04X on "
 							        "Subchannel %04X "
 							        "reports pending status, "
 							        "retry : %d\n",
@@ -5363,7 +5272,7 @@
 										    retry);		    
 						} /* endif */
 
-						if ( pdevstat->flag & DEVSTAT_FLAG_SENSE_AVAIL )
+						else if ( pdevstat->flag & DEVSTAT_FLAG_SENSE_AVAIL )
 						{
 							/*
 							 * if the device doesn't support the SenseID
@@ -5373,7 +5282,7 @@
 							    & (SNS0_CMD_REJECT | SNS0_INTERVENTION_REQ) )
 							{
 #ifdef CONFIG_DEBUG_IO
-								printk( "SenseID : device %04X on "
+								printk( KERN_ERR "SenseID : device %04X on "
 								        "Subchannel %04X "
 								        "reports cmd reject or "
 								        "intervention required\n",
@@ -5394,7 +5303,7 @@
 							else
 							{
 #ifdef CONFIG_DEBUG_IO							
-								printk( "SenseID : UC on "
+								printk( KERN_WARNING "SenseID : UC on "
 								        "dev %04X, "
 								        "retry %d, "
 								        "lpum %02X, "
@@ -5449,7 +5358,7 @@
 							  || ( irq_ret        == -ENODEV         ) )
 						{
 #ifdef CONFIG_DEBUG_IO
-							printk( "SenseID : path %02X for "
+							printk( KERN_ERR "SenseID : path %02X for "
 							        "device %04X on "
 							        "subchannel %04X "
 							        "is 'not operational'\n",
@@ -5479,7 +5388,7 @@
 								DEVSTAT_STATUS_PENDING       ) )
 						{
 #ifdef CONFIG_DEBUG_IO
-							printk( "SenseID : start_IO() for "
+							printk( KERN_INFO "SenseID : start_IO() for "
 							        "device %04X on "
 							        "subchannel %04X "
 							        "returns %d, retry %d, "
@@ -5628,7 +5537,7 @@
 			 *  consider the device "not operational".
 			 */
 #ifdef CONFIG_DEBUG_IO
-			printk( "SenseID : unknown device %04X on subchannel %04X\n",
+			printk( KERN_WARNING "SenseID : unknown device %04X on subchannel %04X\n",
 			        ioinfo[irq]->schib.pmcw.dev,
 			        irq);
 #endif
@@ -5732,8 +5641,7 @@
 	char dbf_txt[15];
 
 	if (cio_debug_initialized) {
-		debug_text_event(cio_debug_trace_id, 4, "dpver");
-		sprintf(dbf_txt, "%x", irq);
+		sprintf(dbf_txt, "dpver%x", irq);
 		debug_text_event(cio_debug_trace_id, 4, dbf_txt);
 	}
 
@@ -5842,7 +5750,7 @@
 						ioinfo[irq]->ui.flags.pgid_supp = 0;
 
 #ifdef CONFIG_DEBUG_IO
-						printk( "PathVerification(%04X) "
+						printk( KERN_WARNING "PathVerification(%04X) "
 						        "- Device %04X doesn't "
 						        " support path grouping\n",
 						        irq,
@@ -5861,7 +5769,7 @@
 				else if ( ret == -EIO ) 
 				{
 #ifdef CONFIG_DEBUG_IO
-					printk("PathVerification(%04X) - I/O error "
+					printk( KERN_ERR "PathVerification(%04X) - I/O error "
 					       "on device %04X\n", irq,
 					       ioinfo[irq]->schib.pmcw.dev);
 #endif
@@ -5876,7 +5784,7 @@
 		    
 				} else {
 #ifdef CONFIG_DEBUG_IO
-					printk( "PathVerification(%04X) "
+					printk( KERN_ERR "PathVerification(%04X) "
 						"- Unexpected error on device %04X\n",
 						irq,
 						ioinfo[irq]->schib.pmcw.dev);
@@ -5914,6 +5822,7 @@
 	devstat_t  devstat;     /* required by request_irq() */
 	devstat_t *pdevstat = &devstat;
         unsigned long flags;
+	char dbf_txt[15];
 
 
 	int        irq_ret = 0; /* return code */
@@ -5929,6 +5838,11 @@
 
 	} /* endif */
 
+	if (cio_debug_initialized) {
+		sprintf(dbf_txt,"SPID%x",irq);
+		debug_text_event(cio_debug_trace_id, 4, dbf_txt);
+	}
+
 	if ( !ioinfo[irq]->ui.flags.ready )
 	{
 		/*
@@ -5966,7 +5880,7 @@
 
 		} /* endif */
 
-		spid_ccw[0].cmd_code = 0x5B;	/* suspend multipath reconnect */
+		spid_ccw[0].cmd_code = CCW_CMD_SUSPEND_RECONN;	
 		spid_ccw[0].cda      = 0;
 		spid_ccw[0].count    = 0;
 		spid_ccw[0].flags    = CCW_FLAG_SLI | CCW_FLAG_CC;
@@ -5999,7 +5913,7 @@
 				if ( pdevstat->flag & DEVSTAT_STATUS_PENDING )
 				{
 #ifdef CONFIG_DEBUG_IO
-					printk( "SPID - Device %04X "
+					printk( KERN_DEBUG "SPID - Device %04X "
 					        "on Subchannel %04X "
 					        "reports pending status, "
 					        "retry : %d\n",
@@ -6037,6 +5951,18 @@
 					{
 						if ( mpath )
 						{
+							/*
+							 * We now try single path mode.
+							 * Note we must not issue the suspend
+							 * multipath reconnect, or we will get
+							 * a command reject by tapes.
+							 */
+							
+							spid_ccw[0].cmd_code = CCW_CMD_SET_PGID;
+							spid_ccw[0].cda      = (__u32)virt_to_phys( pgid );
+							spid_ccw[0].count    = sizeof( pgid_t);
+							spid_ccw[0].flags    = CCW_FLAG_SLI;
+
 							pgid->inf.fc =   SPID_FUNC_SINGLE_PATH
 							               | SPID_FUNC_ESTABLISH;
 							mpath        = 0;
@@ -6053,7 +5979,7 @@
 					else
 					{
 #ifdef CONFIG_DEBUG_IO
-						printk( "SPID - device %04X,"
+						printk( KERN_WARNING "SPID - device %04X,"
 						        " unit check,"
 						        " retry %d, cnt %02d,"
 						        " sns :"
@@ -6101,7 +6027,7 @@
 					/* don't issue warnings during startup unless requested*/
 					if (init_IRQ_complete || cio_notoper_msg) {   
 						
-						printk( "SPID - Device %04X "
+						printk( KERN_WARNING "SPID - Device %04X "
 							"on Subchannel %04X "
 							"became 'not operational'\n",
 							ioinfo[irq]->schib.pmcw.dev,
@@ -6171,6 +6097,7 @@
 	ccw1_t    *snid_ccw;    /* ccw area for SNID command */
 	devstat_t  devstat;     /* required by request_irq() */
 	devstat_t *pdevstat = &devstat;
+	char dbf_txt[15];
 
 	int        irq_ret = 0; /* return code */
 	int        retry   = 5; /* retry count */
@@ -6185,6 +6112,11 @@
 
 	} /* endif */
 
+	if (cio_debug_initialized) {
+		sprintf(dbf_txt,"SNID%x",irq);
+		debug_text_event(cio_debug_trace_id, 4, dbf_txt);
+	}
+
 	if ( !ioinfo[irq]->ui.flags.ready )
 	{
 		/*
@@ -6261,7 +6193,7 @@
 					else
 					{
 #ifdef CONFIG_DEBUG_IO
-						printk( "SNID - device %04X,"
+						printk( KERN_WARNING "SNID - device %04X,"
 						        " unit check,"
 						        " flag %04X, "
 						        " retry %d, cnt %02d,"
@@ -6310,7 +6242,7 @@
 				{
 					/* don't issue warnings during startup unless requested*/
 					if (init_IRQ_complete || cio_notoper_msg) {  
-						printk( "SNID - Device %04X "
+						printk( KERN_WARNING "SNID - Device %04X "
 							"on Subchannel %04X "
 							"became 'not operational'\n",
 							ioinfo[irq]->schib.pmcw.dev,
@@ -6341,7 +6273,7 @@
 				if ( pdevstat->flag & DEVSTAT_STATUS_PENDING )
 				{
 #ifdef CONFIG_DEBUG_IO
-					printk( "SNID - Device %04X "
+					printk( KERN_INFO "SNID - Device %04X "
 					        "on Subchannel %04X "
 					        "reports pending status, "
 					        "retry : %d\n",
@@ -6361,7 +6293,7 @@
 				} /* endif */
 
 
-				printk( "SNID - device %04X,"
+				printk( KERN_WARNING "SNID - device %04X,"
 				        " start_io() reports rc : %d, retrying ...\n",
 				        ioinfo[irq]->schib.pmcw.dev,
 				        irq_ret);
@@ -6425,7 +6357,7 @@
 	int lock     = 0;
 
 #ifdef CONFIG_DEBUG_CRW
-	printk( "do_crw_pending : starting ...\n");
+	printk( KERN_DEBUG "do_crw_pending : starting ...\n");
 #endif
 	if (cio_debug_initialized) 
 		debug_sprintf_event(cio_debug_crw_id, 2, 
@@ -6440,7 +6372,7 @@
 			irq = pcrwe->crw.rsid;
 
 #ifdef CONFIG_DEBUG_CRW
-			printk( KERN_INFO"do_crw_pending : source is "
+			printk( KERN_NOTICE"do_crw_pending : source is "
 			        "subchannel %04X\n", irq);
 #endif
 			if (cio_debug_initialized)
@@ -6465,7 +6397,7 @@
 			} /* endif */
 
 #ifdef CONFIG_DEBUG_CRW
-			printk( "do_crw_pending : subchannel validation - start ...\n");
+			printk( KERN_DEBUG "do_crw_pending : subchannel validation - start ...\n");
 #endif
 			if (cio_debug_initialized)
 				debug_sprintf_event(cio_debug_crw_id, 4,
@@ -6476,7 +6408,7 @@
 				highest_subchannel = irq;
 
 #ifdef CONFIG_DEBUG_CRW
-			printk( "do_crw_pending : subchannel validation - done\n");
+			printk( KERN_DEBUG "do_crw_pending : subchannel validation - done\n");
 #endif
 			if (cio_debug_initialized)
 				debug_sprintf_event(cio_debug_crw_id, 4,
@@ -6496,7 +6428,7 @@
 			if ( ioinfo[irq] != INVALID_STORAGE_AREA )
 			{
 #ifdef CONFIG_DEBUG_CRW
-				printk( "do_crw_pending : ioinfo at "
+				printk( KERN_DEBUG "do_crw_pending : ioinfo at "
 #ifdef CONFIG_ARCH_S390X
 					"%08lX\n",
 					(unsigned long)ioinfo[irq]);
@@ -6523,10 +6455,11 @@
 				if ( ioinfo[irq]->ui.flags.oper == 0 )
 				{
 					 not_oper_handler_func_t nopfunc=ioinfo[irq]->nopfunc;
-					 
+#ifdef CONFIG_PROC_FS					 
 					 /* remove procfs entry */
 					 if (cio_proc_devinfo)
 						 cio_procfs_device_remove(dev_no);
+#endif
 					/*
 					 * If the device has gone
 					 *  call not oper handler        	
@@ -6543,7 +6476,7 @@
 				else
 				{
 #ifdef CONFIG_DEBUG_CRW
-					printk( "do_crw_pending : device "
+					printk( KERN_DEBUG "do_crw_pending : device "
 					        "recognition - start ...\n");
 #endif
 					if (cio_debug_initialized)
@@ -6552,7 +6485,7 @@
 					s390_device_recognition_irq( irq );
 
 #ifdef CONFIG_DEBUG_CRW
-					printk( "do_crw_pending : device "
+					printk( KERN_DEBUG "do_crw_pending : device "
 					        "recognition - done\n");
 #endif
 					if (cio_debug_initialized)
@@ -6573,12 +6506,13 @@
 								pdevreg->oper_func( irq, pdevreg );
 
 						} /* endif */
-
+#ifdef CONFIG_PROC_FS
 						/* add new procfs entry */
 						if (cio_proc_devinfo) 
 							if (highest_subchannel < MAX_CIO_PROCFS_ENTRIES) {
 								cio_procfs_device_create(ioinfo[irq]->devno);
 							}
+#endif
 					}
 					/*
 					 * ... it is and was operational, but
@@ -6586,23 +6520,26 @@
 					 */
 					else if ((ioinfo[irq]->devno != dev_no) && ( ioinfo[irq]->nopfunc != NULL ))   					
 					{
+#ifdef CONFIG_PROC_FS
 						int devno_old = ioinfo[irq]->devno;
+#endif
 						ioinfo[irq]->nopfunc( irq,
 						                      DEVSTAT_REVALIDATE );				
-
+#ifdef CONFIG_PROC_FS
 						/* remove old entry, add new */
 						if (cio_proc_devinfo) {
 							cio_procfs_device_remove(devno_old);
 							cio_procfs_device_create(ioinfo[irq]->devno);
 						}
+#endif
 					} /* endif */
 
 				} /* endif */
-
+#ifdef CONFIG_PROC_FS
 				/* get rid of dead procfs entries */
 				if (cio_proc_devinfo) 
 					cio_procfs_device_purge();
-
+#endif
 			} /* endif */
 
 			break;
@@ -6610,7 +6547,7 @@
 		case CRW_RSC_MONITOR :
 
 #ifdef CONFIG_DEBUG_CRW
-			printk( "do_crw_pending : source is "
+			printk( KERN_NOTICE "do_crw_pending : source is "
 			        "monitoring facility\n");
 #endif
 			if (cio_debug_initialized)
@@ -6623,7 +6560,7 @@
 			chpid = pcrwe->crw.rsid;
 
 #ifdef CONFIG_DEBUG_CRW
-			printk( "do_crw_pending : source is "
+			printk( KERN_NOTICE "do_crw_pending : source is "
 			        "channel path %02X\n", chpid);
 #endif
 			if (cio_debug_initialized)
@@ -6634,7 +6571,7 @@
 		case CRW_RSC_CONFIG : 	
 
 #ifdef CONFIG_DEBUG_CRW
-			printk( "do_crw_pending : source is "
+			printk( KERN_NOTICE "do_crw_pending : source is "
 			        "configuration-alert facility\n");
 #endif
 			if (cio_debug_initialized)
@@ -6645,7 +6582,7 @@
 		case CRW_RSC_CSS :
 
 #ifdef CONFIG_DEBUG_CRW
-			printk( "do_crw_pending : source is "
+			printk( KERN_NOTICE "do_crw_pending : source is "
 			        "channel subsystem\n");
 #endif
 			if (cio_debug_initialized)
@@ -6656,7 +6593,7 @@
 		default :
 
 #ifdef CONFIG_DEBUG_CRW
-			printk( "do_crw_pending : unknown source\n");
+			printk( KERN_NOTICE "do_crw_pending : unknown source\n");
 #endif
 			if (cio_debug_initialized)
 				debug_sprintf_event(cio_debug_crw_id, 2,
@@ -6670,7 +6607,7 @@
 	} /* endwhile */
 
 #ifdef CONFIG_DEBUG_CRW
-	printk( "do_crw_pending : done\n");
+	printk( KERN_DEBUG "do_crw_pending : done\n");
 #endif
 	if (cio_debug_initialized)
 		debug_sprintf_event(cio_debug_crw_id, 2,
@@ -6715,7 +6652,7 @@
 {
 	int ret = 0;
 
-	cio_debug_msg_id = debug_register("cio_msg",2,4,16*sizeof(long));
+	cio_debug_msg_id = debug_register("cio_msg",4,4,16*sizeof(long));
 	if (cio_debug_msg_id != NULL) {
 		debug_register_view(cio_debug_msg_id, &debug_sprintf_view);
 #ifdef CONFIG_DEBUG_IO
@@ -6756,6 +6693,7 @@
 
 __initcall(cio_debug_init);
 
+#ifdef CONFIG_PROC_FS
 /* 
  * Display info on subchannels in /proc/subchannels. 
  * Adapted from procfs stuff in dasd.c by Cornelia Huck, 02/28/01.      
@@ -7163,19 +7101,19 @@
 				entry->cio_chpid_entry = create_proc_entry( "chpids", S_IFREG|S_IRUGO, entry->cio_device_entry);
 				entry->cio_chpid_entry->proc_fops = &cio_chpid_entry_file_ops;
 			} else {
-				printk("Error, could not allocate procfs structure!\n");
+				printk( KERN_WARNING "Error, could not allocate procfs structure!\n");
 				remove_proc_entry(buf, cio_procfs_deviceinfo_root);
 				kfree(entry);
 				rc = -ENOMEM;
 			}
 		} else {
-			printk("Error, could not allocate procfs structure!\n");
+			printk( KERN_WARNING "Error, could not allocate procfs structure!\n");
 			kfree(entry);
 			rc = -ENOMEM;
 		}
 
 	} else {
-		printk("Error, could not allocate procfs structure!\n");
+		printk( KERN_WARNING "Error, could not allocate procfs structure!\n");
 		rc = -ENOMEM;
 	}
 	return rc;
@@ -7288,15 +7226,14 @@
  */
 
 static struct proc_dir_entry *cio_ignore_proc_entry;
-
 static int cio_ignore_proc_open(struct inode *inode, struct file *file)
 {
 	int rc = 0;
 	int size = 1;
 	int len = 0;
 	tempinfo_t *info;
-	dev_blacklist_range_t *tmp;
 	long flags;
+	int i, j;
 
 	info = (tempinfo_t *) vmalloc(sizeof(tempinfo_t));
 	if (info == NULL) {
@@ -7304,7 +7241,7 @@
 		rc = -ENOMEM;
 	} else {
 		file->private_data = (void *) info;
-		size += nr_blacklisted_ranges * 32;
+		size += nr_ignored * 6;
 		info->data = (char *) vmalloc(size);
 		if (size && info->data == NULL) {
 			printk( KERN_WARNING "No memory available for data\n");
@@ -7312,13 +7249,15 @@
 			rc = -ENOMEM;
 		} else {
 			spin_lock_irqsave( &blacklist_lock, flags ); 
-			tmp = dev_blacklist_range_head;
-			while (tmp) {
-				len += sprintf(info->data+len, "%04x ", tmp->from);
-				if (tmp->to != tmp->from) 
-					len += sprintf(info->data+len, "- %04x", tmp->to);
+			for (i=0;i<=highest_ignored;i++) 
+				if (test_bit(i,&bl_dev)) {
+					len += sprintf(info->data+len, "%04x ", i);
+					for (j=i;(j<=highest_ignored) && (test_bit(j,&bl_dev));j++);
+					j--;
+					if (i != j) 
+						len += sprintf(info->data+len, "- %04x", j);
 				len += sprintf(info->data+len, "\n");
-				tmp = tmp->next;
+					i=j;
 			}
 			spin_unlock_irqrestore( &blacklist_lock, flags );
 			info->len = len;
@@ -7370,7 +7309,7 @@
 	}
 	buffer[user_len]='\0';
 #ifdef CIO_DEBUG_IO
-	printk ("/proc/cio_ignore: '%s'\n", buffer);
+	printk ( KERN_DEBUG "/proc/cio_ignore: '%s'\n", buffer);
 #endif /* CIO_DEBUG_IO */
 	if (cio_debug_initialized)
 		debug_sprintf_event(cio_debug_msg_id, 2, "/proc/cio_ignore: '%s'\n",buffer);
@@ -7491,6 +7430,7 @@
 __initcall(cio_irq_proc_init);	
 
 /* end of procfs stuff */
+#endif
 
 schib_t *s390_get_schib( int irq )
 {
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/s390/sysinfo.c linux/drivers/s390/sysinfo.c
--- linux.orig/drivers/s390/sysinfo.c	Thu Jan  1 00:00:00 1970
+++ linux/drivers/s390/sysinfo.c	Fri Dec 21 18:00:38 2001
@@ -0,0 +1,354 @@
+/*
+ *  drivers/s390/sysinfo.c
+ *
+ *    Copyright (C) 2001 IBM Deutschland Entwicklung GmbH, IBM Corporation
+ *    Author(s): Ulrich Weigand (Ulrich.Weigand@de.ibm.com)
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/proc_fs.h>
+#include <linux/init.h>
+#include <asm/ebcdic.h>
+
+struct sysinfo_1_1_1
+{
+	char reserved_0[32];
+	char manufacturer[16];
+	char type[4];
+	char reserved_1[12];
+	char model[16];
+	char sequence[16];
+	char plant[4];
+};
+
+struct sysinfo_1_2_1
+{
+	char reserved_0[80];
+	char sequence[16];
+	char plant[4];
+	char reserved_1[2];
+	unsigned short cpu_address;
+};
+
+struct sysinfo_1_2_2
+{
+	char reserved_0[32];
+	unsigned int capability;
+	unsigned short cpus_total;
+	unsigned short cpus_configured;
+	unsigned short cpus_standby;
+	unsigned short cpus_reserved;
+	unsigned short adjustment[0];
+};
+
+struct sysinfo_2_2_1
+{
+	char reserved_0[80];
+	char sequence[16];
+	char plant[4];
+	unsigned short cpu_id;
+	unsigned short cpu_address;
+};
+
+struct sysinfo_2_2_2
+{
+	char reserved_0[32];
+	unsigned short lpar_number;
+	char reserved_1;
+	unsigned char characteristics;
+	#define LPAR_CHAR_DEDICATED	(1 << 7)
+	#define LPAR_CHAR_SHARED	(1 << 6)
+	#define LPAR_CHAR_LIMITED	(1 << 5)
+	unsigned short cpus_total;
+	unsigned short cpus_configured;
+	unsigned short cpus_standby;
+	unsigned short cpus_reserved;
+	char name[8];
+	unsigned int caf;
+	char reserved_2[16];
+	unsigned short cpus_dedicated;
+	unsigned short cpus_shared;
+};
+
+struct sysinfo_3_2_2
+{
+	char reserved_0[31];
+	unsigned char count;
+	struct
+	{
+		char reserved_0[4];
+		unsigned short cpus_total;
+		unsigned short cpus_configured;
+		unsigned short cpus_standby;
+		unsigned short cpus_reserved;
+		char name[8];
+		unsigned int caf;
+		char cpi[16];
+		char reserved_1[24];
+
+	} vm[8];
+};
+
+union s390_sysinfo
+{
+	struct sysinfo_1_1_1 sysinfo_1_1_1;
+	struct sysinfo_1_2_1 sysinfo_1_2_1;
+	struct sysinfo_1_2_2 sysinfo_1_2_2;
+	struct sysinfo_2_2_1 sysinfo_2_2_1;
+	struct sysinfo_2_2_2 sysinfo_2_2_2;
+	struct sysinfo_3_2_2 sysinfo_3_2_2;
+};
+
+static inline int stsi (void *sysinfo, 
+                        int fc, int sel1, int sel2)
+{
+	int cc, retv;
+
+#ifndef CONFIG_ARCH_S390X
+	__asm__ __volatile__ (	"lr\t0,%2\n"
+				"\tlr\t1,%3\n"
+				"\tstsi\t0(%4)\n"
+				"0:\tipm\t%0\n"
+				"\tsrl\t%0,28\n"
+				"1:lr\t%1,0\n"
+				".section .fixup,\"ax\"\n"
+				"2:\tlhi\t%0,3\n"
+				"\tbras\t1,3f\n"
+				"\t.long 1b\n"
+				"3:\tl\t1,0(1)\n"
+				"\tbr\t1\n"
+				".previous\n"
+				".section __ex_table,\"a\"\n"
+				"\t.align 4\n"
+				"\t.long 0b,2b\n"
+				".previous\n"
+				: "=d" (cc), "=d" (retv)
+				: "d" ((fc << 28) | sel1), "d" (sel2), "a" (sysinfo) 
+				: "cc", "memory", "0", "1" );
+#else
+	__asm__ __volatile__ (	"lr\t0,%2\n"
+				"lr\t1,%3\n"
+				"\tstsi\t0(%4)\n"
+				"0:\tipm\t%0\n"
+				"\tsrl\t%0,28\n"
+				"1:lr\t%1,0\n"
+				".section .fixup,\"ax\"\n"
+				"2:\tlhi\t%0,3\n"
+				"\tjg\t1b\n"
+				".previous\n"
+				".section __ex_table,\"a\"\n"
+				"\t.align 8\n"
+				"\t.quad 0b,2b\n"
+				".previous\n"
+				: "=d" (cc), "=d" (retv)
+				: "d" ((fc << 28) | sel1), "d" (sel2), "a" (sysinfo) 
+				: "cc", "memory", "0", "1" );
+#endif
+
+	return cc? -1 : retv;
+}
+
+static inline int stsi_0 (void)
+{
+	int rc = stsi (NULL, 0, 0, 0);
+	return rc == -1 ? rc : (((unsigned int)rc) >> 28);
+}
+
+static inline int stsi_1_1_1 (struct sysinfo_1_1_1 *info)
+{
+	int rc = stsi (info, 1, 1, 1);
+	if (rc != -1)
+	{
+		EBCASC (info->manufacturer, sizeof(info->manufacturer));
+		EBCASC (info->type, sizeof(info->type));
+		EBCASC (info->model, sizeof(info->model));
+		EBCASC (info->sequence, sizeof(info->sequence));
+		EBCASC (info->plant, sizeof(info->plant));
+	}
+	return rc == -1 ? rc : 0;
+}
+
+static inline int stsi_1_2_1 (struct sysinfo_1_2_1 *info)
+{
+	int rc = stsi (info, 1, 2, 1);
+	if (rc != -1)
+	{
+		EBCASC (info->sequence, sizeof(info->sequence));
+		EBCASC (info->plant, sizeof(info->plant));
+	}
+	return rc == -1 ? rc : 0;
+}
+
+static inline int stsi_1_2_2 (struct sysinfo_1_2_2 *info)
+{
+	int rc = stsi (info, 1, 2, 2);
+	return rc == -1 ? rc : 0;
+}
+
+static inline int stsi_2_2_1 (struct sysinfo_2_2_1 *info)
+{
+	int rc = stsi (info, 2, 2, 1);
+	if (rc != -1)
+	{
+		EBCASC (info->sequence, sizeof(info->sequence));
+		EBCASC (info->plant, sizeof(info->plant));
+	}
+	return rc == -1 ? rc : 0;
+}
+
+static inline int stsi_2_2_2 (struct sysinfo_2_2_2 *info)
+{
+	int rc = stsi (info, 2, 2, 2);
+	if (rc != -1)
+	{
+		EBCASC (info->name, sizeof(info->name));
+  	}
+	return rc == -1 ? rc : 0;
+}
+
+static inline int stsi_3_2_2 (struct sysinfo_3_2_2 *info)
+{
+	int rc = stsi (info, 3, 2, 2);
+	if (rc != -1)
+	{
+		int i;
+		for (i = 0; i < info->count; i++)
+		{
+			EBCASC (info->vm[i].name, sizeof(info->vm[i].name));
+			EBCASC (info->vm[i].cpi, sizeof(info->vm[i].cpi));
+		}
+	}
+	return rc == -1 ? rc : 0;
+}
+
+
+static int proc_read_sysinfo(char *page, char **start,
+                             off_t off, int count,
+                             int *eof, void *data)
+{
+	unsigned long info_page = get_free_page (GFP_KERNEL); 
+	union s390_sysinfo *info = (union s390_sysinfo *) info_page;
+	int len = 0;
+	int level;
+	int i;
+	
+	if (!info)
+		return 0;
+
+	level = stsi_0 ();
+
+	if (level < 1)
+		goto out;
+	if (stsi_1_1_1 (&info->sysinfo_1_1_1))
+		goto out;
+
+	len += sprintf (page+len, "Manufacturer:         %-16.16s\n",
+			info->sysinfo_1_1_1.manufacturer);
+	len += sprintf (page+len, "Type:                 %-4.4s\n",
+			info->sysinfo_1_1_1.type);
+	len += sprintf (page+len, "Model:                %-16.16s\n",
+			info->sysinfo_1_1_1.model);
+	len += sprintf (page+len, "Sequence Code:        %-16.16s\n",
+			info->sysinfo_1_1_1.sequence);
+	len += sprintf (page+len, "Plant:                %-4.4s\n",
+			info->sysinfo_1_1_1.plant);
+
+	if (stsi_1_2_2 (&info->sysinfo_1_2_2))
+		goto out;
+
+	len += sprintf (page+len, "\n");
+	len += sprintf (page+len, "CPUs Total:           %d\n",
+			info->sysinfo_1_2_2.cpus_total);
+	len += sprintf (page+len, "CPUs Configured:      %d\n",
+			info->sysinfo_1_2_2.cpus_configured);
+	len += sprintf (page+len, "CPUs Standby:         %d\n",
+			info->sysinfo_1_2_2.cpus_standby);
+	len += sprintf (page+len, "CPUs Reserved:        %d\n",
+			info->sysinfo_1_2_2.cpus_reserved);
+
+	len += sprintf (page+len, "Capability:           %d\n",
+			info->sysinfo_1_2_2.capability);
+
+	for (i = 2; i <= info->sysinfo_1_2_2.cpus_total; i++)
+		len += sprintf (page+len, "Adjustment %02d-way:    %d\n",
+				i, info->sysinfo_1_2_2.adjustment[i-2]);
+			
+	if (level < 2)
+		goto out;
+	if (stsi_2_2_2 (&info->sysinfo_2_2_2))
+		goto out;
+
+	len += sprintf (page+len, "\n");
+	len += sprintf (page+len, "LPAR Number:          %d\n",
+			info->sysinfo_2_2_2.lpar_number);
+
+	len += sprintf (page+len, "LPAR Characteristics: ");
+	if (info->sysinfo_2_2_2.characteristics & LPAR_CHAR_DEDICATED)
+		len += sprintf (page+len, "Dedicated ");
+	if (info->sysinfo_2_2_2.characteristics & LPAR_CHAR_SHARED)
+		len += sprintf (page+len, "Shared ");
+	if (info->sysinfo_2_2_2.characteristics & LPAR_CHAR_LIMITED)
+		len += sprintf (page+len, "Limited ");
+	len += sprintf (page+len, "\n");
+
+	len += sprintf (page+len, "LPAR Name:            %-8.8s\n",
+			info->sysinfo_2_2_2.name);
+
+	len += sprintf (page+len, "LPAR Adjustment:      %d\n",
+			info->sysinfo_2_2_2.caf);
+
+	len += sprintf (page+len, "LPAR CPUs Total:      %d\n",
+			info->sysinfo_2_2_2.cpus_total);
+	len += sprintf (page+len, "LPAR CPUs Configured: %d\n",
+			info->sysinfo_2_2_2.cpus_configured);
+	len += sprintf (page+len, "LPAR CPUs Standby:    %d\n",
+			info->sysinfo_2_2_2.cpus_standby);
+	len += sprintf (page+len, "LPAR CPUs Reserved:   %d\n",
+			info->sysinfo_2_2_2.cpus_reserved);
+	len += sprintf (page+len, "LPAR CPUs Dedicated:  %d\n",
+			info->sysinfo_2_2_2.cpus_dedicated);
+	len += sprintf (page+len, "LPAR CPUs Shared:     %d\n",
+			info->sysinfo_2_2_2.cpus_shared);
+
+	if (level < 3)
+		goto out;
+	if (stsi_3_2_2 (&info->sysinfo_3_2_2))
+		goto out;
+
+	for (i = 0; i < info->sysinfo_3_2_2.count; i++)
+	{
+		len += sprintf (page+len, "\n");
+		len += sprintf (page+len, "VM%02d Name:            %-8.8s\n",
+				i, info->sysinfo_3_2_2.vm[i].name);
+		len += sprintf (page+len, "VM%02d Control Program: %-16.16s\n",
+				i, info->sysinfo_3_2_2.vm[i].cpi);
+
+		len += sprintf (page+len, "VM%02d Adjustment:      %d\n",
+				i, info->sysinfo_3_2_2.vm[i].caf);
+
+		len += sprintf (page+len, "VM%02d CPUs Total:      %d\n",
+				i, info->sysinfo_3_2_2.vm[i].cpus_total);
+		len += sprintf (page+len, "VM%02d CPUs Configured: %d\n",
+				i, info->sysinfo_3_2_2.vm[i].cpus_configured);
+		len += sprintf (page+len, "VM%02d CPUs Standby:    %d\n",
+				i, info->sysinfo_3_2_2.vm[i].cpus_standby);
+		len += sprintf (page+len, "VM%02d CPUs Reserved:   %d\n",
+				i, info->sysinfo_3_2_2.vm[i].cpus_reserved);
+	}
+
+out:
+	free_page (info_page);
+        return len;
+}
+
+static __init int create_proc_sysinfo(void)
+{
+	create_proc_read_entry ("sysinfo", 0444, NULL, 
+				proc_read_sysinfo, NULL);
+	return 0;
+}
+
+__initcall(create_proc_sysinfo);
+
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/scsi/3w-xxxx.c linux/drivers/scsi/3w-xxxx.c
--- linux.orig/drivers/scsi/3w-xxxx.c	Fri Nov  9 22:05:02 2001
+++ linux/drivers/scsi/3w-xxxx.c	Mon Jan  7 17:06:50 2002
@@ -6,7 +6,7 @@
    		     Arnaldo Carvalho de Melo <acme@conectiva.com.br>
                      Brad Strand <linux@3ware.com>
 
-   Copyright (C) 1999-2001 3ware Inc.
+   Copyright (C) 1999-2002 3ware Inc.
 
    Kernel compatablity By: 	Andre Hedrick <andre@suse.com>
    Non-Copyright (C) 2000	Andre Hedrick <andre@suse.com>
@@ -106,6 +106,19 @@
                  Add entire aen code string list.
    1.02.00.010 - Cleanup queueing code, fix jbod thoughput.
                  Fix get_param for specific units.
+   1.02.00.011 - Fix bug in tw_aen_complete() where aen's could be lost.
+                 Fix tw_aen_drain_queue() to display useful info at init.
+                 Set tw_host->max_id for 12 port cards.
+                 Add ioctl support for raw command packet post from userspace
+                 with sglist fragments (parameter and io).
+   1.02.00.012 - Fix read capacity to under report by 1 sector to fix get
+                 last sector ioctl.
+   1.02.00.013 - Fix bug where more AEN codes weren't coming out during
+                 driver initialization.
+                 Improved handling of PCI aborts.
+   1.02.00.014 - Fix bug in tw_findcards() where AEN code could be lost.
+                 Increase timeout in tw_aen_drain_queue() to 30 seconds.
+   1.02.00.015 - Re-write raw command post with data ioctl method.
 */
 
 #include <linux/module.h>
@@ -114,7 +127,6 @@
 MODULE_DESCRIPTION ("3ware Storage Controller Linux Driver");
 MODULE_LICENSE("GPL");
 
-
 #include <linux/kernel.h>
 #include <linux/pci.h>
 #include <linux/time.h>
@@ -149,11 +161,11 @@
 
 /* Notifier block to get a notify on system shutdown/halt/reboot */
 static struct notifier_block tw_notifier = {
-  tw_halt, NULL, 0
+	tw_halt, NULL, 0
 };
 
 /* Globals */
-char *tw_driver_version="1.02.00.010";
+char *tw_driver_version="1.02.00.015";
 TW_Device_Extension *tw_device_extension_list[TW_MAX_SLOT];
 int tw_device_extension_count = 0;
 
@@ -164,6 +176,7 @@
 {
 	TW_Param *param;
 	unsigned short aen;
+	int error = 0;
 
 	dprintk(KERN_WARNING "3w-xxxx: tw_aen_complete()\n");
 	if (tw_dev->alignment_virtual_address[request_id] == NULL) {
@@ -182,12 +195,15 @@
 			if ((tw_aen_string[aen & 0xff][strlen(tw_aen_string[aen & 0xff])-1]) == '#') {
 				printk(KERN_WARNING "3w-xxxx: scsi%d: AEN: %s%d.\n", tw_dev->host->host_no, tw_aen_string[aen & 0xff], aen >> 8);
 			} else {
-				printk(KERN_WARNING "3w-xxxx: scsi%d: AEN: %s.\n", tw_dev->host->host_no, tw_aen_string[aen & 0xff]);
+				if (aen != 0x0) 
+					printk(KERN_WARNING "3w-xxxx: scsi%d: AEN: %s.\n", tw_dev->host->host_no, tw_aen_string[aen & 0xff]);
 			}
-		} else
+		} else {
 			printk(KERN_WARNING "3w-xxxx: scsi%d: Received AEN %d.\n", tw_dev->host->host_no, aen);
+		}
 	}
-	tw_dev->aen_count++;
+	if (aen != 0x0) 
+		tw_dev->aen_count++;
 
 	/* Now queue the code */
 	tw_dev->aen_queue[tw_dev->aen_tail] = aen;
@@ -203,8 +219,18 @@
 			tw_dev->aen_head = tw_dev->aen_head + 1;
 		}
 	}
-	tw_dev->state[request_id] = TW_S_COMPLETED;
-	tw_state_request_finish(tw_dev, request_id);
+
+	if (aen != TW_AEN_QUEUE_EMPTY) {
+		error = tw_aen_read_queue(tw_dev, request_id);
+		if (error) {
+			printk(KERN_WARNING "3w-xxxx: scsi%d: Error completing AEN.\n", tw_dev->host->host_no);
+			tw_dev->state[request_id] = TW_S_COMPLETED;
+			tw_state_request_finish(tw_dev, request_id);
+		}
+	} else {
+		tw_dev->state[request_id] = TW_S_COMPLETED;
+		tw_state_request_finish(tw_dev, request_id);
+	}
 
 	return 0;
 } /* End tw_aen_complete() */
@@ -235,10 +261,11 @@
 	status_reg_addr = tw_dev->registers.status_reg_addr;
 	response_que_addr = tw_dev->registers.response_que_addr;
 
-	if (tw_poll_status(tw_dev, TW_STATUS_ATTENTION_INTERRUPT, 15)) {
+	if (tw_poll_status(tw_dev, TW_STATUS_ATTENTION_INTERRUPT, 30)) {
 		dprintk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): No attention interrupt for card %d.\n", tw_device_extension_count);
 		return 1;
 	}
+	tw_clear_attention_interrupt(tw_dev);
 
 	/* Initialize command packet */
 	if (tw_dev->command_packet_virtual_address[request_id] == NULL) {
@@ -286,7 +313,7 @@
 	do {
 		/* Post command packet */
 		outl(command_que_value, command_que_addr);
-    
+
 		/* Now poll for completion */
 		for (i=0;i<imax;i++) {
 			mdelay(5);
@@ -324,7 +351,7 @@
 				queue = 0;
 				switch (aen_code) {
 					case TW_AEN_QUEUE_EMPTY:
-						dprintk(KERN_NOTICE "3w-xxxx: tw_aen_drain_queue(): Found TW_AEN_QUEUE_EMPTY.\n");
+						dprintk(KERN_WARNING "3w-xxxx: AEN: %s.\n", tw_aen_string[aen & 0xff]);
 						if (first_reset != 1) {
 							continue;
 						} else {
@@ -332,51 +359,28 @@
 						}
 						break;
 					case TW_AEN_SOFT_RESET:
-						dprintk(KERN_NOTICE "3w-xxxx: tw_aen_drain_queue(): Found TW_AEN_SOFT_RESET.\n");
 						if (first_reset == 0) {
 							first_reset = 1;
 						} else {
+							printk(KERN_WARNING "3w-xxxx: AEN: %s.\n", tw_aen_string[aen & 0xff]);
+							tw_dev->aen_count++;
 							queue = 1;
 						}
 						break;
-					case TW_AEN_DEGRADED_MIRROR:
-						dprintk(KERN_NOTICE "3w-xxxx: tw_aen_drain_queue(): Found TW_AEN_DEGRADED_MIRROR.\n");
-						queue = 1;
-						break;
-					case TW_AEN_CONTROLLER_ERROR:
-						dprintk(KERN_NOTICE "3w-xxxx: tw_aen_drain_queue(): Found TW_AEN_CONTROLLER_ERROR.\n");
-						queue = 1;
-						break;
-					case TW_AEN_REBUILD_FAIL:
-						dprintk(KERN_NOTICE "3w-xxxx: tw_aen_drain_queue(): Found TW_AEN_REBUILD_FAIL.\n");
-						queue = 1;
-						break;
-					case TW_AEN_REBUILD_DONE:
-						dprintk(KERN_NOTICE "3w-xxxx: tw_aen_drain_queue(): Found TW_AEN_REBUILD_DONE.\n");
-						queue = 1;
-						break;
-					case TW_AEN_QUEUE_FULL:
-						dprintk(KERN_NOTICE "3w-xxxx: tw_aen_drain_queue(): Found TW_AEN_QUEUE_FULL.\n");
-						queue = 1;
-						break;
-					case TW_AEN_APORT_TIMEOUT:
-						printk(KERN_WARNING "3w-xxxx: Received drive timeout AEN on port %d, check drive and drive cables.\n", aen >> 8);
-						queue = 1;
-						break;
-					case TW_AEN_DRIVE_ERROR:
-						printk(KERN_WARNING "3w-xxxx: Received drive error AEN on port %d, check/replace cabling, or possible bad drive.\n", aen >> 8);
-						queue = 1;
-						break;
-					case TW_AEN_SMART_FAIL:
-						printk(KERN_WARNING "3w-xxxx: Received S.M.A.R.T. threshold AEN on port %d, check drive/cooling, or possible bad drive.\n", aen >> 8);
-						queue = 1;
-						break;
-					case TW_AEN_SBUF_FAIL:
-						printk(KERN_WARNING "3w-xxxx: Received SBUF integrity check failure AEN, reseat card or bad card.\n");
-						queue = 1;
-						break;
 					default:
-						dprintk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): Unknown AEN code 0x%x.\n", aen_code);
+						if (aen == 0x0ff) {
+							printk(KERN_WARNING "3w-xxxx: AEN: AEN queue overflow.\n");
+						} else {
+							if ((aen & 0x0ff) < TW_AEN_STRING_MAX) {
+								if ((tw_aen_string[aen & 0xff][strlen(tw_aen_string[aen & 0xff])-1]) == '#') {
+									printk(KERN_WARNING "3w-xxxx: AEN: %s%d.\n", tw_aen_string[aen & 0xff], aen >> 8);
+								} else {
+									printk(KERN_WARNING "3w-xxxx: AEN: %s.\n", tw_aen_string[aen & 0xff]);
+								}
+							} else
+								printk(KERN_WARNING "3w-xxxx: Received AEN %d.\n", aen);
+						}
+						tw_dev->aen_count++;
 						queue = 1;
 				}
 
@@ -619,6 +623,11 @@
 	case TW_STATUS_MICROCONTROLLER_ERROR:
 		printk(KERN_WARNING "3w-xxxx: Microcontroller Error.\n");
 		break;
+	case TW_STATUS_PCI_ABORT:
+		printk(KERN_WARNING "3w-xxxx: PCI Abort: clearing.\n");
+		outl(TW_CONTROL_CLEAR_PCI_ABORT, tw_dev->registers.control_reg_addr);
+		pci_write_config_word(tw_dev->tw_pci_dev, PCI_STATUS, TW_PCI_CLEAR_PCI_ABORT);
+		break;
   }
 } /* End tw_decode_bits() */
 
@@ -689,11 +698,22 @@
 	u32 control_reg_value, control_reg_addr;
 
 	control_reg_addr = tw_dev->registers.control_reg_addr;
+	control_reg_value = (TW_CONTROL_ENABLE_INTERRUPTS |
+			     TW_CONTROL_UNMASK_RESPONSE_INTERRUPT);
+	outl(control_reg_value, control_reg_addr);
+} /* End tw_enable_interrupts() */
+
+/* This function will enable interrupts on the controller */
+void tw_enable_and_clear_interrupts(TW_Device_Extension *tw_dev)
+{
+	u32 control_reg_value, control_reg_addr;
+
+	control_reg_addr = tw_dev->registers.control_reg_addr;
 	control_reg_value = (TW_CONTROL_CLEAR_ATTENTION_INTERRUPT |
 			     TW_CONTROL_UNMASK_RESPONSE_INTERRUPT |
 			     TW_CONTROL_ENABLE_INTERRUPTS);
 	outl(control_reg_value, control_reg_addr);
-} /* End tw_enable_interrupts() */
+} /* End tw_enable_and_clear_interrupts() */
 
 /* This function will find and initialize all cards */
 int tw_findcards(Scsi_Host_Template *tw_host) 
@@ -761,14 +781,14 @@
 			  
 				error = tw_aen_drain_queue(tw_dev);
 				if (error) {
-					printk(KERN_WARNING "3w-xxxx: tw_findcards(): No attention interrupt for card %d.\n", numcards);
+					printk(KERN_WARNING "3w-xxxx: AEN drain failed for card %d.\n", numcards);
 					tries++;
 					continue;
 				}
 
 				/* Check for controller errors */
 				if (tw_check_errors(tw_dev)) {
-					printk(KERN_WARNING "3w-xxxx: tw_findcards(): Controller errors found, soft resetting card %d.\n", numcards);
+					printk(KERN_WARNING "3w-xxxx: Controller errors found, retrying for card %d.\n", numcards);
 					tries++;
 					continue;
 				}
@@ -776,7 +796,7 @@
 				/* Empty the response queue */
 				error = tw_empty_response_que(tw_dev);
 				if (error) {
-					printk(KERN_WARNING "3w-xxxx: tw_findcards(): Couldn't empty response queue for card %d.\n", numcards);
+					printk(KERN_WARNING "3w-xxxx: Couldn't empty response queue, retrying for card %d.\n", numcards);
 					tries++;
 					continue;
 				}
@@ -786,7 +806,7 @@
 			}
 
 			if (tries >= TW_MAX_RESET_TRIES) {
-				printk(KERN_WARNING "3w-xxxx: tw_findcards(): Controller error or no attention interrupt: giving up for card %d.\n", numcards);
+				printk(KERN_WARNING "3w-xxxx: Controller errors, card not responding, check all cabling for card %d.\n", numcards);
 				tw_free_device_extension(tw_dev);
 				kfree(tw_dev);
 				continue;
@@ -816,7 +836,7 @@
 
 			error = tw_initconnection(tw_dev, TW_INIT_MESSAGE_CREDITS);
 			if (error) {
-				printk(KERN_WARNING "3w-xxxx: tw_findcards(): Couldn't initconnection for card %d.\n", numcards);
+				printk(KERN_WARNING "3w-xxxx: Connection initialization failed for card %d.\n", numcards);
 				release_region((tw_dev->tw_pci_dev->resource[0].start), TW_IO_ADDRESS_RANGE);
 				tw_free_device_extension(tw_dev);
 				kfree(tw_dev);
@@ -841,13 +861,16 @@
 		/* Register the card with the kernel SCSI layer */
 			host = scsi_register(tw_host, sizeof(TW_Device_Extension));
 			if (host == NULL) {
-				printk(KERN_WARNING "3w-xxxx: tw_findcards(): scsi_register() failed for card %d.\n", numcards-1);
+				printk(KERN_WARNING "3w-xxxx: tw_findcards(): scsi_register() failed for card %d.\n", numcards);
 				release_region((tw_dev->tw_pci_dev->resource[0].start), TW_IO_ADDRESS_RANGE);
 				tw_free_device_extension(tw_dev);
 				kfree(tw_dev);
 				continue;
 			}
 
+			/* Set max target id's */
+			host->max_id = TW_MAX_UNITS;
+
 			/* Set max sectors per io */
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,7)
 			host->max_sectors = TW_MAX_SECTORS;
@@ -872,7 +895,7 @@
 				tw_device_extension_count = numcards;
 				tw_dev2->host = host;
 			} else { 
-				printk(KERN_WARNING "3w-xxxx: tw_findcards(): Bad scsi host data for card %d.\n", numcards-1);
+				printk(KERN_WARNING "3w-xxxx: tw_findcards(): Bad scsi host data for card %d.\n", numcards);
 				scsi_unregister(host);
 				release_region((tw_dev->tw_pci_dev->resource[0].start), TW_IO_ADDRESS_RANGE);
 				tw_free_device_extension(tw_dev);
@@ -1052,6 +1075,7 @@
 		tw_dev->state[i] = TW_S_INITIAL;
 		tw_dev->ioctl_size[i] = 0;
 		tw_dev->aen_queue[i] = 0;
+		tw_dev->ioctl_data[i] = NULL;
 	}
 
 	for (i=0;i<TW_MAX_UNITS;i++) {
@@ -1330,7 +1354,7 @@
 
 	if (tw_dev->tw_pci_dev->irq == irq) {
 		spin_lock(&tw_dev->tw_lock);
-		dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt()\n");
+		dprintk(KERN_WARNING "3w-xxxx: tw_interrupt()\n");
 
 		/* Read the registers */
 		status_reg_addr = tw_dev->registers.status_reg_addr;
@@ -1453,18 +1477,21 @@
 						tw_dev->srb[request_id]->result = (DID_BAD_TARGET << 16);
 						tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]);
 					}
-					if (error) {
+					if (error == 1) {
 						/* Tell scsi layer there was an error */
 						dprintk(KERN_WARNING "3w-xxxx: tw_interrupt(): Scsi Error.\n");
 						tw_dev->srb[request_id]->result = (DID_RESET << 16);
-					} else {
+					}
+					if (error == 0) {
 						/* Tell scsi layer command was a success */
 						tw_dev->srb[request_id]->result = (DID_OK << 16);
 					}
-					tw_dev->state[request_id] = TW_S_COMPLETED;
-					tw_state_request_finish(tw_dev, request_id);
-					tw_dev->posted_request_count--;
-					tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]);
+					if (error != 2) {
+						tw_dev->state[request_id] = TW_S_COMPLETED;
+						tw_state_request_finish(tw_dev, request_id);
+						tw_dev->posted_request_count--;
+						tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]);
+					}
 					status_reg_value = inl(status_reg_addr);
 					if (tw_check_bits(status_reg_value)) {
 						dprintk(KERN_WARNING "3w-xxxx: tw_interrupt(): Unexpected bits.\n");
@@ -1477,19 +1504,21 @@
 	}
 	spin_unlock_irqrestore(&io_request_lock, flags);
 	clear_bit(TW_IN_INTR, &tw_dev->flags);
-}	/* End tw_interrupt() */
+} /* End tw_interrupt() */
 
 /* This function handles ioctls from userspace to the driver */
 int tw_ioctl(TW_Device_Extension *tw_dev, int request_id)
 {
 	unsigned char opcode;
-	int bufflen;
+	int bufflen, error = 0;
 	TW_Param *param;
-	TW_Command *command_packet;
+	TW_Command *command_packet, *command_save;
 	u32 param_value;
 	TW_Ioctl *ioctl = NULL;
 	TW_Passthru *passthru = NULL;
-	int tw_aen_code;
+	int tw_aen_code, i, use_sg;
+	char *data_ptr;
+	int total_bytes = 0;
 
 	ioctl = (TW_Ioctl *)tw_dev->srb[request_id]->request_buffer;
 	if (ioctl == NULL) {
@@ -1610,6 +1639,159 @@
 				printk(KERN_WARNING "3w-xxxx: tw_ioctl(): ioctl->data NULL.\n");
 				return 1;
 			}
+		case TW_CMD_PACKET_WITH_DATA:
+			dprintk(KERN_WARNING "3w-xxxx: tw_ioctl(): caught TW_CMD_PACKET_WITH_DATA.\n");
+			command_save = (TW_Command *)tw_dev->alignment_virtual_address[request_id];
+			if (command_save == NULL) {
+				printk(KERN_WARNING "3w-xxxx: scsi%d: tw_ioctl(): Bad alignment virtual address.\n", tw_dev->host->host_no);
+				return 1;
+			}
+			if (ioctl->data != NULL) {
+				/* Copy down the command packet */
+				memcpy(command_packet, ioctl->data, sizeof(TW_Command));
+				memcpy(command_save, ioctl->data, sizeof(TW_Command));
+				command_packet->request_id = request_id;
+
+				/* Now deal with the two possible sglists */
+				if (command_packet->byte0.sgl_offset == 2) {
+					use_sg = command_packet->size - 3;
+					for (i=0;i<use_sg;i++)
+						total_bytes+=command_packet->byte8.param.sgl[i].length;
+					tw_dev->ioctl_data[request_id] = kmalloc(total_bytes, GFP_ATOMIC);
+					if (!tw_dev->ioctl_data[request_id]) {
+						printk(KERN_WARNING "3w-xxxx: scsi%d: tw_ioctl(): kmalloc failed for request_id %d.\n", tw_dev->host->host_no, request_id);
+						return 1;
+					}
+
+					/* Copy param sglist into the kernel */
+					data_ptr = tw_dev->ioctl_data[request_id];
+					for (i=0;i<use_sg;i++) {
+						if ((u32 *)command_packet->byte8.param.sgl[i].address != NULL) {
+							error = copy_from_user(data_ptr, (u32 *)command_packet->byte8.param.sgl[i].address, command_packet->byte8.param.sgl[i].length);
+							if (error) {
+								printk(KERN_WARNING "3w-xxxx: scsi%d: Error copying param sglist from userspace.\n", tw_dev->host->host_no);
+								goto tw_ioctl_bail;
+							}
+						} else {
+							printk(KERN_WARNING "3w-xxxx: scsi%d: tw_ioctl(): Bad param sgl address.\n", tw_dev->host->host_no);
+							tw_dev->srb[request_id]->result = (DID_RESET << 16);
+							goto tw_ioctl_bail;
+						}
+						data_ptr+=command_packet->byte8.param.sgl[i].length;
+					}
+					command_packet->size = 4;
+					command_packet->byte8.param.sgl[0].address = virt_to_bus(tw_dev->ioctl_data[request_id]);
+					command_packet->byte8.param.sgl[0].length = total_bytes;
+				}
+				if (command_packet->byte0.sgl_offset == 3) {
+					use_sg = command_packet->size - 4;
+					for (i=0;i<use_sg;i++)
+						total_bytes+=command_packet->byte8.io.sgl[i].length;
+					tw_dev->ioctl_data[request_id] = kmalloc(total_bytes, GFP_ATOMIC);
+					if (!tw_dev->ioctl_data[request_id]) {
+						printk(KERN_WARNING "3w-xxxx: scsi%d: tw_ioctl(): kmalloc failed for request_id %d.\n", tw_dev->host->host_no, request_id);
+						return 1;
+					}
+					if (command_packet->byte0.opcode == TW_OP_WRITE) {
+						/* Copy io sglist into the kernel */
+						data_ptr = tw_dev->ioctl_data[request_id];
+						for (i=0;i<use_sg;i++) {
+							if ((u32 *)command_packet->byte8.io.sgl[i].address != NULL) {
+								error = copy_from_user(data_ptr, (u32 *)command_packet->byte8.io.sgl[i].address, command_packet->byte8.io.sgl[i].length);
+								if (error) {
+									printk(KERN_WARNING "3w-xxxx: scsi%d: Error copying io sglist from userspace.\n", tw_dev->host->host_no);
+									goto tw_ioctl_bail;
+								}
+							} else {
+								printk(KERN_WARNING "3w-xxxx: scsi%d: tw_ioctl(): Bad io sgl address.\n", tw_dev->host->host_no);
+								tw_dev->srb[request_id]->result = (DID_RESET << 16);
+								goto tw_ioctl_bail;
+							}
+							data_ptr+=command_packet->byte8.io.sgl[i].length;
+						}
+					}
+					command_packet->size = 5;
+					command_packet->byte8.io.sgl[0].address = virt_to_bus(tw_dev->ioctl_data[request_id]);
+					command_packet->byte8.io.sgl[0].length = total_bytes;
+				}
+
+				spin_unlock_irq(&io_request_lock);
+				spin_unlock_irq(&tw_dev->tw_lock);
+
+				/* Finally post the command packet */
+				tw_post_command_packet(tw_dev, request_id);
+
+				mdelay(TW_IOCTL_WAIT_TIME);
+				spin_lock_irq(&tw_dev->tw_lock);
+				spin_lock_irq(&io_request_lock);
+
+				if (signal_pending(current)) {
+					dprintk(KERN_WARNING "3w-xxxx: scsi%d: tw_ioctl(): Signal pending, aborting ioctl().\n", tw_dev->host->host_no);
+					tw_dev->srb[request_id]->result = (DID_OK << 16);
+					goto tw_ioctl_bail;
+				}
+
+				tw_dev->srb[request_id]->result = (DID_OK << 16);
+				/* Now copy up the param or io sglist to userspace */
+				if (command_packet->byte0.sgl_offset == 2) {
+					use_sg = command_save->size - 3;
+					data_ptr = phys_to_virt(command_packet->byte8.param.sgl[0].address);
+					for (i=0;i<use_sg;i++) {
+						if ((u32 *)command_save->byte8.param.sgl[i].address != NULL) {
+							error = copy_to_user((u32 *)command_save->byte8.param.sgl[i].address, data_ptr, command_save->byte8.param.sgl[i].length);
+							if (error) {
+								printk(KERN_WARNING "3w-xxxx: scsi%d: Error copying param sglist to userspace.\n", tw_dev->host->host_no);
+								goto tw_ioctl_bail;
+							}
+							dprintk(KERN_WARNING "3w-xxxx: scsi%d: Copied %ld bytes to pid %d.\n", tw_dev->host->host_no, command_save->byte8.param.sgl[i].length, current->pid);
+							data_ptr+=command_save->byte8.param.sgl[i].length;
+						} else {
+							printk(KERN_WARNING "3w-xxxx: scsi%d: tw_ioctl(): Bad param sgl address.\n", tw_dev->host->host_no);
+							tw_dev->srb[request_id]->result = (DID_RESET << 16);
+							goto tw_ioctl_bail;
+						}
+					}
+				}
+				if (command_packet->byte0.sgl_offset == 3) {
+					use_sg = command_save->size - 4;
+					if (command_packet->byte0.opcode == TW_OP_READ) {
+						data_ptr = phys_to_virt(command_packet->byte8.io.sgl[0].address);
+						for(i=0;i<use_sg;i++) {
+							if ((u32 *)command_save->byte8.io.sgl[i].address != NULL) {
+								error = copy_to_user((u32 *)command_save->byte8.io.sgl[i].address, data_ptr, command_save->byte8.io.sgl[i].length);
+								if (error) {
+									printk(KERN_WARNING "3w-xxxx: scsi%d: Error copying io sglist to userspace.\n", tw_dev->host->host_no);
+									goto tw_ioctl_bail;
+								}
+								dprintk(KERN_WARNING "3w-xxxx: scsi%d: Copied %ld bytes to pid %d.\n", tw_dev->host->host_no, command_save->byte8.io.sgl[i].length, current->pid);
+								data_ptr+=command_save->byte8.io.sgl[i].length;
+							} else {
+								printk(KERN_WARNING "3w-xxxx: scsi%d: tw_ioctl(): Bad io sgl address.\n", tw_dev->host->host_no);
+								tw_dev->srb[request_id]->result = (DID_RESET << 16);
+								goto tw_ioctl_bail;
+							}
+						}
+					}
+				}
+				
+			tw_ioctl_bail:
+
+				/* Free up sglist memory */
+				if (tw_dev->ioctl_data[request_id])
+					kfree(tw_dev->ioctl_data[request_id]);
+				else
+					printk(KERN_WARNING "3w-xxxx: scsi%d: tw_ioctl(): Error freeing ioctl data.\n", tw_dev->host->host_no);
+				
+				/* Now complete the io */
+				tw_dev->state[request_id] = TW_S_COMPLETED;
+				tw_state_request_finish(tw_dev, request_id);
+				tw_dev->posted_request_count--;
+				tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]);
+				return 0;
+			} else {
+				printk(KERN_WARNING "3w-xxxx: tw_ioctl(): ioctl->data NULL.\n");
+				return 1;
+			}
 		default:
 			printk(KERN_WARNING "3w-xxxx: Unknown ioctl 0x%x.\n", opcode);
 			tw_dev->state[request_id] = TW_S_COMPLETED;
@@ -1653,6 +1835,7 @@
 	TW_Param *param;
 	TW_Ioctl *ioctl = NULL;
 	TW_Passthru *passthru = NULL;
+	TW_Command *command_packet;
 
 	ioctl = (TW_Ioctl *)tw_dev->srb[request_id]->request_buffer;
 	dprintk(KERN_NOTICE "3w-xxxx: tw_ioctl_complete()\n");
@@ -1661,6 +1844,13 @@
 		printk(KERN_WARNING "3w-xxxx: tw_ioctl_complete(): Request buffer NULL.\n");
 		return 1;
 	}
+
+	command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id];
+	if (command_packet == NULL) {
+		printk(KERN_WARNING "3w-xxxx: scsi%d: tw_ioctl_complete(): Bad command packet virtual address.\n", tw_dev->host->host_no);
+		return 1;
+	}
+
 	dprintk(KERN_NOTICE "3w-xxxx: tw_ioctl_complete(): Request_bufflen = %d\n", tw_dev->srb[request_id]->request_bufflen);
 
 	ioctl = (TW_Ioctl *)buff;
@@ -1669,6 +1859,9 @@
 			passthru = (TW_Passthru *)ioctl->data;
 			memcpy(buff, tw_dev->alignment_virtual_address[request_id], passthru->sector_count * 512);
 			break;
+		case TW_CMD_PACKET_WITH_DATA:
+			dprintk(KERN_WARNING "3w-xxxx: tw_ioctl_complete(): caught TW_CMD_PACKET_WITH_DATA.\n");
+			return 2; /* Special case for isr to not complete io */
 		default:
 			memset(buff, 0, tw_dev->srb[request_id]->request_bufflen);
 			param = (TW_Param *)tw_dev->alignment_virtual_address[request_id];
@@ -1819,7 +2012,7 @@
 
 		error = tw_aen_drain_queue(tw_dev);
 		if (error) {
-			printk(KERN_WARNING "3w-xxxx: scsi%d: Card not responding, retrying.\n", tw_dev->host->host_no);
+			printk(KERN_WARNING "3w-xxxx: scsi%d: AEN drain failed, retrying.\n", tw_dev->host->host_no);
 			tries++;
 			continue;
 		}
@@ -1855,7 +2048,7 @@
 	}
 
 	/* Re-enable interrupts */
-	tw_enable_interrupts(tw_dev);
+	tw_enable_and_clear_interrupts(tw_dev);
 
 	return 0;
 } /* End tw_reset_sequence() */
@@ -2381,6 +2574,9 @@
 	capacity = (param_data[3] << 24) | (param_data[2] << 16) | 
 		   (param_data[1] << 8) | param_data[0];
 
+	/* Subtract one sector to fix get last sector ioctl */
+	capacity -= 1;
+
 	dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_read_capacity_complete(): Capacity = 0x%x.\n", capacity);
 
 	/* Number of LBA's */
@@ -2671,7 +2867,7 @@
 	}
 
 	/* Re-enable interrupts */
-	tw_enable_interrupts(tw_dev);
+	tw_enable_and_clear_interrupts(tw_dev);
 
 	return 0;
 } /* End tw_shutdown_device() */
@@ -2719,7 +2915,7 @@
 	int id = 0;
 
 	dprintk(KERN_NOTICE "3w-xxxx: tw_state_request_start()\n");
-
+	
 	/* Obtain next free request_id */
 	do {
 		if (tw_dev->free_head == tw_dev->free_wrap) {
@@ -2749,7 +2945,6 @@
 } /* End tw_unmask_command_interrupt() */
 
 /* Now get things going */
-
 static Scsi_Host_Template driver_template = TWXXXX;
 #include "scsi_module.c"
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/scsi/3w-xxxx.h linux/drivers/scsi/3w-xxxx.h
--- linux.orig/drivers/scsi/3w-xxxx.h	Fri Nov  9 22:05:02 2001
+++ linux/drivers/scsi/3w-xxxx.h	Mon Jan  7 17:06:50 2002
@@ -6,7 +6,7 @@
    		     Arnaldo Carvalho de Melo <acme@conectiva.com.br>
                      Brad Strand <linux@3ware.com>
 
-   Copyright (C) 1999-2001 3ware Inc.
+   Copyright (C) 1999-2002 3ware Inc.
 
    Kernel compatablity By:	Andre Hedrick <andre@suse.com>
    Non-Copyright (C) 2000	Andre Hedrick <andre@suse.com>
@@ -62,7 +62,7 @@
 static char *tw_aen_string[] = {
 	"AEN queue empty",                      // 0x000
 	"Soft reset occurred",                  // 0x001
-	"Mirorr degraded: Unit #",              // 0x002
+	"Unit degraded: Unit #",                // 0x002
 	"Controller error",                     // 0x003 
 	"Rebuild failed: Unit #",               // 0x004
 	"Rebuild complete: Unit #",             // 0x005
@@ -90,10 +90,12 @@
 	"DCB unsupported version: Port #",      // 0x028
 	"Verify started: Unit #",               // 0x029
 	"Verify failed: Port #",                // 0x02A
-	"Verify complete: Unit #"               // 0x02B
+	"Verify complete: Unit #",              // 0x02B
+	"Overwrote bad sector during rebuild: Port #",  //0x2C
+	"Encountered bad sector during rebuild: Port #" //0x2D
 };
 
-#define TW_AEN_STRING_MAX                      0x02C
+#define TW_AEN_STRING_MAX                      0x02E
 
 /* Control register bit definitions */
 #define TW_CONTROL_CLEAR_HOST_INTERRUPT	       0x00080000
@@ -108,6 +110,7 @@
 #define TW_CONTROL_DISABLE_INTERRUPTS	       0x00000040
 #define TW_CONTROL_ISSUE_HOST_INTERRUPT	       0x00000020
 #define TW_CONTROL_CLEAR_PARITY_ERROR          0x00800000
+#define TW_CONTROL_CLEAR_PCI_ABORT             0x00100000
 
 /* Status register bit definitions */
 #define TW_STATUS_MAJOR_VERSION_MASK	       0xF0000000
@@ -140,6 +143,7 @@
 #define TW_DEVICE_ID2 (0x1001)  /* 7000 series controller */
 #define TW_NUMDEVICES 2
 #define TW_PCI_CLEAR_PARITY_ERRORS 0xc100
+#define TW_PCI_CLEAR_PCI_ABORT     0x2000
 
 /* Command packet opcodes */
 #define TW_OP_NOP	      0x0
@@ -153,6 +157,7 @@
 #define TW_OP_AEN_LISTEN      0x1c
 #define TW_CMD_PACKET         0x1d
 #define TW_ATA_PASSTHRU       0x1e
+#define TW_CMD_PACKET_WITH_DATA 0x1f
 
 /* Asynchronous Event Notification (AEN) Codes */
 #define TW_AEN_QUEUE_EMPTY       0x0000
@@ -197,6 +202,7 @@
 #define TW_MAX_SECTORS                        128
 #endif 
 #define TW_AEN_WAIT_TIME                      1000
+#define TW_IOCTL_WAIT_TIME                    (1 * HZ) /* 1 second */
 
 /* Macros */
 #define TW_STATUS_ERRORS(x) \
@@ -381,6 +387,7 @@
 	unsigned char		aen_head;
 	unsigned char		aen_tail;
 	long			flags; /* long req'd for set_bit --RR */
+	char			*ioctl_data[TW_Q_LENGTH];
 } TW_Device_Extension;
 
 /* Function prototypes */
@@ -397,6 +404,7 @@
 void tw_disable_interrupts(TW_Device_Extension *tw_dev);
 int tw_empty_response_que(TW_Device_Extension *tw_dev);
 void tw_enable_interrupts(TW_Device_Extension *tw_dev);
+void tw_enable_and_clear_interrupts(TW_Device_Extension *tw_dev);
 int tw_findcards(Scsi_Host_Template *tw_host);
 void tw_free_device_extension(TW_Device_Extension *tw_dev);
 int tw_initconnection(TW_Device_Extension *tw_dev, int message_credits);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/scsi/53c700.c linux/drivers/scsi/53c700.c
--- linux.orig/drivers/scsi/53c700.c	Thu Oct 11 16:43:29 2001
+++ linux/drivers/scsi/53c700.c	Mon Jan  7 15:35:16 2002
@@ -51,6 +51,14 @@
 
 /* CHANGELOG
  *
+ * Version 2.7
+ *
+ * Fixed scripts problem which caused certain devices (notably CDRWs)
+ * to hang on initial INQUIRY.  Updated NCR_700_readl/writel to use
+ * __raw_readl/writel for parisc compatibility (Thomas
+ * Bogendoerfer). Added missing SCp->request_bufflen initialisation
+ * for sense requests (Ryan Bradetich).
+ *
  * Version 2.6
  *
  * Following test of the 64 bit parisc kernel by Richard Hirst,
@@ -96,7 +104,7 @@
  * Initial modularisation from the D700.  See NCR_D700.c for the rest of
  * the changelog.
  * */
-#define NCR_700_VERSION "2.6"
+#define NCR_700_VERSION "2.7"
 
 #include <linux/config.h>
 #include <linux/version.h>
@@ -1049,6 +1057,7 @@
 						    slot->pCmd,
 						    SCp->cmd_len,
 						    PCI_DMA_TODEVICE);
+				SCp->request_bufflen = sizeof(SCp->sense_buffer);
 				slot->dma_handle = pci_map_single(hostdata->pci_dev, SCp->sense_buffer, sizeof(SCp->sense_buffer), PCI_DMA_FROMDEVICE);
 				slot->SG[0].ins = bS_to_host(SCRIPT_MOVE_DATA_IN | sizeof(SCp->sense_buffer));
 				slot->SG[0].pAddr = bS_to_host(slot->dma_handle);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/scsi/53c700.h linux/drivers/scsi/53c700.h
--- linux.orig/drivers/scsi/53c700.h	Thu Oct 11 16:43:29 2001
+++ linux/drivers/scsi/53c700.h	Mon Jan  7 15:35:16 2002
@@ -210,7 +210,7 @@
 struct NCR_700_Host_Parameters {
 	/* These must be filled in by the calling driver */
 	int	clock;			/* board clock speed in MHz */
-	__u32	base;			/* the base for the port (copied to host) */
+	unsigned long	base;		/* the base for the port (copied to host) */
 	struct pci_dev	*pci_dev;
 	__u32	dmode_extra;	/* adjustable bus settings */
 	__u32	differential:1;	/* if we are differential */
@@ -503,7 +503,7 @@
 static inline __u32
 NCR_700_readl(struct Scsi_Host *host, __u32 reg)
 {
-	__u32 value = readl(host->base + reg);
+	__u32 value = __raw_readl(host->base + reg);
 	const struct NCR_700_Host_Parameters *hostdata __attribute__((unused))
 		= (struct NCR_700_Host_Parameters *)host->hostdata[0];
 #if 1
@@ -536,7 +536,7 @@
 		BUG();
 #endif
 
-	writel(bS_to_host(value), host->base + reg);
+	__raw_writel(bS_to_host(value), host->base + reg);
 }
 #elif defined(CONFIG_53C700_IO_MAPPED)
 static inline __u8
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/scsi/53c700.scr linux/drivers/scsi/53c700.scr
--- linux.orig/drivers/scsi/53c700.scr	Sun Sep 30 19:26:08 2001
+++ linux/drivers/scsi/53c700.scr	Mon Jan  7 15:35:16 2002
@@ -242,7 +242,7 @@
 
 SendIdentifyMsg:
 	CALL	SendMessage
-	JUMP	SendCommand
+	CLEAR	ATN
 
 IgnoreMsgBeforeCommand:
 	CLEAR	ACK
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/scsi/NCR_D700.c linux/drivers/scsi/NCR_D700.c
--- linux.orig/drivers/scsi/NCR_D700.c	Sun Sep  9 18:58:36 2001
+++ linux/drivers/scsi/NCR_D700.c	Mon Jan  7 15:35:16 2002
@@ -36,6 +36,10 @@
 
 /* CHANGELOG 
  *
+ * Version 2.2
+ *
+ * Added mca_set_adapter_name().
+ *
  * Version 2.1
  *
  * Modularise the driver into a Board piece (this file) and a chip
@@ -86,7 +90,7 @@
  * disconnections and reselections are being processed correctly.
  * */
 
-#define NCR_D700_VERSION "2.1"
+#define NCR_D700_VERSION "2.2"
 
 #include <linux/config.h>
 #include <linux/version.h>
@@ -299,6 +303,7 @@
 				continue;
 			}
 			found++;
+			mca_set_adapter_name(slot, "NCR D700 SCSI Adapter (version " NCR_D700_VERSION ")");
 		}
 	}
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/scsi/aic7xxx/aic7xxx_linux.c linux/drivers/scsi/aic7xxx/aic7xxx_linux.c
--- linux.orig/drivers/scsi/aic7xxx/aic7xxx_linux.c	Wed Nov 21 22:05:29 2001
+++ linux/drivers/scsi/aic7xxx/aic7xxx_linux.c	Mon Jan  7 18:55:02 2002
@@ -1703,6 +1703,7 @@
 			       cmd->request_buffer,
 			       cmd->request_bufflen,
 			       scsi_to_pci_dma_dir(cmd->sc_data_direction));
+			scb->sg_count = 0;
 			scb->sg_count = ahc_linux_map_seg(ahc, scb,
 							  sg, addr,
 							  cmd->request_bufflen);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/scsi/lasi700.c linux/drivers/scsi/lasi700.c
--- linux.orig/drivers/scsi/lasi700.c	Sun Sep 30 19:26:08 2001
+++ linux/drivers/scsi/lasi700.c	Mon Jan  7 15:35:16 2002
@@ -136,7 +136,6 @@
 lasi700_driver_callback(struct parisc_device *dev)
 {
 	unsigned long base = dev->hpa + LASI_SCSI_CORE_OFFSET;
-	int irq = busdevice_alloc_irq(dev);
 	char *driver_name;
 	struct Scsi_Host *host;
 	struct NCR_700_Host_Parameters *hostdata =
@@ -170,14 +169,15 @@
 		hostdata->chip710 = 1;
 		hostdata->dmode_extra = DMODE_FC2;
 	}
+	hostdata->pci_dev = ccio_get_fake(dev);
 	if((host = NCR_700_detect(host_tpnt, hostdata)) == NULL) {
 		kfree(hostdata);
 		release_mem_region(host->base, 64);
 		return 1;
 	}
-	host->irq = irq;
-	if(request_irq(irq, NCR_700_intr, SA_SHIRQ, driver_name, host)) {
-		printk(KERN_ERR "%s: irq problem, detatching\n",
+	host->irq = dev->irq;
+	if(request_irq(dev->irq, NCR_700_intr, SA_SHIRQ, driver_name, host)) {
+		printk(KERN_ERR "%s: irq problem, detaching\n",
 		       driver_name);
 		scsi_unregister(host);
 		NCR_700_release(host);
@@ -197,6 +197,7 @@
 	kfree(hostdata);
 	free_irq(host->irq, host);
 	release_mem_region(host->base, 64);
+	unregister_parisc_driver(&lasi700_driver);
 	return 1;
 }
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/scsi/mac_scsi.c linux/drivers/scsi/mac_scsi.c
--- linux.orig/drivers/scsi/mac_scsi.c	Sun Nov 12 03:01:11 2000
+++ linux/drivers/scsi/mac_scsi.c	Mon Jan  7 17:36:23 2002
@@ -353,7 +353,7 @@
 	NCR5380_write( INITIATOR_COMMAND_REG, ICR_BASE );
 	NCR5380_read( RESET_PARITY_INTERRUPT_REG );
 
-	for( end = jiffies + AFTER_RESET_DELAY; jiffies < end; )
+	for( end = jiffies + AFTER_RESET_DELAY; time_before(jiffies, end); )
 		barrier();
 
 	/* switch on SCSI IRQ again */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/scsi/scsi_debug.c linux/drivers/scsi/scsi_debug.c
--- linux.orig/drivers/scsi/scsi_debug.c	Mon Jan  7 23:41:09 2002
+++ linux/drivers/scsi/scsi_debug.c	Wed Dec 26 17:05:14 2001
@@ -3,8 +3,18 @@
  *
  *  Copyright (C) 1992  Eric Youngdale
  *  Simulate a host adapter with 2 disks attached.  Do a lot of checking
- *  to make sure that we are not getting blocks mixed up, and panic if
+ *  to make sure that we are not getting blocks mixed up, and PANIC if
  *  anything out of the ordinary is seen.
+ *
+ *  This version is more generic, simulating a variable number of disk 
+ *  (or disk like devices) sharing a common amount of RAM (default 8 MB
+ *  but can be set at driver/module load time).
+ *
+ *  For documentation see http://www.torque.net/sg/sdebug.html
+ *
+ *   D. Gilbert (dpg) work for MOD device test [20010421]
+ *   dpg, work for devfs large number of disks [20010809]
+ *   dpg, make more generic [20011123]
  */
 
 #include <linux/config.h>
@@ -18,7 +28,9 @@
 #include <linux/string.h>
 #include <linux/genhd.h>
 #include <linux/fs.h>
+#include <linux/init.h>
 #include <linux/proc_fs.h>
+#include <linux/smp_lock.h>
 
 #include <asm/system.h>
 #include <asm/io.h>
@@ -27,151 +39,162 @@
 #include "scsi.h"
 #include "hosts.h"
 
-#include "sd.h"
-
 #include<linux/stat.h>
 
-/* A few options that we want selected */
+#ifndef LINUX_VERSION_CODE
+#include <linux/version.h>
+#endif
+
+static char scsi_debug_version_str[] = "Version: 0.57 (20011209)";
 
-#define NR_HOSTS_PRESENT 1
-#define NR_FAKE_DISKS   3
-#define N_HEAD          255
-#define N_SECTOR        63
-#define N_CYLINDER      524
+#ifndef SCSI_CMD_READ_16
+#define SCSI_CMD_READ_16 0x88
+#endif
+#ifndef SCSI_CMD_WRITE_16
+#define SCSI_CMD_WRITE_16 0x8a
+#endif
+
+/* A few options that we want selected */
+#define DEF_NR_FAKE_DEVS   1
+#define DEF_DEV_SIZE_MB   8
+#define DEF_FAKE_BLK0   0
+
+static int scsi_debug_num_devs = DEF_NR_FAKE_DEVS;
+
+#define NR_HOSTS_PRESENT (((scsi_debug_num_devs - 1) / 7) + 1)
+#define N_HEAD          8
+#define N_SECTOR        32
 #define DISK_READONLY(TGT)      (0)
-#define DISK_REMOVEABLE(TGT)    (1)
-#define DEVICE_TYPE(TGT) (TGT == 2 ? TYPE_TAPE : TYPE_DISK);
+#define DISK_REMOVEABLE(TGT)    (0)
+#define DEVICE_TYPE(TGT) (TYPE_DISK);
+
+#define SCSI_DEBUG_MAILBOXES (scsi_debug_num_devs + 1)
+
+static int scsi_debug_dev_size_mb = DEF_DEV_SIZE_MB;
+#define STORE_SIZE (scsi_debug_dev_size_mb * 1024 * 1024)
+#define STORE_ELEM_ORDER 1
+#define STORE_ELEM_SIZE (PAGE_SIZE * (1 << STORE_ELEM_ORDER))
+#define STORE_ELEMENTS ((STORE_SIZE / STORE_ELEM_SIZE) + 1)
+
+/* default sector size is 512 bytes, 2**9 bytes */
+#define POW2_SECT_SIZE 9
+#define SECT_SIZE (1 << POW2_SECT_SIZE)
+
+#define N_CYLINDER (STORE_SIZE / (SECT_SIZE * N_SECTOR * N_HEAD))
+
+static int scsi_debug_fake_blk0 = DEF_FAKE_BLK0;
 
 /* Do not attempt to use a timer to simulate a real disk with latency */
 /* Only use this in the actual kernel, not in the simulator. */
 #define IMMEDIATE
 
-/* Skip some consistency checking.  Good for benchmarking */
-#define SPEEDY
-/* Read return zeros. Undefine for benchmarking */
-#define CLEAR
-
-/* Number of real scsi disks that will be detected ahead of time */
-static int NR_REAL = -1;
-
-#define NR_BLK_DEV  12
-#ifndef MAJOR_NR
-#define MAJOR_NR 8
-#endif
 #define START_PARTITION 4
 
 /* Time to wait before completing a command */
 #define DISK_SPEED     (HZ/10)	/* 100ms */
 #define CAPACITY (N_HEAD * N_SECTOR * N_CYLINDER)
-#define SIZE(TGT) (TGT == 2 ? 2248 : 512)
+#define SECT_SIZE_PER(TGT) SECT_SIZE
+#define SECT_PER_ELEM (STORE_ELEM_SIZE / SECT_SIZE)
 
 static int starts[] =
 {N_SECTOR,
  N_HEAD * N_SECTOR,		/* Single cylinder */
  N_HEAD * N_SECTOR * 4,
- CAPACITY, 0};
+ 0 /* CAPACITY */, 0};
 static int npart = 0;
 
-#include "scsi_debug.h"
-#ifdef DEBUG
-#define DEB(x) x
-#else
-#define DEB(x)
-#endif
+typedef struct scsi_debug_store_elem {
+	unsigned char * p;
+} Sd_store_elem;
+
+static Sd_store_elem * store_arr = 0;
+
+typedef struct sdebug_dev_info {
+	Scsi_Device * sdp;
+	unsigned short host_no;
+	unsigned short id;
+	char reset;
+	char sb_index;
+} Sdebug_dev_info;
+static Sdebug_dev_info * devInfop;
+
+static int num_aborts = 0;
+static int num_dev_resets = 0;
+static int num_bus_resets = 0;
+static int num_host_resets = 0;
 
-#ifdef SPEEDY
-#define VERIFY1_DEBUG(RW)
-#define VERIFY_DEBUG(RW)
-#else
+static spinlock_t mailbox_lock = SPIN_LOCK_UNLOCKED;
+static rwlock_t sdebug_atomic_rw = RW_LOCK_UNLOCKED;
 
-#define VERIFY1_DEBUG(RW)                           \
-    if (bufflen != 1024) {printk("%d", bufflen); panic("(1)Bad bufflen");};         \
-    start = 0;                          \
-    if ((MINOR(SCpnt->request.rq_dev) & 0xf) != 0) start = starts[(MINOR(SCpnt->request.rq_dev) & 0xf) - 1];        \
-    if (bh){                            \
-	if (bh->b_size != 1024) panic ("Wrong bh size");    \
-	if ((bh->b_blocknr << 1) + start != block)          \
-	{   printk("Wrong bh block# %d %d ",bh->b_blocknr, block);  \
-	    panic ("Wrong bh block#"); \
-	};  \
-	if (bh->b_dev != SCpnt->request.rq_dev)  \
-	    panic ("Bad bh target"); \
-    };
-
-#define VERIFY_DEBUG(RW)                            \
-    if (bufflen != 1024 && (!SCpnt->use_sg)) {printk("%x %d\n ",bufflen, SCpnt->use_sg); panic("Bad bufflen");};    \
-    start = 0;                          \
-    if ((MINOR(SCpnt->request.rq_dev) & 0xf) > npart) panic ("Bad partition");    \
-    if ((MINOR(SCpnt->request.rq_dev) & 0xf) != 0) start = starts[(MINOR(SCpnt->request.rq_dev) & 0xf) - 1];        \
-    if (SCpnt->request.cmd != RW) panic ("Wrong  operation");       \
-    if (SCpnt->request.sector + start != block) panic("Wrong block.");  \
-    if (SCpnt->request.current_nr_sectors != 2 && (!SCpnt->use_sg)) panic ("Wrong # blocks");   \
-    if (SCpnt->request.bh){                         \
-	if (SCpnt->request.bh->b_size != 1024) panic ("Wrong bh size"); \
-	if ((SCpnt->request.bh->b_blocknr << 1) + start != block)           \
-	{   printk("Wrong bh block# %d %d ",SCpnt->request.bh->b_blocknr, block);  \
-	    panic ("Wrong bh block#"); \
-	};  \
-	if (SCpnt->request.bh->b_dev != SCpnt->request.rq_dev) \
-	    panic ("Bad bh target");\
-    };
-#endif
+#include "scsi_debug.h"
 
 typedef void (*done_fct_t) (Scsi_Cmnd *);
 
-static volatile done_fct_t do_done[SCSI_DEBUG_MAILBOXES] =
-{NULL,};
+static volatile done_fct_t * do_done = 0;
 
 struct Scsi_Host * SHpnt = NULL;
 
+static int scsi_debug_read(Scsi_Cmnd * SCpnt, int upper_blk, int block, 
+			   int num, int * errstsp, Sdebug_dev_info * devip);
+static int scsi_debug_write(Scsi_Cmnd * SCpnt, int upper_blk, int block, 
+			    int num, int * errstsp, Sdebug_dev_info * devip);
 static void scsi_debug_send_self_command(struct Scsi_Host * shpnt);
 static void scsi_debug_intr_handle(unsigned long);
+static Sdebug_dev_info * devInfoReg(Scsi_Device * sdp);
+static void mk_sense_buffer(Sdebug_dev_info * devip, int index, int key, 
+			    int asc, int asq, int inbandLen);
+static int check_reset(Scsi_Cmnd * SCpnt, Sdebug_dev_info * devip);
 
-static struct timer_list timeout[SCSI_DEBUG_MAILBOXES];
-
-Scsi_Cmnd *SCint[SCSI_DEBUG_MAILBOXES] =
-{NULL,};
-static char SCrst[SCSI_DEBUG_MAILBOXES] =
-{0,};
+static struct timer_list * timeout = 0;
+static Scsi_Cmnd ** SCint = 0;
 
 /*
  * Semaphore used to simulate bus lockups.
  */
 static int scsi_debug_lockup = 0;
 
-static char sense_buffer[128] =
-{0,};
+#define NUM_SENSE_BUFFS 4
+#define SENSE_BUFF_LEN 32
+static char sense_buffers[NUM_SENSE_BUFFS][SENSE_BUFF_LEN];
 
+#ifdef SCSI_DUMP
 static void scsi_dump(Scsi_Cmnd * SCpnt, int flag)
 {
 	int i;
-#if 0
-	unsigned char *pnt;
-#endif
 	unsigned int *lpnt;
 	struct scatterlist *sgpnt = NULL;
 	printk("use_sg: %d", SCpnt->use_sg);
 	if (SCpnt->use_sg) {
 		sgpnt = (struct scatterlist *) SCpnt->buffer;
 		for (i = 0; i < SCpnt->use_sg; i++) {
-			printk(":%p %d\n", sgpnt[i].address, sgpnt[i].length);
-		};
+			lpnt = (int *) sgpnt[i].alt_address;
+			printk(":%p %p %d\n", sgpnt[i].alt_address, 
+			       sgpnt[i].address, sgpnt[i].length);
+			if (lpnt)
+				printk(" (Alt %x) ", lpnt[15]);
+		}
 	} else {
 		printk("nosg: %p %p %d\n", SCpnt->request.buffer, SCpnt->buffer,
 		       SCpnt->bufflen);
 		lpnt = (int *) SCpnt->request.buffer;
 		if (lpnt)
 			printk(" (Alt %x) ", lpnt[15]);
-	};
+	}
 	lpnt = (unsigned int *) SCpnt;
 	for (i = 0; i < sizeof(Scsi_Cmnd) / 4 + 1; i++) {
 		if ((i & 7) == 0)
 			printk("\n");
 		printk("%x ", *lpnt++);
-	};
+	}
 	printk("\n");
 	if (flag == 0)
 		return;
+	lpnt = (unsigned int *) sgpnt[0].alt_address;
+	for (i = 0; i < sizeof(Scsi_Cmnd) / 4 + 1; i++) {
+		if ((i & 7) == 0)
+			printk("\n");
+		printk("%x ", *lpnt++);
+	}
 #if 0
 	printk("\n");
 	lpnt = (unsigned int *) sgpnt[0].address;
@@ -179,50 +202,85 @@
 		if ((i & 7) == 0)
 			printk("\n");
 		printk("%x ", *lpnt++);
-	};
+	}
 	printk("\n");
 #endif
 	printk("DMA free %d sectors.\n", scsi_dma_free_sectors);
 }
+#endif
+
+static int made_block0 = 0;
+
+static void scsi_debug_mkblock0(unsigned char * buff, int bufflen,
+				Scsi_Cmnd * SCpnt)
+{
+	int i;
+	struct partition *p;
+
+	memset(buff, 0, bufflen);
+	*((unsigned short *) (buff + 510)) = 0xAA55;
+	p = (struct partition *) (buff + 0x1be);
+	i = 0;
+	while (starts[i + 1]) {
+		int start_cyl, end_cyl;
+
+		start_cyl = starts[i] / N_HEAD / N_SECTOR;
+		end_cyl = (starts[i + 1] - 1) / N_HEAD / N_SECTOR;
+		p->boot_ind = 0;
+
+		p->head = (i == 0 ? 1 : 0);
+		p->sector = 1 | ((start_cyl >> 8) << 6);
+		p->cyl = (start_cyl & 0xff);
+
+		p->end_head = N_HEAD - 1;
+		p->end_sector = N_SECTOR | ((end_cyl >> 8) << 6);
+		p->end_cyl = (end_cyl & 0xff);
+
+		p->start_sect = starts[i];
+		p->nr_sects = starts[i + 1] - starts[i];
+		p->sys_ind = 0x83;	/* Linux ext2 partition */
+		p++;
+		i++;
+	}
+	if (!npart)
+		npart = i;
+	made_block0 = 1;
+	i = (bufflen > STORE_ELEM_SIZE) ? STORE_ELEM_SIZE : bufflen;
+	memcpy(store_arr[0].p, buff, i);
+}
 
 int scsi_debug_queuecommand(Scsi_Cmnd * SCpnt, void (*done) (Scsi_Cmnd *))
 {
 	unchar *cmd = (unchar *) SCpnt->cmnd;
-	struct partition *p;
 	int block;
-	struct buffer_head *bh = NULL;
+	int upper_blk;
 	unsigned char *buff;
-	int nbytes, sgcount;
 	int scsi_debug_errsts;
-	struct scatterlist *sgpnt;
 	int target = SCpnt->target;
 	int bufflen = SCpnt->request_bufflen;
-	unsigned long flags;
-	int i;
-	sgcount = 0;
-	sgpnt = NULL;
+	unsigned long iflags;
+	int i, num, capac;
+	Sdebug_dev_info * devip = NULL;
+	char * sbuff;
 
 #ifdef CONFIG_SMP
         /*
          * The io_request_lock *must* be held at this point.
          */
-        if( io_request_lock.lock == 0 )
+        if(! spin_is_locked(&io_request_lock))
         {
-                printk("Warning - io_request_lock is not held in queuecommand\n");
+                printk("Warning - io_request_lock is not held in "
+		       "queuecommand\n");
         }
 #endif
 
 	/*
-	 * If we are being notified of the mid-level reposessing a command due to timeout,
-	 * just return.
+	 * If we are being notified of the mid-level reposessing a command
+	 * due to timeout, just return.
 	 */
 	if (done == NULL) {
 		return 0;
 	}
-	DEB(if (target >= NR_FAKE_DISKS) {
-	    SCpnt->result = DID_TIME_OUT << 16; done(SCpnt); return 0;
-	    }
-	);
 
 	buff = (unsigned char *) SCpnt->request_buffer;
 
@@ -230,249 +288,201 @@
          * If a command comes for the ID of the host itself, just print
          * a silly message and return.
          */
-        if( target == 7 ) {
+        if(target == 7) {
                 printk("How do you do!\n");
                 SCpnt->result = 0;
                 done(SCpnt);
                 return 0;
         }
 
-	if (target >= NR_FAKE_DISKS || SCpnt->lun != 0) {
+	if ((target > 7) || (SCpnt->lun != 0)) {
 		SCpnt->result = DID_NO_CONNECT << 16;
 		done(SCpnt);
 		return 0;
 	}
-	if (SCrst[target] != 0 && !scsi_debug_lockup) {
-		SCrst[target] = 0;
-		memset(SCpnt->sense_buffer, 0, sizeof(SCpnt->sense_buffer));
-		SCpnt->sense_buffer[0] = 0x70;
-		SCpnt->sense_buffer[2] = UNIT_ATTENTION;
-		SCpnt->result = (CHECK_CONDITION << 1);
-		done(SCpnt);
+#if 0
+	printk(KERN_INFO "sdebug:qc: host_no=%d, id=%d, sdp=%p, cmd=0x%x\n",
+	       (int)SCpnt->device->host->host_no, (int)SCpnt->device->id,
+	       SCpnt->device, (int)(unsigned char)*cmd);
+#endif
+	if (NULL == SCpnt->device->hostdata) {
+		devip = devInfoReg(SCpnt->device);
+		if (NULL == devip) {
+			SCpnt->result = DID_NO_CONNECT << 16;
+			done(SCpnt);
+			return 0;
+		}
+		SCpnt->device->hostdata = devip;
 	}
+	devip = SCpnt->device->hostdata;
+
 	switch (*cmd) {
-	case REQUEST_SENSE:
+	case REQUEST_SENSE:	/* mandatory */
 		SCSI_LOG_LLQUEUE(3, printk("Request sense...\n"));
-#ifndef DEBUG
-		{
-			int i;
-			printk("scsi_debug: Requesting sense buffer (%p %p %p %d):", SCpnt, buff, done, bufflen);
-			for (i = 0; i < 12; i++)
-				printk("%d ", sense_buffer[i]);
-			printk("\n");
-		};
-#endif
-		memset(buff, 0, bufflen);
-		memcpy(buff, sense_buffer, bufflen);
-		memset(sense_buffer, 0, sizeof(sense_buffer));
+		if (devip) {
+			sbuff = &sense_buffers[(int)devip->sb_index][0];
+			devip->sb_index = 0; 
+		}
+		else
+		    sbuff = &sense_buffers[0][0]; 
+		memcpy(buff, sbuff, (bufflen < SENSE_BUFF_LEN) ? 
+				     bufflen : SENSE_BUFF_LEN);
+		memset(sbuff, 0, SENSE_BUFF_LEN);
+		sbuff[0] = 0x70;
 		SCpnt->result = 0;
 		done(SCpnt);
 		return 0;
 	case START_STOP:
+		if (check_reset(SCpnt, devip)) {
+			done(SCpnt);
+			return 0;
+		}
 		SCSI_LOG_LLQUEUE(3, printk("START_STOP\n"));
 		scsi_debug_errsts = 0;
 		break;
 	case ALLOW_MEDIUM_REMOVAL:
+		if (check_reset(SCpnt, devip)) {
+			done(SCpnt);
+			return 0;
+		}
 		if (cmd[4]) {
-			SCSI_LOG_LLQUEUE(2, printk("Medium removal inhibited..."));
+			SCSI_LOG_LLQUEUE(2, printk(
+					"Medium removal inhibited..."));
 		} else {
-			SCSI_LOG_LLQUEUE(2, printk("Medium removal enabled..."));
+			SCSI_LOG_LLQUEUE(2, 
+					printk("Medium removal enabled..."));
 		}
 		scsi_debug_errsts = 0;
 		break;
-	case INQUIRY:
+	case INQUIRY:     /* mandatory */
 		SCSI_LOG_LLQUEUE(3, printk("Inquiry...(%p %d)\n", buff, bufflen));
 		memset(buff, 0, bufflen);
 		buff[0] = DEVICE_TYPE(target);
-		buff[1] = DISK_REMOVEABLE(target) ? 0x80 : 0;	/* Removable disk */
-		buff[2] = 1;
-		buff[4] = 33 - 5;
-		memcpy(&buff[8], "Foo Inc", 7);
-		memcpy(&buff[16], "XYZZY", 5);
-		memcpy(&buff[32], "1", 1);
+		buff[1] = DISK_REMOVEABLE(target) ? 0x80 : 0;	
+				/* Removable disk */
+		buff[2] = 2;	/* claim SCSI 2 */
+		buff[4] = 36 - 5;
+		memcpy(&buff[8], "Linux   ", 8);
+		memcpy(&buff[16], "scsi_debug      ", 16);
+		memcpy(&buff[32], "0002", 4);
+		scsi_debug_errsts = 0;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,10)
+		if (SCpnt->host->max_cmd_len != SCSI_DEBUG_MAX_CMD_LEN)
+			SCpnt->host->max_cmd_len = SCSI_DEBUG_MAX_CMD_LEN;
+#endif
+		break;
+	case SEND_DIAGNOSTIC:     /* mandatory */
+		SCSI_LOG_LLQUEUE(3, printk("Send Diagnostic\n"));
+		if (buff)
+			memset(buff, 0, bufflen);
 		scsi_debug_errsts = 0;
 		break;
-	case TEST_UNIT_READY:
+	case TEST_UNIT_READY:     /* mandatory */
 		SCSI_LOG_LLQUEUE(3, printk("Test unit ready(%p %d)\n", buff, bufflen));
 		if (buff)
 			memset(buff, 0, bufflen);
 		scsi_debug_errsts = 0;
 		break;
 	case READ_CAPACITY:
+		if (check_reset(SCpnt, devip)) {
+			done(SCpnt);
+			return 0;
+		}
 		SCSI_LOG_LLQUEUE(3, printk("Read Capacity\n"));
                 SHpnt = SCpnt->host;
-		if (NR_REAL < 0)
-			NR_REAL = (MINOR(SCpnt->request.rq_dev) >> 4) & 0x0f;
 		memset(buff, 0, bufflen);
-		buff[0] = (CAPACITY >> 24);
-		buff[1] = (CAPACITY >> 16) & 0xff;
-		buff[2] = (CAPACITY >> 8) & 0xff;
-		buff[3] = CAPACITY & 0xff;
+		capac = CAPACITY - 1;
+		buff[0] = (capac >> 24);
+		buff[1] = (capac >> 16) & 0xff;
+		buff[2] = (capac >> 8) & 0xff;
+		buff[3] = capac & 0xff;
 		buff[4] = 0;
 		buff[5] = 0;
-		buff[6] = (SIZE(target) >> 8) & 0xff;	/* 512 byte sectors */
-		buff[7] = SIZE(target) & 0xff;
+		buff[6] = (SECT_SIZE_PER(target) >> 8) & 0xff;
+		buff[7] = SECT_SIZE_PER(target) & 0xff;
 
 		scsi_debug_errsts = 0;
 		break;
+	case SCSI_CMD_READ_16:	/* SBC-2 */
+	case READ_12:
 	case READ_10:
 	case READ_6:
-#ifdef DEBUG
-		printk("Read...");
-#endif
-		if ((*cmd) == READ_10)
-			block = cmd[5] + (cmd[4] << 8) + (cmd[3] << 16) + (cmd[2] << 24);
-		else
-			block = cmd[3] + (cmd[2] << 8) + ((cmd[1] & 0x1f) << 16);
-		VERIFY_DEBUG(READ);
-#if defined(SCSI_SETUP_LATENCY) || defined(SCSI_DATARATE)
-		{
-			int delay = SCSI_SETUP_LATENCY;
-
-			delay += SCpnt->request.nr_sectors * SCSI_DATARATE;
-			if (delay)
-				usleep(delay);
-		};
-#endif
-
-#ifdef DEBUG
-		printk("(r%d)", SCpnt->request.nr_sectors);
-#endif
-		nbytes = bufflen;
-		if (SCpnt->use_sg) {
-			sgcount = 0;
-			sgpnt = (struct scatterlist *) buff;
-			buff = sgpnt[sgcount].address;
-			bufflen = sgpnt[sgcount].length;
-			bh = SCpnt->request.bh;
-		};
-		scsi_debug_errsts = 0;
-		do {
-			VERIFY1_DEBUG(READ);
-			/* For the speedy test, we do not even want to fill the buffer with anything */
-#ifdef CLEAR
-			memset(buff, 0, bufflen);
-#endif
-			/* If this is block 0, then we want to read the partition table for this
-			 * device.  Let's make one up */
-			if (block == 0) {
-				int i;
-				memset(buff, 0, bufflen);
-				*((unsigned short *) (buff + 510)) = 0xAA55;
-				p = (struct partition *) (buff + 0x1be);
-				i = 0;
-				while (starts[i + 1]) {
-					int start_cyl, end_cyl;
-
-					start_cyl = starts[i] / N_HEAD / N_SECTOR;
-					end_cyl = (starts[i + 1] - 1) / N_HEAD / N_SECTOR;
-					p->boot_ind = 0;
-
-					p->head = (i == 0 ? 1 : 0);
-					p->sector = 1 | ((start_cyl >> 8) << 6);
-					p->cyl = (start_cyl & 0xff);
-
-					p->end_head = N_HEAD - 1;
-					p->end_sector = N_SECTOR | ((end_cyl >> 8) << 6);
-					p->end_cyl = (end_cyl & 0xff);
-
-					p->start_sect = starts[i];
-					p->nr_sects = starts[i + 1] - starts[i];
-					p->sys_ind = 0x81;	/* Linux partition */
-					p++;
-					i++;
-				};
-				if (!npart)
-					npart = i;
-				scsi_debug_errsts = 0;
-				break;
-			};
-#ifdef DEBUG
-			if (SCpnt->use_sg)
-				printk("Block %x (%d %d)\n", block, SCpnt->request.nr_sectors,
-				       SCpnt->request.current_nr_sectors);
-#endif
-
-#if 0
-			/* Simulate a disk change */
-			if (block == 0xfff0) {
-				sense_buffer[0] = 0x70;
-				sense_buffer[2] = UNIT_ATTENTION;
-				starts[0] += 10;
-				starts[1] += 10;
-				starts[2] += 10;
-
-#ifdef DEBUG
-				{
-					int i;
-					printk("scsi_debug: Filling sense buffer:");
-					for (i = 0; i < 12; i++)
-						printk("%d ", sense_buffer[i]);
-					printk("\n");
-				};
-#endif
-				scsi_debug_errsts = (COMMAND_COMPLETE << 8) | (CHECK_CONDITION << 1);
-				break;
-			}	/* End phony disk change code */
-#endif
-
-#ifdef CLEAR
-			memcpy(buff, &target, sizeof(target));
-			memcpy(buff + sizeof(target), cmd, 24);
-			memcpy(buff + 60, &block, sizeof(block));
-			memcpy(buff + 64, SCpnt, sizeof(Scsi_Cmnd));
-#endif
-			nbytes -= bufflen;
-			if (SCpnt->use_sg) {
-#ifdef CLEAR
-				memcpy(buff + 128, bh, sizeof(struct buffer_head));
-#endif
-				block += bufflen >> 9;
-				bh = bh->b_reqnext;
-				sgcount++;
-				if (nbytes) {
-					if (!bh)
-						panic("Too few blocks for linked request.");
-					buff = sgpnt[sgcount].address;
-					bufflen = sgpnt[sgcount].length;
-				};
-			}
-		} while (nbytes);
-
+		if (check_reset(SCpnt, devip)) {
+			done(SCpnt);
+			return 0;
+		}
+		upper_blk = 0;
+		if ((*cmd) == SCSI_CMD_READ_16) {
+			upper_blk = cmd[5] + (cmd[4] << 8) + 
+				    (cmd[3] << 16) + (cmd[2] << 24);
+			block = cmd[9] + (cmd[8] << 8) + 
+				(cmd[7] << 16) + (cmd[6] << 24);
+			num = cmd[13] + (cmd[12] << 8) + 
+				(cmd[11] << 16) + (cmd[10] << 24);
+		}
+		else if ((*cmd) == READ_12) {
+			block = cmd[5] + (cmd[4] << 8) + 
+				(cmd[3] << 16) + (cmd[2] << 24);
+			num = cmd[9] + (cmd[8] << 8) + 
+				(cmd[7] << 16) + (cmd[6] << 24);
+		}
+		else if ((*cmd) == READ_10) {
+			block = cmd[5] + (cmd[4] << 8) + 
+				(cmd[3] << 16) + (cmd[2] << 24);
+			num = cmd[8] + (cmd[7] << 8);
+		}
+		else {
+			block = cmd[3] + (cmd[2] << 8) + 
+				((cmd[1] & 0x1f) << 16);
+			num = cmd[4];
+		}
+		if (scsi_debug_read(SCpnt, upper_blk, block, num, 
+				    &scsi_debug_errsts, devip))
+			break;
 		SCpnt->result = 0;
-		(done) (SCpnt);
+/* calls bottom half in upper layers before return from scsi_do_...() */
+		(done) (SCpnt);	
 		return 0;
-
-		if (SCpnt->use_sg && !scsi_debug_errsts)
-			if (bh)
-				scsi_dump(SCpnt, 0);
-		break;
+	case SCSI_CMD_WRITE_16:	/* SBC-2 */
+	case WRITE_12:
 	case WRITE_10:
 	case WRITE_6:
-#ifdef DEBUG
-		printk("Write\n");
-#endif
-		if ((*cmd) == WRITE_10)
-			block = cmd[5] + (cmd[4] << 8) + (cmd[3] << 16) + (cmd[2] << 24);
-		else
-			block = cmd[3] + (cmd[2] << 8) + ((cmd[1] & 0x1f) << 16);
-		VERIFY_DEBUG(WRITE);
-		/*      printk("(w%d)",SCpnt->request.nr_sectors); */
-		if (SCpnt->use_sg) {
-			if ((bufflen >> 9) != SCpnt->request.nr_sectors)
-				panic("Trying to write wrong number of blocks\n");
-			sgpnt = (struct scatterlist *) buff;
-			buff = sgpnt[sgcount].address;
-		};
-#if 0
-		if (block != *((unsigned long *) (buff + 60))) {
-			printk("%x %x :", block, *((unsigned long *) (buff + 60)));
-			scsi_dump(SCpnt, 1);
-			panic("Bad block written.\n");
-		};
-#endif
-		scsi_debug_errsts = 0;
-		break;
+		if (check_reset(SCpnt, devip)) {
+			done(SCpnt);
+			return 0;
+		}
+		upper_blk = 0;
+		if ((*cmd) == SCSI_CMD_WRITE_16) {
+			upper_blk = cmd[5] + (cmd[4] << 8) + 
+				    (cmd[3] << 16) + (cmd[2] << 24);
+			block = cmd[9] + (cmd[8] << 8) + 
+				(cmd[7] << 16) + (cmd[6] << 24);
+			num = cmd[13] + (cmd[12] << 8) + 
+				(cmd[11] << 16) + (cmd[10] << 24);
+		}
+		else if ((*cmd) == WRITE_12) {
+			block = cmd[5] + (cmd[4] << 8) + 
+				(cmd[3] << 16) + (cmd[2] << 24);
+			num = cmd[9] + (cmd[8] << 8) + 
+				(cmd[7] << 16) + (cmd[6] << 24);
+		}
+		else if ((*cmd) == WRITE_10) {
+			block = cmd[5] + (cmd[4] << 8) + 
+				(cmd[3] << 16) + (cmd[2] << 24);
+			num = cmd[8] + (cmd[7] << 8);
+		}
+		else {
+			block = cmd[3] + (cmd[2] << 8) + 
+				((cmd[1] & 0x1f) << 16);
+			num = cmd[4];
+		}
+		if (scsi_debug_write(SCpnt, upper_blk, block, num, 
+				     &scsi_debug_errsts, devip))
+			break;
+		SCpnt->result = 0;
+/* calls bottom half in upper layers before return from scsi_do_...() */
+		(done) (SCpnt);	
+		return 0;
 	case MODE_SENSE:
 		/*
 		 * Used to detect write protected status.
@@ -481,26 +491,36 @@
 		memset(buff, 0, 6);
 		break;
 	default:
+#if 0
 		SCSI_LOG_LLQUEUE(3, printk("Unknown command %d\n", *cmd));
 		SCpnt->result = DID_NO_CONNECT << 16;
 		done(SCpnt);
 		return 0;
-	};
+#else
+		if (check_reset(SCpnt, devip)) {
+			done(SCpnt);
+			return 0;
+		}
+		scsi_debug_errsts = (COMMAND_COMPLETE << 8) |
+				    (CHECK_CONDITION << 1);
+		mk_sense_buffer(devip, 2, ILLEGAL_REQUEST, 0x20, 0, 14);
+		break;
+#endif
+	}
 
-	save_flags(flags);
-	cli();
+	spin_lock_irqsave(&mailbox_lock, iflags);
 	for (i = 0; i < SCSI_DEBUG_MAILBOXES; i++) {
 		if (timeout[i].function == NULL)
 			break;
-	};
+	}
 
 	/*
-	 * If all of the slots are full, just return 1.  The new error handling scheme
-	 * allows this, and the mid-level should queue things.
+	 * If all of the slots are full, just return 1.  The new error 
+	 * handling scheme allows this, and the mid-level should queue things.
 	 */
 	if (i >= SCSI_DEBUG_MAILBOXES || timeout[i].function != 0) {
 		SCSI_LOG_LLQUEUE(1, printk("Command rejected - host busy\n"));
-		restore_flags(flags);
+		spin_unlock_irqrestore(&mailbox_lock, iflags);
 		return 1;
 	}
 	SCSI_LOG_LLQUEUE(1, printk("Command accepted - slot %d\n", i));
@@ -512,7 +532,7 @@
 		do_done[i] = done;
 		scsi_debug_intr_handle(i);	/* No timer - do this one right away */
 	}
-	restore_flags(flags);
+	spin_unlock_irqrestore(&mailbox_lock, iflags);
 #else
 
 	SCpnt->result = scsi_debug_errsts;
@@ -522,19 +542,190 @@
 	SCint[i] = SCpnt;
 	do_done[i] = done;
 
-	restore_flags(flags);
+	spin_unlock_irqrestore(&mailbox_lock, iflags);
 	add_timer(&timeout[i]);
 	if (!done)
-		panic("scsi_debug_queuecommand: done can't be NULL\n");
+		printk("scsi_debug_queuecommand: done can't be NULL\n");
 
 #if 0
-	printk("Sending command (%d %x %d %d)...", i, done, timeout[i].expires, jiffies);
+	printk("Sending command (%d %x %d %d)...", i, done, 
+	       timeout[i].expires, jiffies);
 #endif
 #endif
 
 	return 0;
 }
 
+static int check_reset(Scsi_Cmnd * SCpnt, Sdebug_dev_info * devip)
+{
+	if (devip->reset) {
+		devip->reset = 0;
+		mk_sense_buffer(devip, 3, UNIT_ATTENTION, 0x29, 0, 14);
+		SCpnt->result = (COMMAND_COMPLETE << 8) | 
+				(CHECK_CONDITION << 1);
+                return 1;
+	}
+	return 0;
+}
+
+static int scsi_debug_read(Scsi_Cmnd * SCpnt, int upper_blk, int block, 
+			   int num, int * errstsp, Sdebug_dev_info * devip)
+{
+        unsigned char *buff = (unsigned char *) SCpnt->request_buffer;
+        int nbytes, sgcount;
+        struct scatterlist *sgpnt = NULL;
+        int bufflen = SCpnt->request_bufflen;
+	unsigned long iflags;
+
+	if (upper_blk || (block + num > CAPACITY)) {
+		*errstsp = (COMMAND_COMPLETE << 8) |
+			   (CHECK_CONDITION << 1);
+		mk_sense_buffer(devip, 1, ILLEGAL_REQUEST, 0x21, 0, 14);
+		return 1;
+	}
+#if defined(SCSI_SETUP_LATENCY) || defined(SCSI_DATARATE)
+	{
+		int delay = SCSI_SETUP_LATENCY;
+
+		delay += SCpnt->request.nr_sectors * SCSI_DATARATE;
+		if (delay)
+			usleep(delay);
+	}
+#endif
+
+	read_lock_irqsave(&sdebug_atomic_rw, iflags);
+        sgcount = 0;
+	nbytes = bufflen;
+	/* printk("scsi_debug_read: block=%d, tot_bufflen=%d\n", 
+	       block, bufflen); */
+	if (SCpnt->use_sg) {
+		sgcount = 0;
+		sgpnt = (struct scatterlist *) buff;
+		buff = sgpnt[sgcount].address;
+		bufflen = sgpnt[sgcount].length;
+	}
+	*errstsp = 0;
+	do {
+		int resid, k, off, len, rem, blk;
+		unsigned char * bp;
+
+		/* If this is block 0, then we want to read the partition
+		 * table for this device.  Let's make one up */
+		if (scsi_debug_fake_blk0 && (block == 0) && (! made_block0)) {
+			scsi_debug_mkblock0(buff, bufflen, SCpnt);
+			*errstsp = 0;
+			break;
+		}
+		bp = buff;
+		blk = block;
+		for (resid = bufflen; resid > 0; resid -= len) {
+			k = blk / SECT_PER_ELEM; 
+			off = (blk % SECT_PER_ELEM) * SECT_SIZE;
+			rem = STORE_ELEM_SIZE - off;
+			len = (resid > rem) ? rem : resid;
+/* printk("sdr: blk=%d k=%d off=%d rem=%d resid"
+			       "=%d len=%d  sgcount=%d\n", blk, k, 
+				off, rem, resid, len, sgcount); */
+			memcpy(bp, store_arr[k].p + off, len);
+			bp += len;
+			blk += len / SECT_SIZE;
+		}
+#if 0
+		/* Simulate a disk change */
+		if (block == 0xfff0) {
+			sense_buffer[0] = 0x70;
+			sense_buffer[2] = UNIT_ATTENTION;
+			starts[0] += 10;
+			starts[1] += 10;
+			starts[2] += 10;
+
+			*errstsp = (COMMAND_COMPLETE << 8) | 
+				   (CHECK_CONDITION << 1);
+			read_unlock_irqrestore(&sdebug_atomic_rw, iflags);
+			return 1;
+		}	/* End phony disk change code */
+#endif
+		nbytes -= bufflen;
+		if (SCpnt->use_sg) {
+			block += bufflen >> POW2_SECT_SIZE;
+			sgcount++;
+			if (nbytes) {
+				buff = sgpnt[sgcount].address;
+				bufflen = sgpnt[sgcount].length;
+			}
+		}
+		else if (nbytes > 0)
+			printk("sdebug_read: unexpected nbytes=%d\n", nbytes);
+	} while (nbytes);
+	read_unlock_irqrestore(&sdebug_atomic_rw, iflags);
+	return 0;
+}
+
+static int scsi_debug_write(Scsi_Cmnd * SCpnt, int upper_blk, int block, 
+			    int num, int * errstsp, Sdebug_dev_info * devip)
+{
+        unsigned char *buff = (unsigned char *) SCpnt->request_buffer;
+        int nbytes, sgcount;
+        struct scatterlist *sgpnt = NULL;
+        int bufflen = SCpnt->request_bufflen;
+	unsigned long iflags;
+
+	if (upper_blk || (block + num > CAPACITY)) {
+		*errstsp = (COMMAND_COMPLETE << 8) |
+			   (CHECK_CONDITION << 1);
+		mk_sense_buffer(devip, 1, ILLEGAL_REQUEST, 0x21, 0, 14);
+		return 1;
+	}
+
+	write_lock_irqsave(&sdebug_atomic_rw, iflags);
+        sgcount = 0;
+#ifdef SCSI_DUMP
+	if (block != *((unsigned long *) (buff + 60))) {
+		printk("%x %x :", block, *((unsigned long *) (buff + 60)));
+		scsi_dump(SCpnt, 1);
+		printk("Bad block written.\n");
+	}
+#endif
+	nbytes = bufflen;
+	if (SCpnt->use_sg) {
+		sgcount = 0;
+		sgpnt = (struct scatterlist *) buff;
+		buff = sgpnt[sgcount].address;
+		bufflen = sgpnt[sgcount].length;
+	}
+	*errstsp = 0;
+	do {
+		int resid, k, off, len, rem, blk;
+		unsigned char * bp;
+
+		bp = buff;
+		blk = block;
+		for (resid = bufflen; resid > 0; resid -= len) {
+			k = blk / SECT_PER_ELEM; 
+			off = (blk % SECT_PER_ELEM) * SECT_SIZE;
+			rem = STORE_ELEM_SIZE - off;
+			len = (resid > rem) ? rem : resid;
+			memcpy(store_arr[k].p + off, bp, len);
+			bp += len;
+			blk += len / SECT_SIZE;
+		}
+
+		nbytes -= bufflen;
+		if (SCpnt->use_sg) {
+			block += bufflen >> POW2_SECT_SIZE;
+			sgcount++;
+			if (nbytes) {
+				buff = sgpnt[sgcount].address;
+				bufflen = sgpnt[sgcount].length;
+			}
+		}
+		else if (nbytes > 0)
+			printk("sdebug_write: unexpected nbytes=%d\n", nbytes);
+	} while (nbytes);
+	write_unlock_irqrestore(&sdebug_atomic_rw, iflags);
+	return 0;
+}
+
 static void scsi_debug_send_self_command(struct Scsi_Host * shpnt)
 {
 	static unsigned char cmd[6] =
@@ -582,10 +773,6 @@
 {
 	Scsi_Cmnd *SCtmp;
 	void (*my_done) (Scsi_Cmnd *);
-#ifdef DEBUG
-	int to;
-#endif
-
 #if 0
 	del_timer(&timeout[indx]);
 #endif
@@ -600,59 +787,213 @@
 		printk("scsi_debug_intr_handle: Unexpected interrupt\n");
 		return;
 	}
-#ifdef DEBUG
+#if 0
 	printk("In intr_handle...");
 	printk("...done %d %x %d %d\n", i, my_done, to, jiffies);
 	printk("In intr_handle: %d %x %x\n", i, SCtmp, my_done);
 #endif
 
 	my_done(SCtmp);
-#ifdef DEBUG
+#if 0
 	printk("Called done.\n");
 #endif
 }
 
+static int initialized = 0;
+
+static int do_init(void)
+{
+	int sz;
+
+	starts[3] = CAPACITY;
+	sz = sizeof(Sd_store_elem) * STORE_ELEMENTS;
+	store_arr = kmalloc(sz, GFP_ATOMIC);
+	if (NULL == store_arr)
+		return 1;
+	memset(store_arr, 0, sz);
+	sz = sizeof(done_fct_t) * SCSI_DEBUG_MAILBOXES;
+	do_done = kmalloc(sz, GFP_ATOMIC);
+	if (NULL == do_done) {
+		kfree(store_arr);
+		return 1;
+	}
+	memset((void *)do_done, 0, sz);
+	sz = sizeof(struct timer_list) * SCSI_DEBUG_MAILBOXES;
+	timeout = kmalloc(sz, GFP_ATOMIC);
+	if (NULL == timeout) {
+		kfree((void *)do_done);
+		kfree(store_arr);
+		return 1;
+	}
+	memset(timeout, 0, sz);
+	sz = sizeof(Scsi_Cmnd *) * SCSI_DEBUG_MAILBOXES;
+	SCint = kmalloc(sz, GFP_ATOMIC);
+	if (NULL == SCint) {
+		kfree(timeout);
+		kfree((void *)do_done);
+		kfree(store_arr);
+		return 1;
+	}
+	memset(SCint, 0, sz);
+	return 0;
+}
+
+static void do_end(void)
+{
+	kfree(SCint);
+	kfree(timeout);
+	kfree((void *)do_done);
+	kfree(store_arr);
+}
+
 
 int scsi_debug_detect(Scsi_Host_Template * tpnt)
 {
-	int i;
+	int k, num, sz;
+
+	if (0 == initialized) {
+		++initialized;
+		sz = sizeof(Sdebug_dev_info) * scsi_debug_num_devs;
+		devInfop = kmalloc(sz, GFP_ATOMIC);
+		if (NULL == devInfop) {
+			printk("scsi_debug_detect: out of memory, 0.5\n");
+			return 0;
+		}
+		memset(devInfop, 0, sz);
+		if (do_init()) {
+			printk("scsi_debug_detect: out of memory, 0\n");
+			return 0;
+		}
+		for (k = 0; k < STORE_ELEMENTS; ++k) {
+			store_arr[k].p = (unsigned char *)
+				__get_free_pages(GFP_ATOMIC, STORE_ELEM_ORDER);
+			if (0 == store_arr[k].p)
+				goto detect_err;
+			memset(store_arr[k].p, 0, STORE_ELEM_SIZE);
+		}
+		for (k = 0; k < NUM_SENSE_BUFFS; ++k)
+			sense_buffers[k][0] = 0x70;
+		for (k = 0; k < NR_HOSTS_PRESENT; k++) {
+			tpnt->proc_name = "scsi_debug";	/* In the loop??? */
+			scsi_register(tpnt, 0);
+		}
+		return NR_HOSTS_PRESENT;
+	}
+	else {
+		printk("scsi_debug_detect: called again\n");
+		return 0;
+	}
 
-	for (i = 0; i < NR_HOSTS_PRESENT; i++) {
-		tpnt->proc_name = "scsi_debug";	/* Huh? In the loop??? */
-		scsi_register(tpnt, 0);
+detect_err:
+	num = k;
+	for (k = 0; k < STORE_ELEMENTS; ++k) {
+		if (0 != store_arr[k].p) {
+			free_pages((unsigned long)store_arr[k].p, 
+				   STORE_ELEM_ORDER);
+			store_arr[k].p = NULL;
+		}
 	}
-	return NR_HOSTS_PRESENT;
+	printk("scsi_debug_detect: out of memory: %d out of %d bytes\n",
+	       (int)(num * STORE_ELEM_SIZE), 
+	       (int)(scsi_debug_dev_size_mb * 1024 * 1024));
+	return 0;
+}
+
+
+static int num_releases = 0;
+
+int scsi_debug_release(struct Scsi_Host * hpnt)
+{
+	int k;
+	
+	scsi_unregister(hpnt);
+	if (++num_releases != NR_HOSTS_PRESENT)
+		return 0;
+
+	for (k = 0; k < STORE_ELEMENTS; ++k) {
+		if (0 != store_arr[k].p) {
+			free_pages((unsigned long)store_arr[k].p, 
+				   STORE_ELEM_ORDER);
+			store_arr[k].p = NULL;
+		}
+	}
+	do_end();
+	kfree(devInfop);
+	return 0;
+}
+
+static Sdebug_dev_info * devInfoReg(Scsi_Device * sdp)
+{
+	int k;
+	unsigned short host_no, id;
+	Sdebug_dev_info * devip;
+
+	host_no = sdp->host->host_no;
+	id = (unsigned short)sdp->id;
+	for (k = 0; k < scsi_debug_num_devs; ++k) {
+		devip = &devInfop[k];
+		if (devip->sdp && (host_no == devip->host_no) &&
+		    (id == devip->id)) {
+			devip->sdp = sdp; /* overwrite previous sdp */
+			return devip;
+		}
+		if (NULL == devip->sdp) {
+			devip->sdp = sdp;
+			devip->host_no = host_no;
+			devip->id = id;
+			devip->reset = 1;
+			devip->sb_index = 0;
+			return devip;
+		}
+	}
+	return NULL;
+}
+
+static void mk_sense_buffer(Sdebug_dev_info * devip, int index, int key, 
+			    int asc, int asq, int inbandLen)
+{
+	char * sbuff;
+	if ((index < 0) || (index >= NUM_SENSE_BUFFS))
+		return;
+	if (devip)
+		devip->sb_index = index;
+	sbuff = &sense_buffers[index][0];
+	memset(sbuff, 0, SENSE_BUFF_LEN);
+	sbuff[0] = 0x70;
+	sbuff[2] = key;
+	sbuff[7] = (inbandLen > 7) ? (inbandLen - 8) : 0;
+	sbuff[12] = asc;
+	sbuff[13] = asq;
 }
 
 int scsi_debug_abort(Scsi_Cmnd * SCpnt)
 {
-#if 0
+#if 1
+	++num_aborts;
+	return SUCCESS;
+#else
 	int j;
 	void (*my_done) (Scsi_Cmnd *);
-	unsigned long flags;
-#endif
-
-	DEB(printk("scsi_debug_abort\n"));
-#if 0
+	unsigned long iflags;
 	SCpnt->result = SCpnt->abort_reason << 16;
 	for (j = 0; j < SCSI_DEBUG_MAILBOXES; j++) {
 		if (SCpnt == SCint[j]) {
 			my_done = do_done[j];
 			my_done(SCpnt);
-			save_flags(flags);
-			cli();
+			spin_lock_irqsave(&mailbox_lock, iflags);
 			timeout[j] = 0;
 			SCint[j] = NULL;
 			do_done[j] = NULL;
-			restore_flags(flags);
-		};
-	};
-#endif
+			spin_unlock_irqrestore(&mailbox_lock, iflags);
+		}
+	}
 	return SCSI_ABORT_SNOOZE;
+#endif
 }
 
 int scsi_debug_biosparam(Disk * disk, kdev_t dev, int *info)
 {
+	/* int size = disk->capacity; */
 	info[0] = N_HEAD;
 	info[1] = N_SECTOR;
 	info[2] = N_CYLINDER;
@@ -661,35 +1002,130 @@
 	return 0;
 }
 
+#if 0
 int scsi_debug_reset(Scsi_Cmnd * SCpnt, unsigned int why)
 {
 	int i;
-	unsigned long flags;
+	unsigned long iflags;
 
 	void (*my_done) (Scsi_Cmnd *);
 	printk("Bus unlocked by reset - %d\n", why);
 	scsi_debug_lockup = 0;
-	DEB(printk("scsi_debug_reset called\n"));
 	for (i = 0; i < SCSI_DEBUG_MAILBOXES; i++) {
 		if (SCint[i] == NULL)
 			continue;
 		SCint[i]->result = DID_RESET << 16;
 		my_done = do_done[i];
 		my_done(SCint[i]);
-		save_flags(flags);
-		cli();
+		spin_lock_irqsave(&mailbox_lock, iflags);
 		SCint[i] = NULL;
 		do_done[i] = NULL;
 		timeout[i].function = NULL;
-		restore_flags(flags);
+		spin_unlock_irqrestore(&mailbox_lock, iflags);
 	}
 	return SCSI_RESET_SUCCESS;
 }
+#endif
+
+int scsi_debug_device_reset(Scsi_Cmnd * SCpnt)
+{
+	Scsi_Device * sdp;
+	int k;
+
+	++num_dev_resets;
+	if (SCpnt && ((sdp = SCpnt->device))) {
+		for (k = 0; k < scsi_debug_num_devs; ++k) {
+			if (sdp->hostdata == (devInfop + k))
+				break;
+		}
+		if (k < scsi_debug_num_devs)
+			devInfop[k].reset = 1;
+	}
+	return SUCCESS;
+}
 
-const char *scsi_debug_info(void)
+int scsi_debug_bus_reset(Scsi_Cmnd * SCpnt)
 {
-	static char buffer[] = " ";	/* looks nicer without anything here */
-	return buffer;
+	Scsi_Device * sdp;
+	struct Scsi_Host * hp;
+	int k;
+
+	++num_bus_resets;
+	if (SCpnt && ((sdp = SCpnt->device)) && ((hp = sdp->host))) {
+		for (k = 0; k < scsi_debug_num_devs; ++k) {
+			if (hp == devInfop[k].sdp->host)
+				devInfop[k].reset = 1;
+		}
+	}
+	return SUCCESS;
+}
+
+int scsi_debug_host_reset(Scsi_Cmnd * SCpnt)
+{
+	int k;
+
+	++num_host_resets;
+	for (k = 0; k < scsi_debug_num_devs; ++k)
+		devInfop[k].reset = 1;
+
+	return SUCCESS;
+}
+
+#ifndef MODULE
+static int __init scsi_debug_num_devs_setup(char *str)
+{   
+    int tmp; 
+    
+    if (get_option(&str, &tmp) == 1) {
+        if (tmp > 0)
+            scsi_debug_num_devs = tmp;
+        return 1;
+    } else {
+        printk("scsi_debug_num_devs: usage scsi_debug_num_devs=<n> "
+               "(<n> can be from 1 to around 2000)\n");
+        return 0;
+    }
+}
+
+__setup("scsi_debug_num_devs=", scsi_debug_num_devs_setup);
+
+static int __init scsi_debug_dev_size_mb_setup(char *str)
+{   
+    int tmp; 
+    
+    if (get_option(&str, &tmp) == 1) {
+        if (tmp > 0)
+            scsi_debug_dev_size_mb = tmp;
+        return 1;
+    } else {
+        printk("scsi_debug_dev_size_mb: usage scsi_debug_dev_size_mb=<n>\n"
+               "    (<n> is number of MB ram shared by all devs\n");
+        return 0;
+    }
+}
+
+__setup("scsi_debug_dev_size_mb=", scsi_debug_dev_size_mb_setup);
+#endif
+
+MODULE_AUTHOR("Eric Youngdale + Douglas Gilbert");
+MODULE_DESCRIPTION("SCSI debug adapter driver");
+MODULE_PARM(scsi_debug_num_devs, "i");
+MODULE_PARM_DESC(scsi_debug_num_devs, "number of SCSI devices to simulate");
+MODULE_PARM(scsi_debug_dev_size_mb, "i");
+MODULE_PARM_DESC(scsi_debug_dev_size_mb, "size in MB of ram shared by devs");
+
+#ifdef MODULE_LICENSE
+MODULE_LICENSE("GPL");
+#endif
+
+static char sdebug_info[256];
+
+const char * scsi_debug_info(struct Scsi_Host * shp)
+{
+	sprintf(sdebug_info, "scsi_debug, %s, num_devs=%d, "
+		"dev_size_mb=%d\n", scsi_debug_version_str,
+		scsi_debug_num_devs, scsi_debug_dev_size_mb);
+	return sdebug_info;
 }
 
 /* scsi_debug_proc_info
@@ -739,10 +1175,20 @@
 
 	}
 	begin = 0;
-	pos = len = sprintf(buffer,
-	"This driver is not a real scsi driver, but it plays one on TV.\n"
+	pos = len = sprintf(buffer, "scsi_debug adapter driver, %s\n"
+	    "num_devs=%d, shared (ram) size=%d MB, sector_size=%d bytes\n" 
+	    "cylinders=%d, heads=%d, sectors=%d\n"
+	    "number of aborts=%d, device_reset=%d, bus_resets=%d, " 
+	    "host_resets=%d\n",
+	    scsi_debug_version_str, scsi_debug_num_devs, 
+	    scsi_debug_dev_size_mb, SECT_SIZE,
+	    N_CYLINDER, N_HEAD, N_SECTOR,
+	    num_aborts, num_dev_resets, num_bus_resets, num_host_resets);
+#if 0
+	 "This driver is not a real scsi driver, but it plays one on TV.\n"
 	 "It is very handy for debugging specific problems because you\n"
 			 "can simulate a variety of error conditions\n");
+#endif
 	if (pos < offset) {
 		len = 0;
 		begin = pos;
@@ -755,44 +1201,8 @@
 	return (len);
 }
 
-#ifdef CONFIG_USER_DEBUG
-/*
- * This is a hack for the user space emulator.  It allows us to
- * "insert" arbitrary numbers of additional drivers.
- */
-void *scsi_debug_get_handle(void)
-{
-	static Scsi_Host_Template driver_copy = SCSI_DEBUG;
-	void *rtn;
-	rtn = kmalloc(sizeof(driver_copy), GFP_ATOMIC);
-	if(rtn==NULL)
-		return NULL;
-	memcpy(rtn, (void *) &driver_copy, sizeof(driver_copy));
-	return rtn;
-}
-#endif
-
 /* Eventually this will go into an include file, but this will be later */
-static Scsi_Host_Template driver_template = SCSI_DEBUG;
+static Scsi_Host_Template driver_template = SCSI_DEBUG_TEMPLATE;
 
 #include "scsi_module.c"
 
-/*
- * Overrides for Emacs so that we almost follow Linus's tabbing style.
- * Emacs will notice this stuff at the end of the file and automatically
- * adjust the settings for this buffer only.  This must remain at the end
- * of the file.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-indent-level: 4
- * c-brace-imaginary-offset: 0
- * c-brace-offset: -4
- * c-argdecl-indent: 4
- * c-label-offset: -4
- * c-continued-statement-offset: 4
- * c-continued-brace-offset: 0
- * indent-tabs-mode: nil
- * tab-width: 8
- * End:
- */
-MODULE_LICENSE("GPL");
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/scsi/scsi_debug.h linux/drivers/scsi/scsi_debug.h
--- linux.orig/drivers/scsi/scsi_debug.h	Mon Jan  7 23:41:09 2002
+++ linux/drivers/scsi/scsi_debug.h	Wed Dec 26 15:53:59 2001
@@ -8,35 +8,43 @@
 int scsi_debug_queuecommand(Scsi_Cmnd *, void (*done) (Scsi_Cmnd *));
 int scsi_debug_abort(Scsi_Cmnd *);
 int scsi_debug_biosparam(Disk *, kdev_t, int[]);
-int scsi_debug_reset(Scsi_Cmnd *, unsigned int);
+int scsi_debug_bus_reset(Scsi_Cmnd *);
+int scsi_debug_dev_reset(Scsi_Cmnd *);
+int scsi_debug_host_reset(Scsi_Cmnd *);
 int scsi_debug_proc_info(char *, char **, off_t, int, int, int);
+const char * scsi_debug_info(struct Scsi_Host *);
 
 #ifndef NULL
 #define NULL 0
 #endif
 
-
-#define SCSI_DEBUG_MAILBOXES 1
-
 /*
  * Allow the driver to reject commands.  Thus we accept only one, but
  * and the mid-level will queue the remainder.
  */
 #define SCSI_DEBUG_CANQUEUE  255
 
-#define SCSI_DEBUG {proc_info:         scsi_debug_proc_info,	\
+#define SCSI_DEBUG_MAX_CMD_LEN 16
+
+#define SCSI_DEBUG_TEMPLATE \
+		   {proc_info:         scsi_debug_proc_info,	\
 		    name:              "SCSI DEBUG",		\
+		    info:              scsi_debug_info,		\
 		    detect:            scsi_debug_detect,	\
+		    release:           scsi_debug_release,	\
 		    queuecommand:      scsi_debug_queuecommand, \
-		    abort:             scsi_debug_abort,	\
-		    reset:             scsi_debug_reset,	\
+		    eh_abort_handler:  scsi_debug_abort,	\
+		    eh_bus_reset_handler: scsi_debug_bus_reset,	\
+		    eh_device_reset_handler: scsi_debug_device_reset,	\
+		    eh_host_reset_handler: scsi_debug_host_reset,	\
 		    bios_param:        scsi_debug_biosparam,	\
 		    can_queue:         SCSI_DEBUG_CANQUEUE,	\
 		    this_id:           7,			\
-		    sg_tablesize:      16,			\
+		    sg_tablesize:      64,			\
 		    cmd_per_lun:       3,			\
 		    unchecked_isa_dma: 0,			\
 		    use_clustering:    ENABLE_CLUSTERING,	\
 		    use_new_eh_code:   1,			\
 }
+
 #endif
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/scsi/sun3_scsi.c linux/drivers/scsi/sun3_scsi.c
--- linux.orig/drivers/scsi/sun3_scsi.c	Thu Oct 25 20:53:51 2001
+++ linux/drivers/scsi/sun3_scsi.c	Mon Jan  7 17:36:23 2002
@@ -357,7 +357,7 @@
 	NCR5380_write( INITIATOR_COMMAND_REG, ICR_BASE );
 	NCR5380_read( RESET_PARITY_INTERRUPT_REG );
 
-	for( end = jiffies + AFTER_RESET_DELAY; jiffies < end; )
+	for( end = jiffies + AFTER_RESET_DELAY; time_before(jiffies, end); )
 		barrier();
 
 	/* switch on SCSI IRQ again */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/sound/dmasound/Config.in linux/drivers/sound/dmasound/Config.in
--- linux.orig/drivers/sound/dmasound/Config.in	Mon Jan  7 23:41:09 2002
+++ linux/drivers/sound/dmasound/Config.in	Wed Dec 26 16:47:48 2001
@@ -4,7 +4,7 @@
    dep_tristate '  Atari DMA sound support' CONFIG_DMASOUND_ATARI $CONFIG_SOUND
 fi
 if [ "$CONFIG_ALL_PPC" = "y" ]; then
-   dep_tristate '  PowerMac DMA sound support' CONFIG_DMASOUND_AWACS $CONFIG_SOUND
+   dep_tristate '  PowerMac DMA sound support' CONFIG_DMASOUND_PMAC $CONFIG_SOUND
 fi
 if [ "$CONFIG_AMIGA" = "y" -o "$CONFIG_APUS" = "y" ]; then
    dep_tristate '  Amiga DMA sound support' CONFIG_DMASOUND_PAULA $CONFIG_SOUND
@@ -13,15 +13,27 @@
    dep_tristate '  Q40 sound support' CONFIG_DMASOUND_Q40 $CONFIG_SOUND
 fi
 if [ "$CONFIG_DMASOUND_ATARI" = "y" -o \
-     "$CONFIG_DMASOUND_AWACS" = "y" -o \
+     "$CONFIG_DMASOUND_PMAC" = "y" -o \
      "$CONFIG_DMASOUND_PAULA" = "y" -o \
      "$CONFIG_DMASOUND_Q40" = "y" ]; then
    define_tristate CONFIG_DMASOUND y
 else
    if [ "$CONFIG_DMASOUND_ATARI" = "m" -o \
-	"$CONFIG_DMASOUND_AWACS" = "m" -o \
+	"$CONFIG_DMASOUND_PMAC" = "m" -o \
 	"$CONFIG_DMASOUND_PAULA" = "m" -o \
 	"$CONFIG_DMASOUND_Q40" = "m" ]; then
       define_tristate CONFIG_DMASOUND m
    fi
 fi
+
+# the new dmasound_pmac driver needs access to the i2c bus
+if [ "$CONFIG_DMASOUND_PMAC" = "y" ] ; then
+   define_tristate CONFIG_I2C y
+   define_tristate CONFIG_I2C_KEYWEST y
+else
+   if [ "$CONFIG_DMASOUND_PMAC" = "m" ] ; then
+   define_tristate CONFIG_I2C m
+   define_tristate CONFIG_I2C_KEYWEST m
+   fi
+fi
+
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/sound/dmasound/Makefile linux/drivers/sound/dmasound/Makefile
--- linux.orig/drivers/sound/dmasound/Makefile	Mon Jan  7 23:41:09 2002
+++ linux/drivers/sound/dmasound/Makefile	Wed Dec 26 16:47:48 2001
@@ -7,15 +7,21 @@
 #
 # Note 2! The CFLAGS definitions are now in the main makefile...
 
+O_TARGET = dmasound.o
+
 export-objs := dmasound_core.o
 
-obj-$(CONFIG_DMASOUND_ATARI)  += dmasound_core.o dmasound_atari.o
-obj-$(CONFIG_DMASOUND_AWACS)  += dmasound_core.o dmasound_awacs.o
-obj-$(CONFIG_DMASOUND_PAULA)  += dmasound_core.o dmasound_paula.o
-obj-$(CONFIG_DMASOUND_Q40)    += dmasound_core.o dmasound_q40.o
-
-ifeq ($(CONFIG_DMASOUND),y)
-  O_TARGET = dmasound.o
-endif
+list-multi := dmasound_pmac.o
+
+dmasound_pmac-objs := dmasound_awacs.o trans_16.o tas3001c.o dac3550a.o
+
+obj-$(CONFIG_DMASOUND)        += dmasound_core.o
+obj-$(CONFIG_DMASOUND_ATARI)  += dmasound_atari.o
+obj-$(CONFIG_DMASOUND_PMAC)   += dmasound_pmac.o
+obj-$(CONFIG_DMASOUND_PAULA)  += dmasound_paula.o
+obj-$(CONFIG_DMASOUND_Q40)    += dmasound_q40.o
 
 include $(TOPDIR)/Rules.make
+
+dmasound_pmac.o: $(dmasound_pmac-objs)
+	$(LD) $(EXTRA_LDFLAGS) -r -o $@ $(dmasound_pmac-objs)
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/sound/dmasound/awacs_defs.h linux/drivers/sound/dmasound/awacs_defs.h
--- linux.orig/drivers/sound/dmasound/awacs_defs.h	Mon Jan  7 23:41:09 2002
+++ linux/drivers/sound/dmasound/awacs_defs.h	Wed Dec 26 16:47:48 2001
@@ -71,17 +71,20 @@
 /* ------- - --- ----- - ------ */
 #define MASK_GAINRIGHT	(0xf)		/* Gain Right Mask */
 #define MASK_GAINLEFT	(0xf << 4)	/* Gain Left Mask */
-#define MASK_GAINLINE	(0x1 << 8)	/* Change Gain for Line??? */
-#define MASK_GAINMIC	(0x0 << 8)	/* Change Gain for Mic??? */
+#define MASK_GAINLINE	(0x1 << 8)	/* Disable Mic preamp */
+#define MASK_GAINMIC	(0x0 << 8)	/* Enable Mic preamp */
 
 #define MASK_MUX_CD	(0x1 << 9)	/* Select CD in MUX */
-#define MASK_MUX_AUDIN	(0x1 << 10)	/* Select Audio In in MUX */
-#define MASK_MUX_MIC	(0x1 << 11)	/* Select Mic in MUX */
+#define MASK_MUX_MIC	(0x1 << 10)	/* Select Mic in MUX */
+#define MASK_MUX_AUDIN	(0x1 << 11)	/* Select Audio In in MUX */
 #define MASK_MUX_LINE	MASK_MUX_AUDIN
 
 #define GAINRIGHT(x)	((x) & MASK_GAINRIGHT)
 #define GAINLEFT(x)	(((x) << 4) & MASK_GAINLEFT)
 
+#define DEF_CD_GAIN 0x00bb
+#define DEF_MIC_GAIN 0x00cc
+
 /* Address 1 Bit Masks */
 /* ------- - --- ----- */
 #define MASK_ADDR1RES1	(0x3)		/* Reserved */
@@ -93,7 +96,10 @@
 #define MASK_ADDR1RES2	(0x1 << 8)	/* Reserved */
 #define MASK_AMUTE	(0x1 << 9)	/* Output A (Headphone) Mute when 1 */
 #define MASK_HDMUTE	MASK_AMUTE
-#define MASK_PAROUT	(0x3 << 10)	/* Parallel Out (???) */
+#define MASK_PAROUT0	(0x1 << 10)	/* Parallel Output 0 */
+#define MASK_PAROUT1	(0x2 << 10)	/* Parallel Output 1 */
+
+#define MASK_MIC_BOOST  (0x4)           /* screamer mic boost */
 
 #define SAMPLERATE_48000	(0x0 << 3)	/* 48 or 44.1 kHz */
 #define SAMPLERATE_32000	(0x1 << 3)	/* 32 or 29.4 kHz */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/sound/dmasound/dac3550a.c linux/drivers/sound/dmasound/dac3550a.c
--- linux.orig/drivers/sound/dmasound/dac3550a.c	Thu Jan  1 00:00:00 1970
+++ linux/drivers/sound/dmasound/dac3550a.c	Mon Jan  7 13:00:34 2002
@@ -0,0 +1,269 @@
+/*
+ * Driver for the i2c/i2s based DAC3550a sound chip used
+ * on some Apple iBooks. Also known as "DACA".
+ *
+ *  This file is subject to the terms and conditions of the GNU General Public
+ *  License.  See the file COPYING in the main directory of this archive
+ *  for more details.
+ */
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/proc_fs.h>
+#include <linux/ioport.h>
+#include <linux/sysctl.h>
+#include <linux/types.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <asm/uaccess.h>
+#include <asm/errno.h>
+#include <asm/io.h>
+
+#include "dmasound.h"
+
+/* FYI: This code was derived from the tas3001c.c Texas/Tumbler mixer
+ * control code, as well as info derived from the AppleDACAAudio driver
+ * from Darwin CVS (main thing I derived being register numbers and 
+ * values, as well as when to make the calls). */
+
+#define I2C_DRIVERID_DACA (0xFDCB)
+
+#define DACA_VERSION	"0.1"
+#define DACA_DATE "20010930"
+
+static int cur_left_vol;
+static int cur_right_vol;
+static struct i2c_client * daca_client = NULL;
+
+static int daca_attach_adapter(struct i2c_adapter *adapter);
+
+static int daca_attach_adapter(struct i2c_adapter *adapter);
+static int daca_detect_client(struct i2c_adapter *adapter, int address);
+static int daca_detach_client(struct i2c_client *client);
+
+/* Unique ID allocation */
+static int daca_id;
+static int daca_initialized;
+
+struct daca_data
+{
+	int arf; /* place holder for furture use */
+};
+
+struct i2c_driver daca_driver = {  
+	name:		"DAC3550A driver  V " DACA_VERSION,
+	id:		I2C_DRIVERID_DACA,
+	flags:		I2C_DF_NOTIFY,
+	attach_adapter:	&daca_attach_adapter,
+	detach_client:	&daca_detach_client,
+	command:	NULL,
+	inc_use:	NULL, /* &daca_inc_use, */
+	dec_use:	NULL  /* &daca_dev_use  */
+  };
+
+
+#define VOL_MAX ((1<<20) - 1)
+
+void
+daca_get_volume(uint * left_vol, uint  *right_vol)
+{
+	*left_vol = cur_left_vol >> 5;
+	*right_vol = cur_right_vol >> 5;
+}
+
+int
+daca_set_volume(uint left_vol, uint right_vol)
+{
+	unsigned short voldata;
+  
+	if (!daca_client)
+		return -1;
+
+	/* Derived from experience, not from any specific values */
+	left_vol <<= 5;
+	right_vol <<= 5;
+
+	if (left_vol > VOL_MAX)
+		left_vol = VOL_MAX;
+	if (right_vol > VOL_MAX)
+		right_vol = VOL_MAX;
+
+	voldata = ((left_vol >> 14)  & 0x3f) << 8;
+	voldata |= (right_vol >> 14)  & 0x3f;
+  
+	if (i2c_smbus_write_word_data(daca_client, 2, voldata) < 0) {
+		printk("daca: failed to set volume \n");
+		return -1; 
+	}
+
+	cur_left_vol = left_vol;
+	cur_right_vol = right_vol;
+  
+	return 0;
+}
+
+int
+daca_leave_sleep(void)
+{
+	if (!daca_client)
+		return -1;
+  
+	/* Do a short sleep, just to make sure I2C bus is awake and paying
+	 * attention to us
+	 */
+	wait_ms(20);
+	/* Write the sample rate reg the value it needs */
+	i2c_smbus_write_byte_data(daca_client, 1, 8);
+	daca_set_volume(cur_left_vol >> 5, cur_right_vol >> 5);
+	/* Another short delay, just to make sure the other I2C bus writes
+	 * have taken...
+	 */
+	wait_ms(20);
+	/* Write the global config reg - invert right power amp,
+	 * DAC on, use 5-volt mode */
+	i2c_smbus_write_byte_data(daca_client, 3, 0x45);
+
+	return 0;
+}
+
+int
+daca_enter_sleep(void)
+{
+	if (!daca_client)
+		return -1;
+
+	i2c_smbus_write_byte_data(daca_client, 1, 8);
+	daca_set_volume(cur_left_vol >> 5, cur_right_vol >> 5);
+
+	/* Write the global config reg - invert right power amp,
+	 * DAC on, enter low-power mode, use 5-volt mode
+	 */
+	i2c_smbus_write_byte_data(daca_client, 3, 0x65);
+
+	return 0;
+}
+
+static int
+daca_attach_adapter(struct i2c_adapter *adapter)
+{
+	if (!strncmp(adapter->name, "mac-io", 6))
+		daca_detect_client(adapter, 0x4d);
+	return 0;
+}
+
+static int
+daca_init_client(struct i2c_client * new_client)
+{
+	/* 
+	 * Probe is not working with the current i2c-keywest
+	 * driver. We try to use addr 0x4d on each adapters
+	 * instead, by setting the format register.
+	 * 
+	 * FIXME: I'm sure that can be obtained from the
+	 * device-tree. --BenH.
+	 */
+  
+	/* Write the global config reg - invert right power amp,
+	 * DAC on, use 5-volt mode
+	 */
+	if (i2c_smbus_write_byte_data(new_client, 3, 0x45))
+		return -1;
+
+	i2c_smbus_write_byte_data(new_client, 1, 8);
+	daca_client = new_client;
+	daca_set_volume(15000, 15000);
+
+	return 0;
+}
+
+static int
+daca_detect_client(struct i2c_adapter *adapter, int address)
+{
+	int rc = 0;
+	struct i2c_client *new_client;
+
+	struct daca_data *data;
+	const char *client_name = "DAC 3550A Digital Equalizer";
+
+	new_client = kmalloc(
+			sizeof(struct i2c_client) + sizeof(struct daca_data),
+			GFP_KERNEL);
+	if (!new_client) {
+		rc = -ENOMEM;
+		goto bail;
+	}
+
+	/* This is tricky, but it will set the data to the right value. */
+	new_client->data = new_client + 1;
+	data = (struct daca_data *) (new_client->data);
+
+	new_client->addr = address;
+	new_client->data = data;
+	new_client->adapter = adapter;
+	new_client->driver = &daca_driver;
+	new_client->flags = 0;
+
+	strcpy(new_client->name,client_name);
+
+	new_client->id = daca_id++; /* Automatically unique */
+  
+	if (daca_init_client(new_client)) {
+		rc = -ENODEV;
+		goto bail;
+	}
+
+	/* Tell the i2c layer a new client has arrived */
+	if (i2c_attach_client(new_client)) {
+		rc = -ENODEV;
+		goto bail;
+	}
+bail:
+	if (rc && new_client)
+		kfree(new_client);
+	return rc;
+}
+
+
+static int
+daca_detach_client(struct i2c_client *client)
+{
+	if (client == daca_client)
+		daca_client = NULL;
+
+  	i2c_detach_client(client);
+	kfree(client);
+
+	return 0;
+}
+
+int
+daca_cleanup(void)
+{
+	if (!daca_initialized)
+		return -ENODEV;
+	i2c_del_driver(&daca_driver);
+	daca_initialized = 0;
+
+	return 0;
+}
+
+int
+daca_init(void)
+{
+	int rc;
+
+	if (daca_initialized)
+		return 0;
+		
+	printk("dac3550a driver version %s (%s)\n",DACA_VERSION,DACA_DATE);
+    
+	if ((rc = i2c_add_driver(&daca_driver))) {
+		printk("dac3550a: Driver registration failed, module not inserted.\n");
+		daca_cleanup();
+		return rc;
+	}
+	daca_initialized = 1;
+
+	return 0;
+}
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/sound/dmasound/dmasound.h linux/drivers/sound/dmasound/dmasound.h
--- linux.orig/drivers/sound/dmasound/dmasound.h	Mon Jan  7 23:41:09 2002
+++ linux/drivers/sound/dmasound/dmasound.h	Wed Dec 26 16:47:48 2001
@@ -1,4 +1,4 @@
-
+#ifndef _dmasound_h_
 /*
  *  linux/drivers/sound/dmasound/dmasound.h
  *
@@ -10,11 +10,11 @@
  *  device for true DSP processors but it will be called something else.
  *  In v3.0 it's /dev/sndproc but this could be a temporary solution.
  */
+#define _dmasound_h_
 
-
+#include <linux/types.h>
 #include <linux/config.h>
 
-
 #define SND_NDEVS	256	/* Number of supported devices */
 #define SND_DEV_CTL	0	/* Control port /dev/mixer */
 #define SND_DEV_SEQ	1	/* Sequencer output /dev/sequencer (FM
@@ -29,23 +29,16 @@
 #define SND_DEV_SNDPROC 9	/* /dev/sndproc for programmable devices */
 #define SND_DEV_PSS	SND_DEV_SNDPROC
 
-#define DSP_DEFAULT_SPEED	8000
-
-#define ON		1
-#define OFF		0
+/* switch on various prinks */
+#define DEBUG_DMASOUND 1
 
 #define MAX_AUDIO_DEV	5
-#define MAX_MIXER_DEV	2
+#define MAX_MIXER_DEV	4
 #define MAX_SYNTH_DEV	3
 #define MAX_MIDI_DEV	6
 #define MAX_TIMER_DEV	3
 
-
 #define MAX_CATCH_RADIUS	10
-#define MIN_BUFFERS		4
-#define MIN_BUFSIZE		4	/* in KB */
-#define MAX_BUFSIZE		128	/* Limit for Amiga in KB */
-
 
 #define le2be16(x)	(((x)<<8 & 0xff00) | ((x)>>8 & 0x00ff))
 #define le2be16dbl(x)	(((x)<<8 & 0xff00ff00) | ((x)>>8 & 0x00ff00ff))
@@ -67,21 +60,34 @@
      */
 
 #undef HAS_8BIT_TABLES
-#undef HAS_14BIT_TABLES
-#undef HAS_16BIT_TABLES
 #undef HAS_RECORD
 
 #if defined(CONFIG_DMASOUND_ATARI) || defined(CONFIG_DMASOUND_ATARI_MODULE) ||\
     defined(CONFIG_DMASOUND_PAULA) || defined(CONFIG_DMASOUND_PAULA_MODULE) ||\
     defined(CONFIG_DMASOUND_Q40) || defined(CONFIG_DMASOUND_Q40_MODULE)
 #define HAS_8BIT_TABLES
+#define MIN_BUFFERS	4
+#define MIN_BUFSIZE	(1<<12)	/* in bytes (- where does this come from ?) */
+#define MIN_FRAG_SIZE	8	/* not 100% sure about this */
+#define MAX_BUFSIZE	(1<<17)	/* Limit for Amiga is 128 kb */
+#define MAX_FRAG_SIZE	15	/* allow *4 for mono-8 => stereo-16 (for multi) */
+
+#else /* is pmac and multi is off */
+
+#define MIN_BUFFERS	2
+#define MIN_BUFSIZE	(1<<8)	/* in bytes */
+#define MIN_FRAG_SIZE	8
+#define MAX_BUFSIZE	(1<<18)	/* this is somewhat arbitrary for pmac */
+#define MAX_FRAG_SIZE	16	/* need to allow *4 for mono-8 => stereo-16 */
 #endif
-#if defined(CONFIG_DMASOUND_AWACS) || defined(CONFIG_DMASOUND_AWACS_MODULE)
-#define HAS_16BIT_TABLES
+
+#define DEFAULT_N_BUFFERS 4
+#define DEFAULT_BUFF_SIZE (1<<15)
+
+#if defined(CONFIG_DMASOUND_PMAC) || defined(CONFIG_DMASOUND_PMAC_MODULE)
 #define HAS_RECORD
 #endif
 
-
     /*
      *  Initialization
      */
@@ -93,6 +99,14 @@
 #define dmasound_deinit()	do { } while (0)
 #endif
 
+/* description of the set-up applies to either hard or soft settings */
+
+typedef struct {
+    int format;		/* AFMT_* */
+    int stereo;		/* 0 = mono, 1 = stereo */
+    int size;		/* 8/16 bit*/
+    int speed;		/* speed */
+} SETTINGS;
 
     /*
      *  Machine definitions
@@ -117,30 +131,29 @@
     int (*setTreble)(int);
     int (*setGain)(int);
     void (*play)(void);
-    void (*record)(void);			/* optional */
-    void (*mixer_init)(void);			/* optional */
-    int (*mixer_ioctl)(u_int, u_long);		/* optional */
-    void (*write_sq_setup)(void);		/* optional */
-    void (*read_sq_setup)(void);		/* optional */
-    void (*sq_open)(void);			/* optional */
-    int (*state_info)(char *);			/* optional */
-    void (*abort_read)(void);			/* optional */
+    void (*record)(void);		/* optional */
+    void (*mixer_init)(void);		/* optional */
+    int (*mixer_ioctl)(u_int, u_long);	/* optional */
+    int (*write_sq_setup)(void);	/* optional */
+    int (*read_sq_setup)(void);		/* optional */
+    int (*sq_open)(mode_t);		/* optional */
+    int (*state_info)(char *, size_t);	/* optional */
+    void (*abort_read)(void);		/* optional */
     int min_dsp_speed;
+    int max_dsp_speed;
+    int version ;
+    int hardware_afmts ;		/* OSS says we only return h'ware info */
+					/* when queried via SNDCTL_DSP_GETFMTS */
+    int capabilities ;		/* low-level reply to SNDCTL_DSP_GETCAPS */
+    SETTINGS default_hard ;	/* open() or init() should set something valid */
+    SETTINGS default_soft ;	/* you can make it look like old OSS, if you want to */
 } MACHINE;
 
-
     /*
      *  Low level stuff
      */
 
 typedef struct {
-    int format;		/* AFMT_* */
-    int stereo;		/* 0 = mono, 1 = stereo */
-    int size;		/* 8/16 bit*/
-    int speed;		/* speed */
-} SETTINGS;
-
-typedef struct {
     ssize_t (*ct_ulaw)(const u_char *, size_t, u_char *, ssize_t *, ssize_t);
     ssize_t (*ct_alaw)(const u_char *, size_t, u_char *, ssize_t *, ssize_t);
     ssize_t (*ct_s8)(const u_char *, size_t, u_char *, ssize_t *, ssize_t);
@@ -170,11 +183,10 @@
 
 extern struct sound_settings dmasound;
 
+#ifdef HAS_8BIT_TABLES
 extern char dmasound_ulaw2dma8[];
 extern char dmasound_alaw2dma8[];
-extern short dmasound_ulaw2dma16[];
-extern short dmasound_alaw2dma16[];
-
+#endif
 
     /*
      *  Mid level stuff
@@ -207,14 +219,17 @@
 
 struct sound_queue {
     /* buffers allocated for this queue */
-    int numBufs;
-    int bufSize;			/* in bytes */
+    int numBufs;		/* real limits on what the user can have */
+    int bufSize;		/* in bytes */
     char **buffers;
 
     /* current parameters */
-    int max_count;
-    int block_size;			/* in bytes */
-    int max_active;
+    int locked ;		/* params cannot be modified when != 0 */
+    int user_frags ;		/* user requests this many */
+    int user_frag_size ;	/* of this size */
+    int max_count;		/* actual # fragments <= numBufs */
+    int block_size;		/* internal block size in bytes */
+    int max_active;		/* in-use fragments <= max_count */
 
     /* it shouldn't be necessary to declare any of these volatile */
     int front, rear, count;
@@ -230,19 +245,35 @@
     int active;
     wait_queue_head_t action_queue, open_queue, sync_queue;
     int open_mode;
-    int busy, syncing;
+    int busy, syncing, xruns, died;
 };
 
 #define SLEEP(queue)		interruptible_sleep_on_timeout(&queue, HZ)
 #define WAKE_UP(queue)		(wake_up_interruptible(&queue))
 
 extern struct sound_queue dmasound_write_sq;
+#ifdef HAS_RECORD
 extern struct sound_queue dmasound_read_sq;
+#endif
 
 #define write_sq	dmasound_write_sq
+#ifdef HAS_RECORD
 #define read_sq		dmasound_read_sq
+#endif
 
 extern int dmasound_catchRadius;
 
 #define catchRadius	dmasound_catchRadius
 
+/* define the value to be put in the byte-swap reg in mac-io
+   when we want it to swap for us.
+*/
+#define BS_VAL 1
+
+static inline void wait_ms(unsigned int ms)
+{
+	current->state = TASK_UNINTERRUPTIBLE;
+	schedule_timeout(1 + ms * HZ / 1000);
+}
+
+#endif /* _dmasound_h_ */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/sound/dmasound/dmasound_atari.c linux/drivers/sound/dmasound/dmasound_atari.c
--- linux.orig/drivers/sound/dmasound/dmasound_atari.c	Mon Jan  7 23:41:09 2002
+++ linux/drivers/sound/dmasound/dmasound_atari.c	Wed Dec 26 16:47:48 2001
@@ -1,10 +1,16 @@
-
 /*
  *  linux/drivers/sound/dmasound/dmasound_atari.c
  *
  *  Atari TT and Falcon DMA Sound Driver
  *
  *  See linux/drivers/sound/dmasound/dmasound_core.c for copyright and credits
+ *  prior to 28/01/2001
+ *
+ *  28/01/2001 [0.1] Iain Sandoe
+ *		     - added versioning
+ *		     - put in and populated the hardware_afmts field.
+ *             [0.2] - put in SNDCTL_DSP_GETCAPS value.
+ *  01/02/2001 [0.3] - put in default hard/soft settings.
  */
 
 
@@ -21,10 +27,11 @@
 
 #include "dmasound.h"
 
+#define DMASOUND_ATARI_REVISION 0
+#define DMASOUND_ATARI_EDITION 3
 
 extern void atari_microwire_cmd(int cmd);
 
-
 static int is_falcon;
 static int write_sq_ignore_int = 0;	/* ++TeSche: used for Falcon */
 
@@ -137,9 +144,9 @@
 static int TTMixerIoctl(u_int cmd, u_long arg);
 static int FalconMixerIoctl(u_int cmd, u_long arg);
 static void AtaWriteSqSetup(void);
-static void AtaSqOpen(void);
-static int TTStateInfo(char *buffer);
-static int FalconStateInfo(char *buffer);
+static int AtaSqOpen(mode_t mode);
+static int TTStateInfo(char *buffer, size_t space);
+static int FalconStateInfo(char *buffer, size_t space);
 
 
 /*** Translations ************************************************************/
@@ -1438,43 +1445,73 @@
 	return AtaMixerIoctl(cmd, arg);
 }
 
-static void AtaWriteSqSetup(void)
+static int AtaWriteSqSetup(void)
 {
 	write_sq_ignore_int = 0;
+	return 0 ;
 }
 
-static void AtaSqOpen(void)
+static int AtaSqOpen(mode_t mode)
 {
 	write_sq_ignore_int = 1;
+	return 0 ;
 }
 
-static int TTStateInfo(char *buffer)
+static int TTStateInfo(char *buffer, size_t space)
 {
 	int len = 0;
-	len += sprintf(buffer+len, "\tsound.volume_left = %ddB [-40...0]\n",
+	len += sprintf(buffer+len, "\tvol left  %ddB [-40...  0]\n",
 		       dmasound.volume_left);
-	len += sprintf(buffer+len, "\tsound.volume_right = %ddB [-40...0]\n",
+	len += sprintf(buffer+len, "\tvol right %ddB [-40...  0]\n",
 		       dmasound.volume_right);
-	len += sprintf(buffer+len, "\tsound.bass = %ddB [-12...+12]\n",
+	len += sprintf(buffer+len, "\tbass      %ddB [-12...+12]\n",
 		       dmasound.bass);
-	len += sprintf(buffer+len, "\tsound.treble = %ddB [-12...+12]\n",
+	len += sprintf(buffer+len, "\ttreble    %ddB [-12...+12]\n",
 		       dmasound.treble);
+	if (len >= space) {
+		printk(KERN_ERR "dmasound_atari: overflowed state buffer alloc.\n") ;
+		len = space ;
+	}
 	return len;
 }
 
-static int FalconStateInfo(char *buffer)
+static int FalconStateInfo(char *buffer, size_t space)
 {
 	int len = 0;
-	len += sprintf(buffer+len, "\tsound.volume_left = %ddB [-22.5...0]\n",
+	len += sprintf(buffer+len, "\tvol left  %ddB [-22.5 ... 0]\n",
 		       dmasound.volume_left);
-	len += sprintf(buffer+len, "\tsound.volume_right = %ddB [-22.5...0]\n",
+	len += sprintf(buffer+len, "\tvol right %ddB [-22.5 ... 0]\n",
 		       dmasound.volume_right);
+	if (len >= space) {
+		printk(KERN_ERR "dmasound_atari: overflowed state buffer alloc.\n") ;
+		len = space ;
+	}
 	return len;
 }
 
 
 /*** Machine definitions *****************************************************/
 
+static SETTINGS def_hard_falcon = {
+	format: AFMT_S8,
+	stereo: 0,
+	size: 8,
+	speed: 8195
+} ;
+
+static SETTINGS def_hard_tt = {
+	format: AFMT_S8,
+	stereo: 0,
+	size: 8,
+	speed: 12517
+} ;
+
+static SETTINGS def_soft = {
+	format: AFMT_U8,
+	stereo: 0,
+	size: 8,
+	speed: 8000
+} ;
 
 static MACHINE machTT = {
 	name:		"Atari",
@@ -1501,6 +1538,9 @@
 	sq_open:	AtaSqOpen,
 	state_info:	TTStateInfo,
 	min_dsp_speed:	6258,
+	version:	((DMASOUND_ATARI_REVISION<<8) | DMASOUND_ATARI_EDITION),
+	hardware_afmts:	AFMT_S8,  /* h'ware-supported formats *only* here */
+	capabilities:	 DSP_CAP_BATCH	/* As per SNDCTL_DSP_GETCAPS */
 };
 
 static MACHINE machFalcon = {
@@ -1525,6 +1565,9 @@
 	sq_open:	AtaSqOpen,
 	state_info:	FalconStateInfo,
 	min_dsp_speed:	8195,
+	version:	((DMASOUND_ATARI_REVISION<<8) | DMASOUND_ATARI_EDITION),
+	hardware_afmts:	(AFMT_S8 | AFMT_S16_BE), /* h'ware-supported formats *only* here */
+	capabilities:	 DSP_CAP_BATCH	/* As per SNDCTL_DSP_GETCAPS */
 };
 
 
@@ -1536,9 +1579,13 @@
 	if (MACH_IS_ATARI && ATARIHW_PRESENT(PCM_8BIT)) {
 	    if (ATARIHW_PRESENT(CODEC)) {
 		dmasound.mach = machFalcon;
+		dmasound.mach.default_soft = def_soft ;
+		dmasound.mach.default_hard = def_hard_falcon ;
 		is_falcon = 1;
 	    } else if (ATARIHW_PRESENT(MICROWIRE)) {
 		dmasound.mach = machTT;
+		dmasound.mach.default_soft = def_soft ;
+		dmasound.mach.default_hard = def_hard_tt ;
 		is_falcon = 0;
 	    } else
 		return -ENODEV;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/sound/dmasound/dmasound_awacs.c linux/drivers/sound/dmasound/dmasound_awacs.c
--- linux.orig/drivers/sound/dmasound/dmasound_awacs.c	Mon Jan  7 23:41:09 2002
+++ linux/drivers/sound/dmasound/dmasound_awacs.c	Wed Dec 26 16:47:48 2001
@@ -1,13 +1,54 @@
-
 /*
  *  linux/drivers/sound/dmasound/dmasound_awacs.c
  *
  *  PowerMac `AWACS' and `Burgundy' DMA Sound Driver
+ *  with some limited support for DACA & Tumbler
  *
- *  See linux/drivers/sound/dmasound/dmasound_core.c for copyright and credits
- */
-
+ *  See linux/drivers/sound/dmasound/dmasound_core.c for copyright and
+ *  history prior to 2001/01/26.
+ *
+ *	26/01/2001 ed 0.1 Iain Sandoe
+ *		- added version info.
+ *		- moved dbdma command buffer allocation to PMacXXXSqSetup()
+ *		- fixed up beep dbdma cmd buffers
+ *
+ *	08/02/2001 [0.2]
+ *		- make SNDCTL_DSP_GETFMTS return the correct info for the h/w
+ *		- move soft format translations to a separate file
+ *		- [0.3] make SNDCTL_DSP_GETCAPS return correct info.
+ *		- [0.4] more informative machine name strings.
+ *		- [0.5]
+ *		- record changes.
+ *		- made the default_hard/soft entries.
+ *	04/04/2001 [0.6]
+ *		- minor correction to bit assignments in awacs_defs.h
+ *		- incorporate mixer changes from 2.2.x back-port.
+ *		- take out passthru as a rec input (it isn't).
+ *              - make Input Gain slider work the 'right way up'.
+ *              - try to make the mixer sliders more logical - so now the
+ *                input selectors are just two-state (>50% == ON) and the
+ *                Input Gain slider handles the rest of the gain issues.
+ *              - try to pick slider representations that most closely match
+ *                the actual use - e.g. IGain for input gain... 
+ *              - first stab at over/under-run detection.
+ *		- minor cosmetic changes to IRQ identification.
+ *		- fix bug where rates > max would be reported as supported.
+ *              - first stab at over/under-run detection.
+ *              - make use of i2c for mixer settings conditional on perch
+ *                rather than cuda (some machines without perch have cuda).
+ *              - fix bug where TX stops when dbdma status comes up "DEAD"
+ *		  so far only reported on PowerComputing clones ... but.
+ *		- put in AWACS/Screamer register write timeouts.
+ *		- part way to partitioning the init() stuff
+ *		- first pass at 'tumbler' stuff (not support - just an attempt
+ *		  to allow the driver to load on new G4s).
+*/
+
+/* GENERAL FIXME/TODO: check that the assumptions about what is written to
+   mac-io is valid for DACA & Tumbler.
+*/
 
+#include <linux/types.h>
 #include <linux/module.h>
 #include <linux/config.h>
 #include <linux/slab.h>
@@ -18,6 +59,9 @@
 #include <linux/nvram.h>
 #include <linux/tty.h>
 #include <linux/vt_kern.h>
+#include <linux/irq.h>
+#include <linux/kmod.h>
+#include <asm/semaphore.h>
 #ifdef CONFIG_ADB_CUDA
 #include <linux/cuda.h>
 #endif
@@ -25,20 +69,30 @@
 #include <linux/pmu.h>
 #endif
 
+#include <linux/i2c-dev.h>
+
 #include <asm/uaccess.h>
 #include <asm/prom.h>
 #include <asm/machdep.h>
 #include <asm/io.h>
 #include <asm/dbdma.h>
-#include <asm/feature.h>
+#include <asm/pmac_feature.h>
 #include <asm/irq.h>
+#include <asm/nvram.h>
 
 #include "awacs_defs.h"
 #include "dmasound.h"
 
+#define DMASOUND_AWACS_REVISION 0
+#define DMASOUND_AWACS_EDITION 6
 
+#define AWACS_BURGUNDY	100	/* fake revision # for burgundy */
+#define AWACS_TUMBLER    90	/* fake revision # for tumbler */
+#define AWACS_DACA	 80	/* fake revision # for daca (ibook) */
+#define AWACS_AWACS       2     /* holding revision for AWACS */
+#define AWACS_SCREAMER    3     /* holding revision for Screamer */
 /*
- * Interrupt numbers and addresses, obtained from the device tree.
+ * Interrupt numbers and addresses, & info obtained from the device tree.
  */
 static int awacs_irq, awacs_tx_irq, awacs_rx_irq;
 static volatile struct awacs_regs *awacs;
@@ -50,28 +104,58 @@
 
 static char awacs_name[64];
 static int awacs_revision;
-int awacs_is_screamer = 0;
-int awacs_device_id = 0;
-int awacs_has_iic = 0;
-#define AWACS_BURGUNDY	100		/* fake revision # for burgundy */
+static int awacs_sleeping;
+static DECLARE_MUTEX(dmasound_sem);
+
+static int sound_device_id;		/* exists after iMac revA */
+static int hw_can_byteswap = 1 ;	/* most pmac sound h/w can */
+
+/* model info */
+/* To be replaced with better interaction with pmac_feature.c */
+static int is_pbook_3X00;
+static int is_pbook_g3;
+
+/* expansion info */
+static int has_perch;
+static int has_ziva;
+
+/* for earlier powerbooks which need fiddling with mac-io to enable
+ * cd etc.
+*/
+static unsigned char *latch_base;
+static unsigned char *macio_base;
 
 /*
  * Space for the DBDMA command blocks.
  */
 static void *awacs_tx_cmd_space;
 static volatile struct dbdma_cmd *awacs_tx_cmds;
+static int number_of_tx_cmd_buffers = 0;
 
 static void *awacs_rx_cmd_space;
 static volatile struct dbdma_cmd *awacs_rx_cmds;
+static int number_of_rx_cmd_buffers = 0;
 
 /*
  * Cached values of AWACS registers (we can't read them).
- * Except on the burgundy. XXX
+ * Except on the burgundy (and screamer). XXX
  */
+
 int awacs_reg[8];
+int awacs_reg1_save;
+
+/* tracking values for the mixer contents
+*/
 
-#define HAS_16BIT_TABLES
-#undef HAS_8BIT_TABLES
+static int spk_vol = 0 ;
+static int line_vol = 0 ;
+static int passthru_vol = 0 ;
+
+static int ip_gain = 0 ; /* mic preamp settings */
+static int rec_lev = 0x4545 ; /* default CD gain 69 % */
+static int mic_lev = 0 ;
+static int cd_lev = 0x6363 ; /* 99 % */
+static int line_lev = 0 ;
 
 /*
  * Stuff for outputting a beep.  The values range from -327 to +327
@@ -113,20 +197,18 @@
 	-269,	-245,	-218,	-187,	-153,	-117,	-79,	-40,
 };
 
+/* beep support */
 #define BEEP_SRATE	22050	/* 22050 Hz sample rate */
 #define BEEP_BUFLEN	512
 #define BEEP_VOLUME	15	/* 0 - 100 */
 
-static int beep_volume = BEEP_VOLUME;
+static int beep_vol = BEEP_VOLUME;
 static int beep_playing = 0;
 static int awacs_beep_state = 0;
 static short *beep_buf;
+static void *beep_dbdma_cmd_space;
 static volatile struct dbdma_cmd *beep_dbdma_cmd;
 static void (*orig_mksound)(unsigned int, unsigned int);
-static int is_pbook_3400;
-static unsigned char *latch_base;
-static int is_pbook_G3;
-static unsigned char *macio_base;
 
 /* Burgundy functions */
 static void awacs_burgundy_wcw(unsigned addr,unsigned newval);
@@ -136,6 +218,18 @@
 static void awacs_burgundy_write_mvolume(unsigned address, int volume);
 static int awacs_burgundy_read_mvolume(unsigned address);
 
+/* we will allocate a single 'emergency' dbdma cmd block to use if the
+   tx status comes up "DEAD".  This happens on some PowerComputing Pmac
+   clones, either owing to a bug in dbdma or some interaction between
+   IDE and sound.  However, this measure would deal with DEAD status if
+   if appeared elsewhere.
+
+   for the sake of memory efficiency we'll allocate this cmd as part of
+   the beep cmd stuff.
+*/
+
+static volatile struct dbdma_cmd *emergency_dbdma_cmd;
+
 #ifdef CONFIG_PMAC_PBOOK
 /*
  * Stuff for restoring after a sleep.
@@ -146,78 +240,11 @@
 };
 #endif /* CONFIG_PMAC_PBOOK */
 
-static int expand_bal;	/* Balance factor for expanding (not volume!) */
-static int expand_data;	/* Data for expanding */
-
-
-/*** Translations ************************************************************/
-
-
-/* ++TeSche: radically changed for new expanding purposes...
- *
- * These two routines now deal with copying/expanding/translating the samples
- * from user space into our buffer at the right frequency. They take care about
- * how much data there's actually to read, how much buffer space there is and
- * to convert samples into the right frequency/encoding. They will only work on
- * complete samples so it may happen they leave some bytes in the input stream
- * if the user didn't write a multiple of the current sample size. They both
- * return the number of bytes they've used from both streams so you may detect
- * such a situation. Luckily all programs should be able to cope with that.
- *
- * I think I've optimized anything as far as one can do in plain C, all
- * variables should fit in registers and the loops are really short. There's
- * one loop for every possible situation. Writing a more generalized and thus
- * parameterized loop would only produce slower code. Feel free to optimize
- * this in assembler if you like. :)
- *
- * I think these routines belong here because they're not yet really hardware
- * independent, especially the fact that the Falcon can play 16bit samples
- * only in stereo is hardcoded in both of them!
- *
- * ++geert: split in even more functions (one per format)
- */
-
-static ssize_t pmac_ct_law(const u_char *userPtr, size_t userCount,
-			   u_char frame[], ssize_t *frameUsed,
-			   ssize_t frameLeft);
-static ssize_t pmac_ct_s8(const u_char *userPtr, size_t userCount,
-			  u_char frame[], ssize_t *frameUsed,
-			  ssize_t frameLeft);
-static ssize_t pmac_ct_u8(const u_char *userPtr, size_t userCount,
-			  u_char frame[], ssize_t *frameUsed,
-			  ssize_t frameLeft);
-static ssize_t pmac_ct_s16(const u_char *userPtr, size_t userCount,
-			   u_char frame[], ssize_t *frameUsed,
-			   ssize_t frameLeft);
-static ssize_t pmac_ct_u16(const u_char *userPtr, size_t userCount,
-			   u_char frame[], ssize_t *frameUsed,
-			   ssize_t frameLeft);
-static ssize_t pmac_ctx_law(const u_char *userPtr, size_t userCount,
-			    u_char frame[], ssize_t *frameUsed,
-			    ssize_t frameLeft);
-static ssize_t pmac_ctx_s8(const u_char *userPtr, size_t userCount,
-			   u_char frame[], ssize_t *frameUsed,
-			   ssize_t frameLeft);
-static ssize_t pmac_ctx_u8(const u_char *userPtr, size_t userCount,
-			   u_char frame[], ssize_t *frameUsed,
-			   ssize_t frameLeft);
-static ssize_t pmac_ctx_s16(const u_char *userPtr, size_t userCount,
-			    u_char frame[], ssize_t *frameUsed,
-			    ssize_t frameLeft);
-static ssize_t pmac_ctx_u16(const u_char *userPtr, size_t userCount,
-			    u_char frame[], ssize_t *frameUsed,
-			    ssize_t frameLeft);
-static ssize_t pmac_ct_s16_read(const u_char *userPtr, size_t userCount,
-			   u_char frame[], ssize_t *frameUsed,
-			   ssize_t frameLeft);
-static ssize_t pmac_ct_u16_read(const u_char *userPtr, size_t userCount,
-			   u_char frame[], ssize_t *frameUsed,
-			   ssize_t frameLeft);
-
+/* for (soft) sample rate translations */
+int expand_bal;		/* Balance factor for expanding (not volume!) */
 
 /*** Low level stuff *********************************************************/
 
-
 static void PMacOpen(void);
 static void PMacRelease(void);
 static void *PMacAlloc(unsigned int size, int flags);
@@ -244,565 +271,212 @@
 
 /*** Mid level stuff **********************************************************/
 
-
 static int PMacMixerIoctl(u_int cmd, u_long arg);
-static void PMacWriteSqSetup(void);
-static void PMacReadSqSetup(void);
+static int PMacWriteSqSetup(void);
+static int PMacReadSqSetup(void);
 static void PMacAbortRead(void);
 
+extern TRANS transAwacsNormal ;
+extern TRANS transAwacsExpand ;
+extern TRANS transAwacsNormalRead ;
+
+extern int daca_init(void);
+extern int daca_cleanup(void);
+extern int daca_set_volume(uint left_vol, uint right_vol);
+extern void daca_get_volume(uint * left_vol, uint  *right_vol);
+extern int daca_enter_sleep(void);
+extern int daca_leave_sleep(void);
+
+extern int tas_init(void);
+extern int tas_cleanup(void);
+extern int tumbler_set_volume(uint left_vol, uint right_vol);
+extern void tumbler_get_volume(uint * left_vol, uint  *right_vol);
+extern void tumbler_set_treble(int treble);
+extern void tumbler_get_treble(int *treble);
+extern void tumbler_set_bass(int bass);
+extern void tumbler_get_bass(int *bass);
+extern void tumbler_set_pcm_lvl(int pcm_lvl);
+extern void tumbler_get_pcm_lvl(int *pcm_lvl);
+
+#define TRY_LOCK()	\
+	if ((rc = down_interruptible(&dmasound_sem)) != 0)	\
+		return rc;
+#define LOCK()		down(&dmasound_sem);
+
+#define UNLOCK()	up(&dmasound_sem);
+
+/* We use different versions that the ones provided in dmasound.h
+ * 
+ * FIXME: Use different names ;)
+ */
+#undef IOCTL_IN
+#undef IOCTL_OUT
 
-/*** Translations ************************************************************/
-
-
-static ssize_t pmac_ct_law(const u_char *userPtr, size_t userCount,
-			   u_char frame[], ssize_t *frameUsed,
-			   ssize_t frameLeft)
-{
-	short *table = dmasound.soft.format == AFMT_MU_LAW
-		? dmasound_ulaw2dma16 : dmasound_alaw2dma16;
-	ssize_t count, used;
-	short *p = (short *) &frame[*frameUsed];
-	int val, stereo = dmasound.soft.stereo;
-
-	frameLeft >>= 2;
-	if (stereo)
-		userCount >>= 1;
-	used = count = min_t(unsigned long, userCount, frameLeft);
-	while (count > 0) {
-		u_char data;
-		if (get_user(data, userPtr++))
-			return -EFAULT;
-		val = table[data];
-		*p++ = val;
-		if (stereo) {
-			if (get_user(data, userPtr++))
-				return -EFAULT;
-			val = table[data];
-		}
-		*p++ = val;
-		count--;
-	}
-	*frameUsed += used * 4;
-	return stereo? used * 2: used;
-}
-
-
-static ssize_t pmac_ct_s8(const u_char *userPtr, size_t userCount,
-			  u_char frame[], ssize_t *frameUsed,
-			  ssize_t frameLeft)
-{
-	ssize_t count, used;
-	short *p = (short *) &frame[*frameUsed];
-	int val, stereo = dmasound.soft.stereo;
-
-	frameLeft >>= 2;
-	if (stereo)
-		userCount >>= 1;
-	used = count = min_t(unsigned long, userCount, frameLeft);
-	while (count > 0) {
-		u_char data;
-		if (get_user(data, userPtr++))
-			return -EFAULT;
-		val = data << 8;
-		*p++ = val;
-		if (stereo) {
-			if (get_user(data, userPtr++))
-				return -EFAULT;
-			val = data << 8;
-		}
-		*p++ = val;
-		count--;
-	}
-	*frameUsed += used * 4;
-	return stereo? used * 2: used;
-}
-
-
-static ssize_t pmac_ct_u8(const u_char *userPtr, size_t userCount,
-			  u_char frame[], ssize_t *frameUsed,
-			  ssize_t frameLeft)
-{
-	ssize_t count, used;
-	short *p = (short *) &frame[*frameUsed];
-	int val, stereo = dmasound.soft.stereo;
-
-	frameLeft >>= 2;
-	if (stereo)
-		userCount >>= 1;
-	used = count = min_t(unsigned long, userCount, frameLeft);
-	while (count > 0) {
-		u_char data;
-		if (get_user(data, userPtr++))
-			return -EFAULT;
-		val = (data ^ 0x80) << 8;
-		*p++ = val;
-		if (stereo) {
-			if (get_user(data, userPtr++))
-				return -EFAULT;
-			val = (data ^ 0x80) << 8;
-		}
-		*p++ = val;
-		count--;
-	}
-	*frameUsed += used * 4;
-	return stereo? used * 2: used;
-}
-
-
-static ssize_t pmac_ct_s16(const u_char *userPtr, size_t userCount,
-			   u_char frame[], ssize_t *frameUsed,
-			   ssize_t frameLeft)
-{
-	ssize_t count, used;
-	int stereo = dmasound.soft.stereo;
-	short *fp = (short *) &frame[*frameUsed];
-
-	frameLeft >>= 2;
-	userCount >>= (stereo? 2: 1);
-	used = count = min_t(unsigned long, userCount, frameLeft);
-	if (!stereo) {
-		short *up = (short *) userPtr;
-		while (count > 0) {
-			short data;
-			if (get_user(data, up++))
-				return -EFAULT;
-			*fp++ = data;
-			*fp++ = data;
-			count--;
-		}
-	} else {
-		if (copy_from_user(fp, userPtr, count * 4))
-			return -EFAULT;
-	}
-	*frameUsed += used * 4;
-	return stereo? used * 4: used * 2;
-}
+#define IOCTL_IN(arg, ret)	\
+	rc = get_user(ret, (int *)(arg)); \
+	if (rc) break;
+#define IOCTL_OUT(arg, ret)	\
+	ioctl_return2((int *)(arg), ret)
 
-static ssize_t pmac_ct_u16(const u_char *userPtr, size_t userCount,
-			   u_char frame[], ssize_t *frameUsed,
-			   ssize_t frameLeft)
-{
-	ssize_t count, used;
-	int mask = (dmasound.soft.format == AFMT_U16_LE? 0x0080: 0x8000);
-	int stereo = dmasound.soft.stereo;
-	short *fp = (short *) &frame[*frameUsed];
-	short *up = (short *) userPtr;
-
-	frameLeft >>= 2;
-	userCount >>= (stereo? 2: 1);
-	used = count = min_t(unsigned long, userCount, frameLeft);
-	while (count > 0) {
-		int data;
-		if (get_user(data, up++))
-			return -EFAULT;
-		data ^= mask;
-		*fp++ = data;
-		if (stereo) {
-			if (get_user(data, up++))
-				return -EFAULT;
-			data ^= mask;
-		}
-		*fp++ = data;
-		count--;
-	}
-	*frameUsed += used * 4;
-	return stereo? used * 4: used * 2;
+static inline int ioctl_return2(int *addr, int value)
+{
+	return value < 0 ? value : put_user(value, addr);
 }
 
 
-static ssize_t pmac_ctx_law(const u_char *userPtr, size_t userCount,
-			    u_char frame[], ssize_t *frameUsed,
-			    ssize_t frameLeft)
-{
-	unsigned short *table = (unsigned short *)
-		(dmasound.soft.format == AFMT_MU_LAW
-		 ? dmasound_ulaw2dma16 : dmasound_alaw2dma16);
-	unsigned int data = expand_data;
-	unsigned int *p = (unsigned int *) &frame[*frameUsed];
-	int bal = expand_bal;
-	int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
-	int utotal, ftotal;
-	int stereo = dmasound.soft.stereo;
-
-	frameLeft >>= 2;
-	if (stereo)
-		userCount >>= 1;
-	ftotal = frameLeft;
-	utotal = userCount;
-	while (frameLeft) {
-		u_char c;
-		if (bal < 0) {
-			if (userCount == 0)
-				break;
-			if (get_user(c, userPtr++))
-				return -EFAULT;
-			data = table[c];
-			if (stereo) {
-				if (get_user(c, userPtr++))
-					return -EFAULT;
-				data = (data << 16) + table[c];
-			} else
-				data = (data << 16) + data;
-			userCount--;
-			bal += hSpeed;
-		}
-		*p++ = data;
-		frameLeft--;
-		bal -= sSpeed;
-	}
-	expand_bal = bal;
-	expand_data = data;
-	*frameUsed += (ftotal - frameLeft) * 4;
-	utotal -= userCount;
-	return stereo? utotal * 2: utotal;
-}
+/*** AE - TUMBLER START *********************************************************/
 
 
-static ssize_t pmac_ctx_s8(const u_char *userPtr, size_t userCount,
-			   u_char frame[], ssize_t *frameUsed,
-			   ssize_t frameLeft)
-{
-	unsigned int *p = (unsigned int *) &frame[*frameUsed];
-	unsigned int data = expand_data;
-	int bal = expand_bal;
-	int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
-	int stereo = dmasound.soft.stereo;
-	int utotal, ftotal;
-
-	frameLeft >>= 2;
-	if (stereo)
-		userCount >>= 1;
-	ftotal = frameLeft;
-	utotal = userCount;
-	while (frameLeft) {
-		u_char c;
-		if (bal < 0) {
-			if (userCount == 0)
-				break;
-			if (get_user(c, userPtr++))
-				return -EFAULT;
-			data = c << 8;
-			if (stereo) {
-				if (get_user(c, userPtr++))
-					return -EFAULT;
-				data = (data << 16) + (c << 8);
-			} else
-				data = (data << 16) + data;
-			userCount--;
-			bal += hSpeed;
-		}
-		*p++ = data;
-		frameLeft--;
-		bal -= sSpeed;
-	}
-	expand_bal = bal;
-	expand_data = data;
-	*frameUsed += (ftotal - frameLeft) * 4;
-	utotal -= userCount;
-	return stereo? utotal * 2: utotal;
-}
+int gpio_audio_reset, gpio_audio_reset_pol;
+int gpio_amp_mute, gpio_amp_mute_pol;
+int gpio_headphone_mute, gpio_headphone_mute_pol;
+int gpio_headphone_detect, gpio_headphone_detect_pol;
+int gpio_headphone_irq;
 
+int
+setup_audio_gpio(const char *name, const char* compatible, int *gpio_addr, int* gpio_pol)
+{
+	struct device_node *np;
+	u32* pp;
+	
+	np = find_devices("gpio");
+	if (!np)
+		return -ENODEV;
 
-static ssize_t pmac_ctx_u8(const u_char *userPtr, size_t userCount,
-			   u_char frame[], ssize_t *frameUsed,
-			   ssize_t frameLeft)
-{
-	unsigned int *p = (unsigned int *) &frame[*frameUsed];
-	unsigned int data = expand_data;
-	int bal = expand_bal;
-	int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
-	int stereo = dmasound.soft.stereo;
-	int utotal, ftotal;
-
-	frameLeft >>= 2;
-	if (stereo)
-		userCount >>= 1;
-	ftotal = frameLeft;
-	utotal = userCount;
-	while (frameLeft) {
-		u_char c;
-		if (bal < 0) {
-			if (userCount == 0)
+	np = np->child;
+	while(np != 0) {
+		if (name) {
+			char *property = get_property(np,"audio-gpio",NULL);
+			if (property != 0 && strcmp(property,name) == 0)
 				break;
-			if (get_user(c, userPtr++))
-				return -EFAULT;
-			data = (c ^ 0x80) << 8;
-			if (stereo) {
-				if (get_user(c, userPtr++))
-					return -EFAULT;
-				data = (data << 16) + ((c ^ 0x80) << 8);
-			} else
-				data = (data << 16) + data;
-			userCount--;
-			bal += hSpeed;
-		}
-		*p++ = data;
-		frameLeft--;
-		bal -= sSpeed;
-	}
-	expand_bal = bal;
-	expand_data = data;
-	*frameUsed += (ftotal - frameLeft) * 4;
-	utotal -= userCount;
-	return stereo? utotal * 2: utotal;
+		} else if (compatible && device_is_compatible(np, compatible))
+			break;
+		np = np->sibling;
+	}
+	if (!np)
+		return -ENODEV;
+	pp = (u32 *)get_property(np, "AAPL,address", NULL);
+	if (!pp)
+		return -ENODEV;
+	*gpio_addr = (*pp) & 0x0000ffff;
+	pp = (u32 *)get_property(np, "audio-gpio-active-state", NULL);
+	if (pp)
+		*gpio_pol = *pp;
+	else
+		*gpio_pol = 1;
+	if (np->n_intrs > 0)
+		return np->intrs[0].line;
+	
+	return 0;
 }
 
-
-static ssize_t pmac_ctx_s16(const u_char *userPtr, size_t userCount,
-			    u_char frame[], ssize_t *frameUsed,
-			    ssize_t frameLeft)
-{
-	unsigned int *p = (unsigned int *) &frame[*frameUsed];
-	unsigned int data = expand_data;
-	unsigned short *up = (unsigned short *) userPtr;
-	int bal = expand_bal;
-	int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
-	int stereo = dmasound.soft.stereo;
-	int utotal, ftotal;
-
-	frameLeft >>= 2;
-	userCount >>= (stereo? 2: 1);
-	ftotal = frameLeft;
-	utotal = userCount;
-	while (frameLeft) {
-		unsigned short c;
-		if (bal < 0) {
-			if (userCount == 0)
-				break;
-			if (get_user(data, up++))
-				return -EFAULT;
-			if (stereo) {
-				if (get_user(c, up++))
-					return -EFAULT;
-				data = (data << 16) + c;
-			} else
-				data = (data << 16) + data;
-			userCount--;
-			bal += hSpeed;
-		}
-		*p++ = data;
-		frameLeft--;
-		bal -= sSpeed;
-	}
-	expand_bal = bal;
-	expand_data = data;
-	*frameUsed += (ftotal - frameLeft) * 4;
-	utotal -= userCount;
-	return stereo? utotal * 4: utotal * 2;
+static inline void
+write_audio_gpio(int gpio_addr, int data)
+{
+	if (!gpio_addr)
+		return;
+	pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, gpio_addr, data ? 0x05 : 0x04);
 }
 
+static inline int
+read_audio_gpio(int gpio_addr)
+{
+	if (!gpio_addr)
+		return 0;
+	return ((pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, gpio_addr, 0) & 0x02) !=0);
+}
 
-static ssize_t pmac_ctx_u16(const u_char *userPtr, size_t userCount,
-			    u_char frame[], ssize_t *frameUsed,
-			    ssize_t frameLeft)
-{
-	int mask = (dmasound.soft.format == AFMT_U16_LE? 0x0080: 0x8000);
-	unsigned int *p = (unsigned int *) &frame[*frameUsed];
-	unsigned int data = expand_data;
-	unsigned short *up = (unsigned short *) userPtr;
-	int bal = expand_bal;
-	int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
-	int stereo = dmasound.soft.stereo;
-	int utotal, ftotal;
-
-	frameLeft >>= 2;
-	userCount >>= (stereo? 2: 1);
-	ftotal = frameLeft;
-	utotal = userCount;
-	while (frameLeft) {
-		unsigned short c;
-		if (bal < 0) {
-			if (userCount == 0)
-				break;
-			if (get_user(data, up++))
-				return -EFAULT;
-			data ^= mask;
-			if (stereo) {
-				if (get_user(c, up++))
-					return -EFAULT;
-				data = (data << 16) + (c ^ mask);
-			} else
-				data = (data << 16) + data;
-			userCount--;
-			bal += hSpeed;
-		}
-		*p++ = data;
-		frameLeft--;
-		bal -= sSpeed;
-	}
-	expand_bal = bal;
-	expand_data = data;
-	*frameUsed += (ftotal - frameLeft) * 4;
-	utotal -= userCount;
-	return stereo? utotal * 4: utotal * 2;
-}
-
-static ssize_t pmac_ct_s8_read(const u_char *userPtr, size_t userCount,
-			  u_char frame[], ssize_t *frameUsed,
-			  ssize_t frameLeft)
-{
-	ssize_t count, used;
-	short *p = (short *) &frame[*frameUsed];
-	int val, stereo = dmasound.soft.stereo;
-
-	frameLeft >>= 2;
-	if (stereo)
-		userCount >>= 1;
-	used = count = min_t(unsigned long, userCount, frameLeft);
-	while (count > 0) {
-		u_char data;
-
-		val = *p++;
-		data = val >> 8;
-		if (put_user(data, (u_char *)userPtr++))
-			return -EFAULT;
-		if (stereo) {
-			val = *p;
-			data = val >> 8;
-			if (put_user(data, (u_char *)userPtr++))
-				return -EFAULT;
-		}
-		p++;
-		count--;
-	}
-	*frameUsed += used * 4;
-	return stereo? used * 2: used;
-}
-
-
-static ssize_t pmac_ct_u8_read(const u_char *userPtr, size_t userCount,
-			  u_char frame[], ssize_t *frameUsed,
-			  ssize_t frameLeft)
-{
-	ssize_t count, used;
-	short *p = (short *) &frame[*frameUsed];
-	int val, stereo = dmasound.soft.stereo;
-
-	frameLeft >>= 2;
-	if (stereo)
-		userCount >>= 1;
-	used = count = min_t(unsigned long, userCount, frameLeft);
-	while (count > 0) {
-		u_char data;
-
-		val = *p++;
-		data = (val >> 8) ^ 0x80;
-		if (put_user(data, (u_char *)userPtr++))
-			return -EFAULT;
-		if (stereo) {
-			val = *p;
-			data = (val >> 8) ^ 0x80;
-			if (put_user(data, (u_char *)userPtr++))
-				return -EFAULT;
-		}
-		p++;
-		count--;
-	}
-	*frameUsed += used * 4;
-	return stereo? used * 2: used;
-}
-
-
-static ssize_t pmac_ct_s16_read(const u_char *userPtr, size_t userCount,
-			   u_char frame[], ssize_t *frameUsed,
-			   ssize_t frameLeft)
-{
-	ssize_t count, used;
-	int stereo = dmasound.soft.stereo;
-	short *fp = (short *) &frame[*frameUsed];
-
-	frameLeft >>= 2;
-	userCount >>= (stereo? 2: 1);
-	used = count = min_t(unsigned long, userCount, frameLeft);
-	if (!stereo) {
-		short *up = (short *) userPtr;
-		while (count > 0) {
-			short data;
-			data = *fp;
-			if (put_user(data, up++))
-				return -EFAULT;
-			fp+=2;
-			count--;
-		}
+static void
+headphone_intr(int irq, void *devid, struct pt_regs *regs)
+{
+	if (read_audio_gpio(gpio_headphone_detect) == gpio_headphone_detect_pol) {
+		printk(KERN_INFO "Audio jack plugged, muting speakers.\n");
+		write_audio_gpio(gpio_amp_mute, gpio_amp_mute_pol);
+		write_audio_gpio(gpio_headphone_mute, !gpio_headphone_mute_pol);
 	} else {
-		if (copy_to_user((u_char *)userPtr, fp, count * 4))
-			return -EFAULT;
+		printk(KERN_INFO "Audio jack unplugged, enabling speakers.\n");
+		write_audio_gpio(gpio_amp_mute, !gpio_amp_mute_pol);
+		write_audio_gpio(gpio_headphone_mute, gpio_headphone_mute_pol);
 	}
-	*frameUsed += used * 4;
-	return stereo? used * 4: used * 2;
 }
 
-static ssize_t pmac_ct_u16_read(const u_char *userPtr, size_t userCount,
-			   u_char frame[], ssize_t *frameUsed,
-			   ssize_t frameLeft)
-{
-	ssize_t count, used;
-	int mask = (dmasound.soft.format == AFMT_U16_LE? 0x0080: 0x8000);
-	int stereo = dmasound.soft.stereo;
-	short *fp = (short *) &frame[*frameUsed];
-	short *up = (short *) userPtr;
-
-	frameLeft >>= 2;
-	userCount >>= (stereo? 2: 1);
-	used = count = min_t(unsigned long, userCount, frameLeft);
-	while (count > 0) {
-		int data;
-
-		data = *fp++;
-		data ^= mask;
-		if (put_user(data, up++))
-			return -EFAULT;
-		if (stereo) {
-			data = *fp;
-			data ^= mask;
-			if (put_user(data, up++))
-				return -EFAULT;
-		}
-		fp++;
-		count--;
-	}
-	*frameUsed += used * 4;
-	return stereo? used * 4: used * 2;
+
+/* Initialize tumbler */
+
+static int
+awacs_tumbler_init(void)
+{
+	setup_audio_gpio(
+		"audio-hw-reset",
+		NULL,
+		&gpio_audio_reset,
+		&gpio_audio_reset_pol);
+	setup_audio_gpio(
+		"amp-mute",
+		NULL,
+		&gpio_amp_mute,
+		&gpio_amp_mute_pol);
+	setup_audio_gpio("headphone-mute",
+		NULL,
+		&gpio_headphone_mute,
+		&gpio_headphone_mute_pol);
+	gpio_headphone_irq = setup_audio_gpio(
+		"headphone-detect",
+		NULL,
+		&gpio_headphone_detect,
+		&gpio_headphone_detect_pol);
+	/* Fix some broken OF entries in desktop machines */
+	if (!gpio_headphone_irq)
+		gpio_headphone_irq = setup_audio_gpio(
+			NULL,
+			"keywest-gpio15",
+			&gpio_headphone_detect,
+			&gpio_headphone_detect_pol);
+
+	write_audio_gpio(gpio_audio_reset, gpio_audio_reset_pol);
+	wait_ms(100);
+	write_audio_gpio(gpio_audio_reset, !gpio_audio_reset_pol);
+	wait_ms(100);
+  	if (gpio_headphone_irq) {
+		if (request_irq(gpio_headphone_irq,headphone_intr,0,"Headphone detect",0) < 0) {
+    			printk(KERN_ERR "tumbler: Can't request headphone interrupt\n");
+    			gpio_headphone_irq = 0;
+    		} else {
+			u8 val;
+			/* Activate headphone status interrupts */
+			val = pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, gpio_headphone_detect, 0);
+			pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, gpio_headphone_detect, val | 0x80);
+			/* Trigger it */
+  			headphone_intr(0,0,0);
+  		}
+  	}
+  	if (!gpio_headphone_irq) {
+  		/* Some machine enter this case ? */
+  		printk(KERN_WARNING "tumbler: Headphone detect IRQ not found, enabling all outputs !\n");
+  		write_audio_gpio(gpio_amp_mute, !gpio_amp_mute_pol);
+  		write_audio_gpio(gpio_headphone_mute, !gpio_headphone_mute_pol);
+  	}
+	return 0;
 }
 
 
-static TRANS transAwacsNormal = {
-	ct_ulaw:	pmac_ct_law,
-	ct_alaw:	pmac_ct_law,
-	ct_s8:		pmac_ct_s8,
-	ct_u8:		pmac_ct_u8,
-	ct_s16be:	pmac_ct_s16,
-	ct_u16be:	pmac_ct_u16,
-	ct_s16le:	pmac_ct_s16,
-	ct_u16le:	pmac_ct_u16,
-};
+static int
+awacs_tumbler_cleanup(void)
+{
+	if (gpio_headphone_irq)
+		free_irq(gpio_headphone_irq, 0);
+	return 0;
+}
 
-static TRANS transAwacsExpand = {
-	ct_ulaw:	pmac_ctx_law,
-	ct_alaw:	pmac_ctx_law,
-	ct_s8:		pmac_ctx_s8,
-	ct_u8:		pmac_ctx_u8,
-	ct_s16be:	pmac_ctx_s16,
-	ct_u16be:	pmac_ctx_u16,
-	ct_s16le:	pmac_ctx_s16,
-	ct_u16le:	pmac_ctx_u16,
-};
 
-static TRANS transAwacsNormalRead = {
-	ct_s8:		pmac_ct_s8_read,
-	ct_u8:		pmac_ct_u8_read,
-	ct_s16be:	pmac_ct_s16_read,
-	ct_u16be:	pmac_ct_u16_read,
-	ct_s16le:	pmac_ct_s16_read,
-	ct_u16le:	pmac_ct_u16_read,
-};
+/*** AE - TUMBLER END *********************************************************/
 
-/*** Low level stuff *********************************************************/
 
 
+/*** Low level stuff *********************************************************/
 
 /*
- * PCI PowerMac, with AWACS and DBDMA.
+ * PCI PowerMac, with AWACS, Screamer, Burgundy, DACA or Tumbler and DBDMA.
  */
 
 static void PMacOpen(void)
@@ -827,9 +501,9 @@
 
 static int __init PMacIrqInit(void)
 {
-	if (request_irq(awacs_irq, pmac_awacs_intr, 0, "AWACS", 0)
-	    || request_irq(awacs_tx_irq, pmac_awacs_tx_intr, 0, "AWACS out", 0)
-	    || request_irq(awacs_rx_irq, pmac_awacs_rx_intr, 0, "AWACS in", 0))
+	if (request_irq(awacs_irq, pmac_awacs_intr, 0, "Built-in Sound misc", 0)
+	    || request_irq(awacs_tx_irq, pmac_awacs_tx_intr, 0, "Built-in Sound out", 0)
+	    || request_irq(awacs_rx_irq, pmac_awacs_rx_intr, 0, "Built-in Sound in", 0))
 		return 0;
 	return 1;
 }
@@ -837,25 +511,44 @@
 #ifdef MODULE
 static void PMacIrqCleanup(void)
 {
-	/* turn off output dma */
-	out_le32(&awacs_txdma->control, RUN<<16);
+	/* turn off input & output dma */
+	DBDMA_DO_STOP(awacs_txdma);
+	DBDMA_DO_STOP(awacs_rxdma);
+
 	/* disable interrupts from awacs interface */
 	out_le32(&awacs->control, in_le32(&awacs->control) & 0xfff);
-#ifdef CONFIG_PMAC_PBOOK
-	if (is_pbook_G3) {
-		feature_clear(awacs_node, FEATURE_Sound_power);
-		feature_clear(awacs_node, FEATURE_Sound_CLK_enable);
+
+	/* Switch off the sound clock */
+	pmac_call_feature(PMAC_FTR_SOUND_CHIP_ENABLE, awacs_node, 0, 0);
+	/* Make sure proper bits are set on pismo & tipb */
+	if (machine_is_compatible("PowerBook3,1") ||
+	    machine_is_compatible("PowerBook3,2")) {
+		awacs_reg[1] |= MASK_PAROUT0 | MASK_PAROUT1;
+		awacs_write(MASK_ADDR1 | awacs_reg[1]);
+		wait_ms(200);
 	}
-#endif
 	free_irq(awacs_irq, 0);
 	free_irq(awacs_tx_irq, 0);
 	free_irq(awacs_rx_irq, 0);
-	kfree(awacs_tx_cmd_space);
+	/* all OF versions I've seen use this value */
+	iounmap((void *)awacs);
+	iounmap((void *)awacs_txdma);
+	iounmap((void *)awacs_rxdma);
+
+	release_OF_resource(awacs_node, 0);
+	release_OF_resource(awacs_node, 1);
+	release_OF_resource(awacs_node, 2);
+
+	if (awacs_tx_cmd_space)
+		kfree(awacs_tx_cmd_space);
 	if (awacs_rx_cmd_space)
 		kfree(awacs_rx_cmd_space);
-	if (beep_buf)
+	if (beep_dbdma_cmd_space)
+		kfree(beep_dbdma_cmd_space);
+	if (beep_buf) {
 		kfree(beep_buf);
-	kd_mksound = orig_mksound;
+		kd_mksound = orig_mksound;
+	}
 #ifdef CONFIG_PMAC_PBOOK
 	pmu_unregister_sleep_notifier(&awacs_sleep_notifier);
 #endif
@@ -865,7 +558,32 @@
 static void PMacSilence(void)
 {
 	/* turn off output dma */
-	out_le32(&awacs_txdma->control, RUN<<16);
+	DBDMA_DO_STOP(awacs_txdma);
+}
+
+static int tumbler_freqs[2] = { 48000, 44100 } ;
+static int tumbler_freqs_ok[2] = { 1, 1 } ;
+
+/* don't know what to do really - just have to leave it where
+ * OF left things
+*/
+
+static int tumbler_set_frame_rate(void)
+{
+	dmasound.hard.speed = 44100 ;
+	awacs_rate_index = 0 ;
+	return 44100 ;
+}
+
+/* don't know what to do really - just have to leave it where
+ * OF left things
+*/
+
+static int daca_set_frame_rate(void)
+{
+	dmasound.hard.speed = 44100 ;
+	awacs_rate_index = 0 ;
+	return 44100 ;
 }
 
 static int awacs_freqs[8] = {
@@ -873,59 +591,115 @@
 };
 static int awacs_freqs_ok[8] = { 1, 1, 1, 1, 1, 1, 1, 1 };
 
-static void PMacInit(void)
+static int awacs_set_frame_rate(int desired, int catch_r)
 {
-	int i, tolerance;
-
-	switch (dmasound.soft.format) {
-	case AFMT_S16_LE:
-	case AFMT_U16_LE:
-		dmasound.hard.format = AFMT_S16_LE;
-		break;
-	default:
-		dmasound.hard.format = AFMT_S16_BE;
-		break;
-	}
-	dmasound.hard.stereo = 1;
-	dmasound.hard.size = 16;
-
+	int tolerance, i = 8 ;
 	/*
 	 * If we have a sample rate which is within catchRadius percent
 	 * of the requested value, we don't have to expand the samples.
 	 * Otherwise choose the next higher rate.
-	 * N.B.: burgundy awacs (iMac and later) only works at 44100 Hz.
+	 * N.B.: burgundy awacs only works at 44100 Hz.
 	 */
-	i = 8;
 	do {
-		tolerance = catchRadius * awacs_freqs[--i] / 100;
+		tolerance = catch_r * awacs_freqs[--i] / 100;
 		if (awacs_freqs_ok[i]
 		    && dmasound.soft.speed <= awacs_freqs[i] + tolerance)
 			break;
 	} while (i > 0);
-	if (dmasound.soft.speed >= awacs_freqs[i] - tolerance)
-		dmasound.trans_write = &transAwacsNormal;
-	else
-		dmasound.trans_write = &transAwacsExpand;
-	dmasound.trans_read = &transAwacsNormalRead;
 	dmasound.hard.speed = awacs_freqs[i];
 	awacs_rate_index = i;
 
-	/* XXX disable error interrupt on burgundy for now */
-	out_le32(&awacs->control, MASK_IEPC | (i << 8) | 0x11
-		 | (awacs_revision < AWACS_BURGUNDY? MASK_IEE: 0));
+	out_le32(&awacs->control, MASK_IEPC | (i << 8) | 0x11 );
 	awacs_reg[1] = (awacs_reg[1] & ~MASK_SAMPLERATE) | (i << 3);
 	awacs_write(awacs_reg[1] | MASK_ADDR1);
-	out_le32(&awacs->byteswap, dmasound.hard.format != AFMT_S16_BE);
+	return dmasound.hard.speed;
+}
+
+static int burgundy_frame_rates = 1 ;
+static int burgundy_set_frame_rate(void)
+{
+#ifdef DEBUG_DMASOUND
+if (burgundy_frame_rates > 1)
+	printk("dmasound_pmac: warning Burgundy had more than one frame rate\n");
+#endif
+	awacs_rate_index = 0 ;
+	awacs_reg[1] = (awacs_reg[1] & ~MASK_SAMPLERATE) ;
+	/* XXX disable error interrupt on burgundy for now */
+	out_le32(&awacs->control, MASK_IEPC | 0 | 0x11 | MASK_IEE);
+	return 44100 ;
+}
+
+static int set_frame_rate(int desired, int catch_r)
+{
+	switch (awacs_revision) {
+		case AWACS_BURGUNDY:
+			dmasound.hard.speed =
+			  burgundy_set_frame_rate();
+			break ;
+		case AWACS_TUMBLER:
+			dmasound.hard.speed =
+			  tumbler_set_frame_rate();
+			break ;
+		case AWACS_DACA:
+			dmasound.hard.speed =
+			  daca_set_frame_rate();
+			break ;
+		default:
+			dmasound.hard.speed =
+			  awacs_set_frame_rate(desired, catch_r);
+			break ;
+	}
+	return dmasound.hard.speed ;
+}
 
-	/* We really want to execute a DMA stop command, after the AWACS
-	 * is initialized.
-	 * For reasons I don't understand, it stops the hissing noise
-	 * common to many PowerBook G3 systems (like mine :-).
+static void
+awacs_recalibrate(void)
+{
+	/* Sorry for the horrible delays... I hope to get that improved
+	 * by making the whole PM process asynchronous in a future version
 	 */
-	out_le32(&awacs_txdma->control, (RUN|WAKE|FLUSH|PAUSE) << 16);
-	st_le16(&beep_dbdma_cmd->command, DBDMA_STOP);
-	out_le32(&awacs_txdma->cmdptr, virt_to_bus(beep_dbdma_cmd));
-	out_le32(&awacs_txdma->control, RUN | (RUN << 16));
+	wait_ms(750);
+	awacs_reg[1] |= MASK_CMUTE | MASK_AMUTE;
+	awacs_write(awacs_reg[1] | MASK_RECALIBRATE | MASK_ADDR1);
+	wait_ms(1000);
+	awacs_write(awacs_reg[1] | MASK_ADDR1);
+}
+
+static void PMacInit(void)
+{
+	int tolerance;
+
+	switch (dmasound.soft.format) {
+	    case AFMT_S16_LE:
+	    case AFMT_U16_LE:
+		if (hw_can_byteswap)
+			dmasound.hard.format = AFMT_S16_LE;
+		else
+			dmasound.hard.format = AFMT_S16_BE;
+		break;
+	default:
+		dmasound.hard.format = AFMT_S16_BE;
+		break;
+	}
+	dmasound.hard.stereo = 1;
+	dmasound.hard.size = 16;
+
+	/* set dmasound.hard.speed - on the basis of what we want (soft)
+	 * and the tolerance we'll allow.
+	*/
+	set_frame_rate(dmasound.soft.speed, catchRadius) ;
+
+	tolerance = (catchRadius * dmasound.hard.speed) / 100;
+	if (dmasound.soft.speed >= dmasound.hard.speed - tolerance)
+		dmasound.trans_write = &transAwacsNormal;
+	else
+		dmasound.trans_write = &transAwacsExpand;
+	dmasound.trans_read = &transAwacsNormalRead;
+
+	if (hw_can_byteswap && (dmasound.hard.format == AFMT_S16_LE))
+		out_le32(&awacs->byteswap, BS_VAL);
+	else
+		out_le32(&awacs->byteswap, 0);
 
 	expand_bal = -dmasound.soft.speed;
 }
@@ -933,7 +707,8 @@
 static int PMacSetFormat(int format)
 {
 	int size;
-
+	int req_format = format;
+		
 	switch (format) {
 	case AFMT_QUERY:
 		return dmasound.soft.format;
@@ -943,10 +718,16 @@
 	case AFMT_S8:
 		size = 8;
 		break;
-	case AFMT_S16_BE:
-	case AFMT_U16_BE:
 	case AFMT_S16_LE:
+		if(!hw_can_byteswap)
+			format = AFMT_S16_BE;
+	case AFMT_S16_BE:
+		size = 16;
+		break;
 	case AFMT_U16_LE:
+		if(!hw_can_byteswap)
+			format = AFMT_U16_BE;
+	case AFMT_U16_BE:
 		size = 16;
 		break;
 	default: /* :-) */
@@ -955,16 +736,16 @@
 		size = 8;
 		format = AFMT_U8;
 	}
-
-	dmasound.soft.format = format;
-	dmasound.soft.size = size;
-	if (dmasound.minDev == SND_DEV_DSP) {
-		dmasound.dsp.format = format;
-		dmasound.dsp.size = size;
+	
+	if (req_format == format) {
+		dmasound.soft.format = format;
+		dmasound.soft.size = size;
+		if (dmasound.minDev == SND_DEV_DSP) {
+			dmasound.dsp.format = format;
+			dmasound.dsp.size = size;
+		}
 	}
 
-	PMacInit();
-
 	return format;
 }
 
@@ -1007,48 +788,91 @@
 	return awacs_volume_setter(volume, 2, MASK_AMUTE, 6);
 }
 
-static void PMacPlay(void)
+static void __PMacPlay(void)
 {
 	volatile struct dbdma_cmd *cp;
-	int i, count;
+	int next_frg, count;
 	unsigned long flags;
 
+	/* CHECK: how much of this *really* needs IRQs masked? */
+
 	save_flags(flags); cli();
+	count = 300 ; /* > two cycles at the lowest sample rate */
+
+	/* what we want to send next */
+	next_frg = (write_sq.front + write_sq.active) % write_sq.max_count;
+
 	if (awacs_beep_state) {
 		/* sound takes precedence over beeps */
+		/* stop the dma channel */
 		out_le32(&awacs_txdma->control, (RUN|PAUSE|FLUSH|WAKE) << 16);
+		while ( (in_le32(&awacs_txdma->status) & RUN) && count--)
+			udelay(1);
+		/* FIXME: check that this is OK for other chip sets */
 		out_le32(&awacs->control,
 			 (in_le32(&awacs->control) & ~0x1f00)
 			 | (awacs_rate_index << 8));
-		out_le32(&awacs->byteswap, dmasound.hard.format != AFMT_S16_BE);
-		out_le32(&awacs_txdma->cmdptr, virt_to_bus(&(awacs_tx_cmds[(write_sq.front+write_sq.active) % write_sq.max_count])));
+
+		if (hw_can_byteswap && (dmasound.hard.format == AFMT_S16_LE))
+			out_le32(&awacs->byteswap, BS_VAL);
+		else
+			out_le32(&awacs->byteswap, 0);
+		out_le32(&awacs_txdma->cmdptr,
+			 virt_to_bus(&(awacs_tx_cmds[next_frg])));
 
 		beep_playing = 0;
 		awacs_beep_state = 0;
 	}
-	i = write_sq.front + write_sq.active;
-	if (i >= write_sq.max_count)
-		i -= write_sq.max_count;
+	/* this won't allow more than two frags to be in the output queue at
+	   once. (or one, if the max frags is 2 - because count can't exceed
+	   2 in that case)
+	*/
 	while (write_sq.active < 2 && write_sq.active < write_sq.count) {
-		count = (write_sq.count == write_sq.active + 1)?write_sq.rear_size:write_sq.block_size;
-		if (count < write_sq.block_size && !write_sq.syncing)
-			/* last block not yet filled, and we're not syncing. */
-			break;
-		cp = &awacs_tx_cmds[i];
+		count = (write_sq.count == write_sq.active + 1) ?
+				write_sq.rear_size:write_sq.block_size ;
+		if (count < write_sq.block_size) {
+			if (!write_sq.syncing) /* last block not yet filled,*/
+				break; 	/* and we're not syncing or POST-ed */
+			else {
+				/* pretend the block is full to force a new
+				   block to be started on the next write */
+				write_sq.rear_size = write_sq.block_size ;
+				write_sq.syncing &= ~2 ; /* clear POST */
+			}
+		}
+		cp = &awacs_tx_cmds[next_frg];
 		st_le16(&cp->req_count, count);
 		st_le16(&cp->xfer_status, 0);
-		if (++i >= write_sq.max_count)
-			i = 0;
-		out_le16(&awacs_tx_cmds[i].command, DBDMA_STOP);
-		out_le16(&cp->command, OUTPUT_MORE + INTR_ALWAYS);
+		st_le16(&cp->command, OUTPUT_MORE + INTR_ALWAYS);
+		/* put a STOP at the end of the queue - but only if we have
+		   space for it.  This means that, if we under-run and we only
+		   have two fragments, we might re-play sound from an existing
+		   queued frag.  I guess the solution to that is not to set two
+		   frags if you are likely to under-run...
+		*/
+		if (write_sq.count < write_sq.max_count) {
+			if (++next_frg >= write_sq.max_count)
+				next_frg = 0 ; /* wrap */
+			/* if we get here then we've underrun so we will stop*/
+			st_le16(&awacs_tx_cmds[next_frg].command, DBDMA_STOP);
+		}
+		/* set the dbdma controller going, if it is not already */
 		if (write_sq.active == 0)
 			out_le32(&awacs_txdma->cmdptr, virt_to_bus(cp));
+		(void)in_le32(&awacs_txdma->status);
 		out_le32(&awacs_txdma->control, ((RUN|WAKE) << 16) + (RUN|WAKE));
 		++write_sq.active;
 	}
 	restore_flags(flags);
 }
 
+static void PMacPlay(void)
+{
+	LOCK();
+	if (!awacs_sleeping)
+		__PMacPlay();
+	UNLOCK();
+}
 
 static void PMacRecord(void)
 {
@@ -1067,6 +891,26 @@
 	restore_flags(flags);
 }
 
+/* if the TX status comes up "DEAD" - reported on some Power Computing machines
+   we need to re-start the dbdma - but from a different physical start address
+   and with a different transfer length.  It would get very messy to do this
+   with the normal dbdma_cmd blocks - we would have to re-write the buffer start
+   addresses each time.  So, we will keep a single dbdma_cmd block which can be
+   fiddled with.
+   When DEAD status is first reported the content of the faulted dbdma block is
+   copied into the emergency buffer and we note that the buffer is in use.
+   we then bump the start physical address by the amount that was successfully
+   output before it died.
+   On any subsequent DEAD result we just do the bump-ups (we know that we are
+   already using the emergency dbdma_cmd).
+   CHECK: this just tries to "do it".  It is possible that we should abandon
+   xfers when the number of residual bytes gets below a certain value - I can
+   see that this might cause a loop-forever if too small a transfer causes
+   DEAD status.  However this is a TODO for now - we'll see what gets reported.
+   When we get a successful transfer result with the emergency buffer we just
+   pretend that it completed using the original dmdma_cmd and carry on.  The
+   'next_cmd' field will already point back to the original loop of blocks.
+*/
 
 static void
 pmac_awacs_tx_intr(int irq, void *devid, struct pt_regs *regs)
@@ -1074,35 +918,92 @@
 	int i = write_sq.front;
 	int stat;
 	volatile struct dbdma_cmd *cp;
+	/* != 0 when we are dealing with a DEAD xfer */
+	static int emergency_in_use = 0 ;
 
-	while (write_sq.active > 0) {
-		cp = &awacs_tx_cmds[i];
+	while (write_sq.active > 0) { /* we expect to have done something*/
+		if (emergency_in_use) /* we are dealing with DEAD xfer */
+			cp = emergency_dbdma_cmd ;
+		else
+			cp = &awacs_tx_cmds[i];
 		stat = ld_le16(&cp->xfer_status);
+		if (stat & DEAD) {
+			unsigned short req, res ;
+			unsigned int phy ;
+#ifdef DEBUG_DMASOUND
+printk("dmasound_pmac: tx-irq: xfer died - patching it up...\n") ;
+#endif
+			/* to clear DEAD status we must first clear RUN
+			   set it to quiescent to be on the safe side */
+			(void)in_le32(&awacs_txdma->status);
+			out_le32(&awacs_txdma->control,
+				(RUN|PAUSE|FLUSH|WAKE) << 16);
+			write_sq.died++ ;
+			if (!emergency_in_use) { /* new problem */
+				memcpy((void *)emergency_dbdma_cmd, (void *)cp,
+					sizeof(struct dbdma_cmd));
+				emergency_in_use = 1;
+				cp = emergency_dbdma_cmd;
+			}
+			/* now bump the values to reflect the amount
+			   we haven't yet shifted */
+			req = ld_le16(&cp->req_count);
+			res = ld_le16(&cp->res_count);
+			phy = ld_le32(&cp->phy_addr);
+			phy += (req - res);
+			st_le16(&cp->req_count, res);
+			st_le16(&cp->res_count, 0);
+			st_le16(&cp->xfer_status, 0);
+			st_le32(&cp->phy_addr, phy);
+			st_le16(&cp->command, OUTPUT_MORE + INTR_ALWAYS);
+			/* point at our patched up command block */
+			out_le32(&awacs_txdma->cmdptr, virt_to_bus(cp));
+			/* we must re-start the controller */
+			(void)in_le32(&awacs_txdma->status);
+			/* should complete clearing the DEAD status */
+			out_le32(&awacs_txdma->control,
+				((RUN|WAKE) << 16) + (RUN|WAKE));
+			break; /* this block is still going */
+		}
 		if ((stat & ACTIVE) == 0)
 			break;	/* this frame is still going */
+		if (emergency_in_use)
+			emergency_in_use = 0 ; /* done that */
 		--write_sq.count;
 		--write_sq.active;
 		if (++i >= write_sq.max_count)
 			i = 0;
 	}
+
+	/* if we stopped and we were not sync-ing - then we under-ran */
+	if( write_sq.syncing == 0 ){
+		stat = in_le32(&awacs_txdma->status) ;
+		/* we hit the dbdma_stop */
+		if( (stat & ACTIVE) == 0 ) write_sq.xruns++ ;
+	}
+
+	/* if we used some data up then wake the writer to supply some more*/
 	if (i != write_sq.front)
 		WAKE_UP(write_sq.action_queue);
 	write_sq.front = i;
 
-	PMacPlay();
-
-	if (!write_sq.active)
-		WAKE_UP(write_sq.sync_queue);
+	/* but make sure we funnel what we've already got */\
+	 if (!awacs_sleeping)
+		__PMacPlay();
+
+	/* make the wake-on-empty conditional on syncing */
+	if (!write_sq.active && (write_sq.syncing & 1))
+		WAKE_UP(write_sq.sync_queue); /* any time we're empty */
 }
 
 
 static void
 pmac_awacs_rx_intr(int irq, void *devid, struct pt_regs *regs)
 {
-
+	int stat ;
 	/* For some reason on my PowerBook G3, I get one interrupt
 	 * when the interrupt vector is installed (like something is
-	 * pending).  This happens before the dbdma is initialize by
+	 * pending).  This happens before the dbdma is initialized by
 	 * us, so I just check the command pointer and if it is zero,
 	 * just blow it off.
 	 */
@@ -1118,8 +1019,35 @@
 	 * interrupt processing for a long time.  Geeze, I really hope
 	 * this doesn't happen.
 	 */
-	while (awacs_rx_cmds[read_sq.rear].xfer_status) {
+	while ((stat=awacs_rx_cmds[read_sq.rear].xfer_status)) {
 
+		/* if we got a "DEAD" status then just log it for now.
+		   and try to restart dma.
+		   TODO: figure out how best to fix it up
+		*/
+		if (stat & DEAD){
+#ifdef DEBUG_DMASOUND
+printk("dmasound_pmac: rx-irq: DIED - attempting resurection\n");
+#endif
+			/* to clear DEAD status we must first clear RUN
+			   set it to quiescent to be on the safe side */
+			(void)in_le32(&awacs_txdma->status);
+			out_le32(&awacs_txdma->control,
+				(RUN|PAUSE|FLUSH|WAKE) << 16);
+			awacs_rx_cmds[read_sq.rear].xfer_status = 0;
+			awacs_rx_cmds[read_sq.rear].res_count = 0;
+			read_sq.died++ ;
+			(void)in_le32(&awacs_txdma->status);
+			/* re-start the same block */
+			out_le32(&awacs_rxdma->cmdptr,
+				virt_to_bus(&awacs_rx_cmds[read_sq.rear]));
+			/* we must re-start the controller */
+			(void)in_le32(&awacs_rxdma->status);
+			/* should complete clearing the DEAD status */
+			out_le32(&awacs_rxdma->control,
+				((RUN|WAKE) << 16) + (RUN|WAKE));
+			return; /* try this block again */
+		}
 		/* Clear status and move on to next buffer.
 		*/
 		awacs_rx_cmds[read_sq.rear].xfer_status = 0;
@@ -1138,6 +1066,7 @@
 		 */
 		if (read_sq.rear == read_sq.front) {
 			read_sq.front++;
+			read_sq.xruns++ ; /* we overan */
 			if (read_sq.front >= read_sq.max_active)
 				read_sq.front = 0;
 		}
@@ -1157,8 +1086,9 @@
 	}
 	if (ctrl & MASK_CNTLERR) {
 		int err = (in_le32(&awacs->codec_stat) & MASK_ERRCODE) >> 16;
-		if (err != 0 && awacs_revision < AWACS_BURGUNDY)
-			printk(KERN_ERR "AWACS: error %x\n", err);
+		/* CHECK: we just swallow burgundy errors at the moment..*/
+		if (err != 0 && awacs_revision != AWACS_BURGUNDY)
+			printk(KERN_ERR "dmasound_pmac: error %x\n", err);
 	}
 	/* Writing 1s to the CNTLERR and PORTCHG bits clears them... */
 	out_le32(&awacs->control, ctrl);
@@ -1167,25 +1097,38 @@
 static void
 awacs_write(int val)
 {
-	if (awacs_revision >= AWACS_BURGUNDY)
-		return;
-	while (in_le32(&awacs->codec_ctrl) & MASK_NEWECMD)
-		;	/* XXX should have timeout */
+	int count = 300 ;
+	if (awacs_revision >= AWACS_DACA)
+		return ;
+
+	while ((in_le32(&awacs->codec_ctrl) & MASK_NEWECMD) && count--)
+		udelay(1) ;	/* timeout is > 2 samples at lowest rate */
 	out_le32(&awacs->codec_ctrl, val | (awacs_subframe << 22));
+	(void)in_le32(&awacs->byteswap);
 }
 
+/* this is called when the beep timer expires... it will be called even
+   if the beep has been overidden by other sound output.
+*/
 static void awacs_nosound(unsigned long xx)
 {
 	unsigned long flags;
+	int count = 600 ; /* > four samples at lowest rate */
 
 	save_flags(flags); cli();
 	if (beep_playing) {
 		st_le16(&beep_dbdma_cmd->command, DBDMA_STOP);
 		out_le32(&awacs_txdma->control, (RUN|PAUSE|FLUSH|WAKE) << 16);
+		while ((in_le32(&awacs_txdma->status) & RUN) && count--)
+			udelay(1);
+		/* FIXME: check this is OK for DACA, Tumbler */
 		out_le32(&awacs->control,
 			 (in_le32(&awacs->control) & ~0x1f00)
 			 | (awacs_rate_index << 8));
-		out_le32(&awacs->byteswap, dmasound.hard.format != AFMT_S16_BE);
+		if (hw_can_byteswap && (dmasound.hard.format == AFMT_S16_LE))
+			out_le32(&awacs->byteswap, BS_VAL);
+		else
+			out_le32(&awacs->byteswap, 0);
 		beep_playing = 0;
 	}
 	restore_flags(flags);
@@ -1195,6 +1138,11 @@
 	function: awacs_nosound
 };
 
+/* we generate the beep with a single dbdma command that loops a buffer
+   forever - without generating interrupts.
+   So, to stop it you have to stop dma output as per awacs_nosound.
+*/
+
 static void awacs_mksound(unsigned int hz, unsigned int ticks)
 {
 	unsigned long flags;
@@ -1207,11 +1155,19 @@
 	static int beep_nsamples_cache;
 	static int beep_volume_cache;
 
-	for (i = 0; i < 8 && awacs_freqs[i] >= BEEP_SRATE; ++i)
-		if (awacs_freqs_ok[i])
-			beep_speed = i;
-	srate = awacs_freqs[beep_speed];
+	if (beep_buf == NULL)
+		return;
+
+	/* quick-hack fix for DACA, Burgundy & Tumbler */
 
+	if (awacs_revision >= AWACS_DACA){
+		srate = 44100 ;
+	} else {
+		for (i = 0; i < 8 && awacs_freqs[i] >= BEEP_SRATE; ++i)
+			if (awacs_freqs_ok[i])
+				beep_speed = i;
+		srate = awacs_freqs[beep_speed];
+	}
 	if (hz <= srate / BEEP_BUFLEN || hz > srate / 2) {
 #if 1
 		/* this is a hack for broken X server code */
@@ -1237,7 +1193,7 @@
 	st_le16(&beep_dbdma_cmd->command, OUTPUT_MORE + BR_ALWAYS);
 	restore_flags(flags);
 
-	if (hz == beep_hz_cache && beep_volume == beep_volume_cache) {
+	if (hz == beep_hz_cache && beep_vol == beep_volume_cache) {
 		nsamples = beep_nsamples_cache;
 	} else {
 		period = srate * 256 / hz;	/* fixed point */
@@ -1247,11 +1203,11 @@
 		j = 0;
 		p = beep_buf;
 		for (i = 0; i < nsamples; ++i, p += 2) {
-			p[0] = p[1] = beep_wform[j >> 8] * beep_volume;
+			p[0] = p[1] = beep_wform[j >> 8] * beep_vol;
 			j = (j + f) & 0xffff;
 		}
 		beep_hz_cache = hz;
-		beep_volume_cache = beep_volume;
+		beep_volume_cache = beep_vol;
 		beep_nsamples_cache = nsamples;
 	}
 
@@ -1263,78 +1219,157 @@
 
 	save_flags(flags); cli();
 	if (beep_playing) {	/* i.e. haven't been terminated already */
+		int count = 300 ;
 		out_le32(&awacs_txdma->control, (RUN|WAKE|FLUSH|PAUSE) << 16);
+		while ((in_le32(&awacs_txdma->status) & RUN) && count--)
+			udelay(1); /* timeout > 2 samples at lowest rate*/
+		/* FIXME: check this is OK on DACA, Tumbler */
 		out_le32(&awacs->control,
 			 (in_le32(&awacs->control) & ~0x1f00)
 			 | (beep_speed << 8));
-		out_le32(&awacs->byteswap, 0);
+		out_le32(&awacs->byteswap, 0); /* force BE */
 		out_le32(&awacs_txdma->cmdptr, virt_to_bus(beep_dbdma_cmd));
+		(void)in_le32(&awacs_txdma->status);
 		out_le32(&awacs_txdma->control, RUN | (RUN << 16));
 	}
 	restore_flags(flags);
 }
 
+/* used in init and for wake-up */
+
+static void
+load_awacs(void)
+{
+	awacs_write(awacs_reg[0] + MASK_ADDR0);
+	awacs_write(awacs_reg[1] + MASK_ADDR1);
+	awacs_write(awacs_reg[2] + MASK_ADDR2);
+	awacs_write(awacs_reg[4] + MASK_ADDR4);
+
+	if (awacs_revision == AWACS_SCREAMER) {
+		awacs_write(awacs_reg[5] + MASK_ADDR5);
+		wait_ms(100);
+		awacs_write(awacs_reg[6] + MASK_ADDR6);
+		wait_ms(2);
+		awacs_write(awacs_reg[1] + MASK_ADDR1);
+		awacs_write(awacs_reg[7] + MASK_ADDR7);
+	}
+	if (hw_can_byteswap && (dmasound.hard.format == AFMT_S16_LE))
+		out_le32(&awacs->byteswap, BS_VAL);
+	else
+		out_le32(&awacs->byteswap, 0);
+}
+
 #ifdef CONFIG_PMAC_PBOOK
 /*
  * Save state when going to sleep, restore it afterwards.
  */
+/* FIXME: sort out disabling/re-enabling of read stuff as well */
 static int awacs_sleep_notify(struct pmu_sleep_notifier *self, int when)
 {
 	switch (when) {
-	case PBOOK_SLEEP_NOW:
-		/* XXX we should stop any dma in progress when going to sleep
-		   and restart it when we wake. */
+	case PBOOK_SLEEP_NOW:		
+		LOCK();
+		awacs_sleeping = 1;
+		/* Tell the rest of the driver we are now going to sleep */
+		mb();
+		if (awacs_revision == AWACS_SCREAMER ||
+		    awacs_revision == AWACS_AWACS) {
+			awacs_reg1_save = awacs_reg[1];
+			awacs_reg[1] |= MASK_AMUTE | MASK_CMUTE;
+			awacs_write(MASK_ADDR1 | awacs_reg[1]);
+		}
+
 		PMacSilence();
+		/* stop rx - if going - a bit of a daft user... but */
+		out_le32(&awacs_rxdma->control, (RUN|WAKE|FLUSH << 16));
+		/* deny interrupts */
+		switch (awacs_revision) {
+			case AWACS_TUMBLER:
+				break ;		/* dont know how yet */
+			case AWACS_DACA:
+				daca_enter_sleep();
+				break ;
+			case AWACS_BURGUNDY:
+				break ;
+			case AWACS_SCREAMER:
+			case AWACS_AWACS:
+			default:
+				out_le32(&awacs->control, 0x11) ;
+				break ;
+		}
 		disable_irq(awacs_irq);
 		disable_irq(awacs_tx_irq);
-		if (is_pbook_G3) {
-			feature_clear(awacs_node, FEATURE_Sound_CLK_enable);
-			feature_clear(awacs_node, FEATURE_Sound_power);
+		disable_irq(awacs_rx_irq);
+		/* Disable sound clock */
+		pmac_call_feature(PMAC_FTR_SOUND_CHIP_ENABLE, awacs_node, 0, 0);
+		/* According to Darwin, we do that after turning off the sound
+		 * chip clock. All this will have to be cleaned up once we properly
+		 * parse the OF sound-objects
+		 */
+		if (machine_is_compatible("PowerBook3,1") ||
+		    machine_is_compatible("PowerBook3,2")) {
+			awacs_reg[1] |= MASK_PAROUT0 | MASK_PAROUT1;
+			awacs_write(MASK_ADDR1 | awacs_reg[1]);
+			wait_ms(200);
 		}
 		break;
 	case PBOOK_WAKE:
-		/* There is still a problem on wake. Sound seems to work fine
-		   if I launch mpg123 and resumes fine if mpg123 was playing,
-		   but the console beep is dead until I do something with the
-		   mixer. Probably yet another timing issue */
-		if (!feature_test(awacs_node, FEATURE_Sound_CLK_enable)
-		    || !feature_test(awacs_node, FEATURE_Sound_power)) {
-			/* these aren't present on the 3400 AFAIK -- paulus */
-			feature_set(awacs_node, FEATURE_Sound_CLK_enable);
-			feature_set(awacs_node, FEATURE_Sound_power);
-			mdelay(1000);
-		}
-		out_le32(&awacs->control, MASK_IEPC
-			 | (awacs_rate_index << 8) | 0x11
-			 | (awacs_revision < AWACS_BURGUNDY? MASK_IEE: 0));
-		awacs_write(awacs_reg[0] | MASK_ADDR0);
-		awacs_write(awacs_reg[1] | MASK_ADDR1);
-		awacs_write(awacs_reg[2] | MASK_ADDR2);
-		awacs_write(awacs_reg[4] | MASK_ADDR4);
-		if (awacs_is_screamer) {
-			awacs_write(awacs_reg[5] + MASK_ADDR5);
-			awacs_write(awacs_reg[6] + MASK_ADDR6);
-			awacs_write(awacs_reg[7] + MASK_ADDR7);
-		}
-		out_le32(&awacs->byteswap, dmasound.hard.format != AFMT_S16_BE);
+		/* Enable sound clock */
+		pmac_call_feature(PMAC_FTR_SOUND_CHIP_ENABLE, awacs_node, 0, 1);
+		if (machine_is_compatible("PowerBook3,1") ||
+		    machine_is_compatible("PowerBook3,2")) {
+			wait_ms(100);
+			awacs_reg[1] &= ~(MASK_PAROUT0 | MASK_PAROUT1);
+			awacs_write(MASK_ADDR1 | awacs_reg[1]);
+			wait_ms(300);
+		} else
+			wait_ms(1000);
+ 		/* restore settings */
+		switch (awacs_revision) {
+			case AWACS_TUMBLER:
+				headphone_intr(0,0,0);
+				break;
+			case AWACS_DACA:
+				wait_ms(10); /* Check this !!! */
+				daca_leave_sleep();
+				break ;		/* dont know how yet */
+			case AWACS_BURGUNDY:
+				break ;
+			case AWACS_SCREAMER:
+			case AWACS_AWACS:
+			default:
+		 		load_awacs() ;
+				break ;
+		}
+		/* Recalibrate chip */
+		if (awacs_revision == AWACS_SCREAMER)
+			awacs_recalibrate();
+		/* Make sure dma is stopped */
+		PMacSilence();
 		enable_irq(awacs_irq);
 		enable_irq(awacs_tx_irq);
-		if (awacs_revision == 3) {
-			mdelay(100);
-			awacs_write(0x6000);
-			mdelay(2);
-			awacs_write(awacs_reg[1] | MASK_ADDR1);
-		}
-		/* enable CD sound input */
-		if (macio_base && is_pbook_G3) {
+ 		enable_irq(awacs_rx_irq);
+ 		/* OK, allow ints back again */
+ 		out_le32(&awacs->control, MASK_IEPC
+ 		 	| (awacs_rate_index << 8) | 0x11
+ 			 | (awacs_revision < AWACS_DACA ? MASK_IEE: 0));
+ 		if (macio_base && is_pbook_g3) {
+			/* FIXME: should restore the setup we had...*/
 			out_8(macio_base + 0x37, 3);
-		} else if (is_pbook_3400) {
-			feature_set(awacs_node, FEATURE_IOBUS_enable);
-			udelay(10);
+ 		} else if (is_pbook_3X00) {
 			in_8(latch_base + 0x190);
 		}
+		/* Remove mute */
+		if (awacs_revision == AWACS_SCREAMER ||
+		    awacs_revision == AWACS_AWACS) {
+			awacs_reg[1] = awacs_reg1_save;
+			awacs_write(MASK_ADDR1 | awacs_reg[1]);
+		}
+ 		awacs_sleeping = 0;
 		/* Resume pending sounds. */
-		PMacPlay();
+		/* we don't try to restart input... */
+		__PMacPlay();
+		UNLOCK();
 	}
 	return PBOOK_SLEEP_OK;
 }
@@ -1347,17 +1382,20 @@
 inline static void
 awacs_burgundy_busy_wait(void)
 {
-	while (in_le32(&awacs->codec_ctrl) & MASK_NEWECMD)
-		;
+	int count = 50; /* > 2 samples at 44k1 */
+	while ((in_le32(&awacs->codec_ctrl) & MASK_NEWECMD) && count--)
+		udelay(1) ;
 }
 
 inline static void
 awacs_burgundy_extend_wait(void)
 {
-	while (!(in_le32(&awacs->codec_stat) & MASK_EXTEND))
-		;
-	while (in_le32(&awacs->codec_stat) & MASK_EXTEND)
-		;
+	int count = 50 ; /* > 2 samples at 44k1 */
+	while ((!(in_le32(&awacs->codec_stat) & MASK_EXTEND)) && count--)
+		udelay(1) ;
+	count = 50;
+	while ((in_le32(&awacs->codec_stat) & MASK_EXTEND) && count--)
+		udelay(1);
 }
 
 static void
@@ -1447,7 +1485,7 @@
 awacs_burgundy_init(void)
 {
 	if (awacs_burgundy_check()) {
-		printk(KERN_WARNING "AWACS: disabled by MacOS :-(\n");
+		printk(KERN_WARNING "dmasound_pmac: burgundy not working :-(\n");
 		return 1;
 	}
 
@@ -1515,9 +1553,6 @@
 	return softvolume > 0 ? softvolume : 0;
 }
 
-
-
-
 static int
 awacs_burgundy_read_mvolume(unsigned address)
 {
@@ -1533,7 +1568,6 @@
 	return lvolume + (rvolume << 8);
 }
 
-
 static void
 awacs_burgundy_write_mvolume(unsigned address, int volume)
 {
@@ -1550,21 +1584,23 @@
 
 /* End burgundy functions */
 
+/* Set up output volumes on machines with the 'perch/whisper' extension card.
+ * this has an SGS i2c chip (7433) which is accessed using the cuda.
+ *
+ * TODO: split this out and make use of the other parts of the SGS chip to
+ * do Bass, Treble etc.
+ */
 
-
-
-
-/* Turn on sound output, needed on G3 desktop powermacs */
 static void
 awacs_enable_amp(int spkr_vol)
 {
+#ifdef CONFIG_ADB_CUDA
 	struct adb_request req;
 
 	awacs_spkr_vol = spkr_vol;
 	if (sys_ctrler != SYS_CTRLER_CUDA)
 		return;
 
-#ifdef CONFIG_ADB_CUDA
 	/* turn on headphones */
 	cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC,
 		     0x8a, 4, 0);
@@ -1595,146 +1631,229 @@
  * /dev/mixer abstraction
  */
 
-static int awacs_mixer_ioctl(u_int cmd, u_long arg)
+static void do_line_lev(int data)
 {
-	int data;
-
-	switch (cmd) {
-	case SOUND_MIXER_READ_DEVMASK:
-		data = SOUND_MASK_VOLUME | SOUND_MASK_SPEAKER
-			| SOUND_MASK_LINE | SOUND_MASK_MIC
-			| SOUND_MASK_CD | SOUND_MASK_RECLEV
-			| SOUND_MASK_ALTPCM
-			| SOUND_MASK_MONITOR;
-		return IOCTL_OUT(arg, data);
-	case SOUND_MIXER_READ_RECMASK:
-		data = SOUND_MASK_LINE | SOUND_MASK_MIC
-			| SOUND_MASK_CD;
-		return IOCTL_OUT(arg, data);
-	case SOUND_MIXER_READ_RECSRC:
-		data = 0;
-		if (awacs_reg[0] & MASK_MUX_AUDIN)
-			data |= SOUND_MASK_LINE;
-		if (awacs_reg[0] & MASK_MUX_MIC)
-			data |= SOUND_MASK_MIC;
-		if (awacs_reg[0] & MASK_MUX_CD)
-			data |= SOUND_MASK_CD;
-		if (awacs_reg[1] & MASK_LOOPTHRU)
-			data |= SOUND_MASK_MONITOR;
-		return IOCTL_OUT(arg, data);
-	case SOUND_MIXER_WRITE_RECSRC:
-		IOCTL_IN(arg, data);
-		data &= (SOUND_MASK_LINE
-			 | SOUND_MASK_MIC | SOUND_MASK_CD
-			 | SOUND_MASK_MONITOR);
-		awacs_reg[0] &= ~(MASK_MUX_CD | MASK_MUX_MIC
-				  | MASK_MUX_AUDIN);
-		awacs_reg[1] &= ~MASK_LOOPTHRU;
-		if (data & SOUND_MASK_LINE)
+		line_lev = data ;
+		awacs_reg[0] &= ~MASK_MUX_AUDIN;
+		if ((data & 0xff) >= 50)
 			awacs_reg[0] |= MASK_MUX_AUDIN;
-		if (data & SOUND_MASK_MIC)
-			awacs_reg[0] |= MASK_MUX_MIC;
-		if (data & SOUND_MASK_CD)
-			awacs_reg[0] |= MASK_MUX_CD;
-		if (data & SOUND_MASK_MONITOR)
+		awacs_write(MASK_ADDR0 | awacs_reg[0]);
+}
+
+static void do_ip_gain(int data)
+{
+	ip_gain = data ;
+	data &= 0xff;
+	awacs_reg[0] &= ~MASK_GAINLINE;
+	if (awacs_revision == AWACS_SCREAMER) {
+		awacs_reg[6] &= ~MASK_MIC_BOOST ;
+		if (data >= 33) {
+			awacs_reg[0] |= MASK_GAINLINE;
+			if( data >= 66)
+				awacs_reg[6] |= MASK_MIC_BOOST ;
+		}
+		awacs_write(MASK_ADDR6 | awacs_reg[6]) ;
+	} else {
+		if (data >= 50)
+			awacs_reg[0] |= MASK_GAINLINE;
+	}
+	awacs_write(MASK_ADDR0 | awacs_reg[0]);
+}
+
+static void do_mic_lev(int data)
+{
+	mic_lev = data ;
+	data &= 0xff;
+	awacs_reg[0] &= ~MASK_MUX_MIC;
+	if (data >= 50)
+		awacs_reg[0] |= MASK_MUX_MIC;
+	awacs_write(MASK_ADDR0 | awacs_reg[0]);
+}
+
+static void do_cd_lev(int data)
+{
+	cd_lev = data ;
+	awacs_reg[0] &= ~MASK_MUX_CD;
+	if ((data & 0xff) >= 50)
+		awacs_reg[0] |= MASK_MUX_CD;
+	awacs_write(MASK_ADDR0 | awacs_reg[0]);
+}
+
+static void do_rec_lev(int data)
+{
+	int left, right ;
+	rec_lev = data ;
+	/* need to fudge this to use the volume setter routine */
+	left = 100 - (data & 0xff) ; if( left < 0 ) left = 0 ;
+	right = 100 - ((data >> 8) & 0xff) ; if( right < 0 ) right = 0 ;
+	left |= (right << 8 );
+	left = awacs_volume_setter(left, 0, 0, 4);
+}
+
+static void do_passthru_vol(int data)
+{
+	passthru_vol = data ;
+	awacs_reg[1] &= ~MASK_LOOPTHRU;
+	if (awacs_revision == AWACS_SCREAMER) {
+		if( data ) { /* switch it on for non-zero */
 			awacs_reg[1] |= MASK_LOOPTHRU;
+			awacs_write(MASK_ADDR1 | awacs_reg[1]);
+		}
+		data = awacs_volume_setter(data, 5, 0, 6) ;
+	} else {
+		if ((data & 0xff) >= 50)
+			awacs_reg[1] |= MASK_LOOPTHRU;
+		awacs_write(MASK_ADDR1 | awacs_reg[1]);
+		data = (awacs_reg[1] & MASK_LOOPTHRU)? 100: 0;
+	}
+}
+
+static int awacs_mixer_ioctl(u_int cmd, u_long arg)
+{
+	int data;
+	int rc;
+
+	switch (cmd) {
+	case SOUND_MIXER_READ_CAPS:
+		/* say we will allow multiple inputs?  prob. wrong
+			so I'm switching it to single */
+		return IOCTL_OUT(arg, 1);
+	case SOUND_MIXER_READ_DEVMASK:
+		data  = SOUND_MASK_VOLUME | SOUND_MASK_SPEAKER
+			| SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD
+			| SOUND_MASK_IGAIN | SOUND_MASK_RECLEV
+			| SOUND_MASK_ALTPCM
+			| SOUND_MASK_MONITOR;
+		rc = IOCTL_OUT(arg, data);
+		break;
+	case SOUND_MIXER_READ_RECMASK:
+		data = SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD;
+		rc = IOCTL_OUT(arg, data);
+		break;
+	case SOUND_MIXER_READ_RECSRC:
+		data = 0;
+		if (awacs_reg[0] & MASK_MUX_AUDIN)
+			data |= SOUND_MASK_LINE;
+		if (awacs_reg[0] & MASK_MUX_MIC)
+			data |= SOUND_MASK_MIC;
+		if (awacs_reg[0] & MASK_MUX_CD)
+			data |= SOUND_MASK_CD;
+		rc = IOCTL_OUT(arg, data);
+		break;
+	case SOUND_MIXER_WRITE_RECSRC:
+		IOCTL_IN(arg, data);
+		data &= (SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD);
+		awacs_reg[0] &= ~(MASK_MUX_CD | MASK_MUX_MIC
+				  | MASK_MUX_AUDIN);
+		if (data & SOUND_MASK_LINE)
+			awacs_reg[0] |= MASK_MUX_AUDIN;
+		if (data & SOUND_MASK_MIC)
+			awacs_reg[0] |= MASK_MUX_MIC;
+		if (data & SOUND_MASK_CD)
+			awacs_reg[0] |= MASK_MUX_CD;
 		awacs_write(awacs_reg[0] | MASK_ADDR0);
-		awacs_write(awacs_reg[1] | MASK_ADDR1);
-		return IOCTL_OUT(arg, data);
+		rc = IOCTL_OUT(arg, data);
+		break;
 	case SOUND_MIXER_READ_STEREODEVS:
-		data = SOUND_MASK_VOLUME | SOUND_MASK_SPEAKER
-			| SOUND_MASK_RECLEV;
-		return IOCTL_OUT(arg, data);
-	case SOUND_MIXER_READ_CAPS:
-		return IOCTL_OUT(arg, 0);
-	case SOUND_MIXER_READ_VOLUME:
-		data = (awacs_reg[1] & MASK_AMUTE)? 0:
-			awacs_get_volume(awacs_reg[2], 6);
-		return IOCTL_OUT(arg, data);
+		data = SOUND_MASK_VOLUME | SOUND_MASK_SPEAKER| SOUND_MASK_RECLEV  ;
+		if (awacs_revision == AWACS_SCREAMER)
+			data |= SOUND_MASK_MONITOR ;
+		rc = IOCTL_OUT(arg, data);
+		break;
 	case SOUND_MIXER_WRITE_VOLUME:
 		IOCTL_IN(arg, data);
-		return IOCTL_OUT(arg, PMacSetVolume(data));
-	case SOUND_MIXER_READ_SPEAKER:
-		if (awacs_revision == 3
-		    && sys_ctrler == SYS_CTRLER_CUDA)
-			data = awacs_spkr_vol;
-		else
-			data = (awacs_reg[1] & MASK_CMUTE)? 0:
-				awacs_get_volume(awacs_reg[4], 6);
-		return IOCTL_OUT(arg, data);
+		line_vol = data ;
+		awacs_volume_setter(data, 2, 0, 6);
+		/* fall through */
+	case SOUND_MIXER_READ_VOLUME:
+		rc = IOCTL_OUT(arg, line_vol);
+		break;
 	case SOUND_MIXER_WRITE_SPEAKER:
 		IOCTL_IN(arg, data);
-		if (awacs_revision == 3
-		    && sys_ctrler == SYS_CTRLER_CUDA)
+		spk_vol = data ;
+		if (has_perch)
 			awacs_enable_amp(data);
 		else
-			data = awacs_volume_setter(data, 4, MASK_CMUTE, 6);
-		return IOCTL_OUT(arg, data);
+			(void)awacs_volume_setter(data, 4, MASK_CMUTE, 6);
+		/* fall though */
+	case SOUND_MIXER_READ_SPEAKER:
+		rc = IOCTL_OUT(arg, spk_vol);
+		break;
 	case SOUND_MIXER_WRITE_ALTPCM:	/* really bell volume */
 		IOCTL_IN(arg, data);
-		beep_volume = data & 0xff;
-				/* fall through */
+		beep_vol = data & 0xff;
+		/* fall through */
 	case SOUND_MIXER_READ_ALTPCM:
-		return IOCTL_OUT(arg, beep_volume);
+		rc = IOCTL_OUT(arg, beep_vol);
+		break;
 	case SOUND_MIXER_WRITE_LINE:
 		IOCTL_IN(arg, data);
-		awacs_reg[0] &= ~MASK_MUX_AUDIN;
-		if ((data & 0xff) >= 50)
-			awacs_reg[0] |= MASK_MUX_AUDIN;
-		awacs_write(MASK_ADDR0 | awacs_reg[0]);
-				/* fall through */
+		do_line_lev(data) ;
+		/* fall through */
 	case SOUND_MIXER_READ_LINE:
-		data = (awacs_reg[0] & MASK_MUX_AUDIN)? 100: 0;
-		return IOCTL_OUT(arg, data);
+		rc = IOCTL_OUT(arg, line_lev);
+		break;
+	case SOUND_MIXER_WRITE_IGAIN:
+		IOCTL_IN(arg, data);
+		do_ip_gain(data) ;
+		/* fall through */
+	case SOUND_MIXER_READ_IGAIN:
+		rc = IOCTL_OUT(arg, ip_gain);
+		break;
 	case SOUND_MIXER_WRITE_MIC:
 		IOCTL_IN(arg, data);
-		data &= 0xff;
-		awacs_reg[0] &= ~(MASK_MUX_MIC | MASK_GAINLINE);
-		if (data >= 25) {
-			awacs_reg[0] |= MASK_MUX_MIC;
-			if (data >= 75)
-				awacs_reg[0] |= MASK_GAINLINE;
-		}
-		awacs_write(MASK_ADDR0 | awacs_reg[0]);
-				/* fall through */
+		do_mic_lev(data);
+		/* fall through */
 	case SOUND_MIXER_READ_MIC:
-		data = (awacs_reg[0] & MASK_MUX_MIC)?
-			(awacs_reg[0] & MASK_GAINLINE? 100: 50): 0;
-		return IOCTL_OUT(arg, data);
+		rc = IOCTL_OUT(arg, mic_lev);
+		break;
 	case SOUND_MIXER_WRITE_CD:
 		IOCTL_IN(arg, data);
-		awacs_reg[0] &= ~MASK_MUX_CD;
-		if ((data & 0xff) >= 50)
-			awacs_reg[0] |= MASK_MUX_CD;
-		awacs_write(MASK_ADDR0 | awacs_reg[0]);
-				/* fall through */
+		do_cd_lev(data);
+		/* fall through */
 	case SOUND_MIXER_READ_CD:
-		data = (awacs_reg[0] & MASK_MUX_CD)? 100: 0;
-		return IOCTL_OUT(arg, data);
+		rc = IOCTL_OUT(arg, cd_lev);
+		break;
 	case SOUND_MIXER_WRITE_RECLEV:
 		IOCTL_IN(arg, data);
-		data = awacs_volume_setter(data, 0, 0, 4);
-		return IOCTL_OUT(arg, data);
+		do_rec_lev(data) ;
+		/* fall through */
 	case SOUND_MIXER_READ_RECLEV:
-		data = awacs_get_volume(awacs_reg[0], 4);
-		return IOCTL_OUT(arg, data);
+		rc = IOCTL_OUT(arg, rec_lev);
+		break;
 	case MIXER_WRITE(SOUND_MIXER_MONITOR):
 		IOCTL_IN(arg, data);
-		awacs_reg[1] &= ~MASK_LOOPTHRU;
-		if ((data & 0xff) >= 50)
-			awacs_reg[1] |= MASK_LOOPTHRU;
-		awacs_write(MASK_ADDR1 | awacs_reg[1]);
+		do_passthru_vol(data) ;
 		/* fall through */
 	case MIXER_READ(SOUND_MIXER_MONITOR):
-		data = (awacs_reg[1] & MASK_LOOPTHRU)? 100: 0;
-		return IOCTL_OUT(arg, data);
+		rc = IOCTL_OUT(arg, passthru_vol);
+		break;
+	default:
+		rc = -EINVAL;
 	}
-	return -EINVAL;
+	
+	return rc;
+}
+
+static void awacs_mixer_init(void)
+{
+	awacs_volume_setter(line_vol, 2, 0, 6);
+	if (has_perch)
+		awacs_enable_amp(spk_vol);
+	else
+		(void)awacs_volume_setter(spk_vol, 4, MASK_CMUTE, 6);
+	do_line_lev(line_lev) ;
+	do_ip_gain(ip_gain) ;
+	do_mic_lev(mic_lev) ;
+	do_cd_lev(cd_lev) ;
+	do_rec_lev(rec_lev) ;
+	do_passthru_vol(passthru_vol) ;
 }
 
 static int burgundy_mixer_ioctl(u_int cmd, u_long arg)
 {
 	int data;
+	int rc;
 
 	/* We are, we are, we are... Burgundy or better */
 	switch(cmd) {
@@ -1742,11 +1861,13 @@
 		data = SOUND_MASK_VOLUME | SOUND_MASK_CD |
 			SOUND_MASK_LINE | SOUND_MASK_MIC |
 			SOUND_MASK_SPEAKER | SOUND_MASK_ALTPCM;
-		return IOCTL_OUT(arg, data);
+		rc = IOCTL_OUT(arg, data);
+		break;
 	case SOUND_MIXER_READ_RECMASK:
 		data = SOUND_MASK_LINE | SOUND_MASK_MIC
 			| SOUND_MASK_CD;
-		return IOCTL_OUT(arg, data);
+		rc = IOCTL_OUT(arg, data);
+		break;
 	case SOUND_MIXER_READ_RECSRC:
 		data = 0;
 		if (awacs_reg[0] & MASK_MUX_AUDIN)
@@ -1755,7 +1876,8 @@
 			data |= SOUND_MASK_MIC;
 		if (awacs_reg[0] & MASK_MUX_CD)
 			data |= SOUND_MASK_CD;
-		return IOCTL_OUT(arg, data);
+		rc = IOCTL_OUT(arg, data);
+		break;
 	case SOUND_MIXER_WRITE_RECSRC:
 		IOCTL_IN(arg, data);
 		data &= (SOUND_MASK_LINE
@@ -1769,23 +1891,26 @@
 		if (data & SOUND_MASK_CD)
 			awacs_reg[0] |= MASK_MUX_CD;
 		awacs_write(awacs_reg[0] | MASK_ADDR0);
-		return IOCTL_OUT(arg, data);
+		rc = IOCTL_OUT(arg, data);
+		break;
 	case SOUND_MIXER_READ_STEREODEVS:
 		data = SOUND_MASK_VOLUME | SOUND_MASK_SPEAKER
 			| SOUND_MASK_RECLEV | SOUND_MASK_CD
 			| SOUND_MASK_LINE;
-		return IOCTL_OUT(arg, data);
+		rc = IOCTL_OUT(arg, data);
+		break;
 	case SOUND_MIXER_READ_CAPS:
-		return IOCTL_OUT(arg, 0);
+		rc = IOCTL_OUT(arg, 0);
+		break;
 	case SOUND_MIXER_WRITE_VOLUME:
 		IOCTL_IN(arg, data);
 		awacs_burgundy_write_mvolume(MASK_ADDR_BURGUNDY_MASTER_VOLUME, data);
 				/* Fall through */
 	case SOUND_MIXER_READ_VOLUME:
-		return IOCTL_OUT(arg, awacs_burgundy_read_mvolume(MASK_ADDR_BURGUNDY_MASTER_VOLUME));
+		rc = IOCTL_OUT(arg, awacs_burgundy_read_mvolume(MASK_ADDR_BURGUNDY_MASTER_VOLUME));
+		break;
 	case SOUND_MIXER_WRITE_SPEAKER:
 		IOCTL_IN(arg, data);
-
 		if (!(data & 0xff)) {
 			/* Mute the left speaker */
 			awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES,
@@ -1806,7 +1931,7 @@
 		}
 
 		data = (((data&0xff)*16)/100 > 0xf ? 0xf :
-			(((data&0xff)*16)/100)) + 
+			(((data&0xff)*16)/100)) +
 			((((data>>8)*16)/100 > 0xf ? 0xf :
 			  ((((data>>8)*16)/100)))<<4);
 
@@ -1815,21 +1940,24 @@
 	case SOUND_MIXER_READ_SPEAKER:
 		data = awacs_burgundy_rcb(MASK_ADDR_BURGUNDY_ATTENSPEAKER);
 		data = (((data & 0xf)*100)/16) + ((((data>>4)*100)/16)<<8);
-		return IOCTL_OUT(arg, ~data);
+		rc = IOCTL_OUT(arg, ~data);
+		break;
 	case SOUND_MIXER_WRITE_ALTPCM:	/* really bell volume */
 		IOCTL_IN(arg, data);
-		beep_volume = data & 0xff;
+		beep_vol = data & 0xff;
 				/* fall through */
 	case SOUND_MIXER_READ_ALTPCM:
-		return IOCTL_OUT(arg, beep_volume);
+		rc = IOCTL_OUT(arg, beep_vol);
+		break;
 	case SOUND_MIXER_WRITE_LINE:
 		IOCTL_IN(arg, data);
 		awacs_burgundy_write_volume(MASK_ADDR_BURGUNDY_VOLLINE, data);
 
 				/* fall through */
 	case SOUND_MIXER_READ_LINE:
-		data = awacs_burgundy_read_volume(MASK_ADDR_BURGUNDY_VOLLINE);				
-		return IOCTL_OUT(arg, data);
+		data = awacs_burgundy_read_volume(MASK_ADDR_BURGUNDY_VOLLINE);
+		rc = IOCTL_OUT(arg, data);
+		break;
 	case SOUND_MIXER_WRITE_MIC:
 		IOCTL_IN(arg, data);
 				/* Mic is mono device */
@@ -1837,66 +1965,330 @@
 		awacs_burgundy_write_volume(MASK_ADDR_BURGUNDY_VOLMIC, data);
 				/* fall through */
 	case SOUND_MIXER_READ_MIC:
-		data = awacs_burgundy_read_volume(MASK_ADDR_BURGUNDY_VOLMIC);				
+		data = awacs_burgundy_read_volume(MASK_ADDR_BURGUNDY_VOLMIC);
 		data <<= 24;
-		return IOCTL_OUT(arg, data);
+		rc = IOCTL_OUT(arg, data);
+		break;
 	case SOUND_MIXER_WRITE_CD:
 		IOCTL_IN(arg, data);
 		awacs_burgundy_write_volume(MASK_ADDR_BURGUNDY_VOLCD, data);
 				/* fall through */
 	case SOUND_MIXER_READ_CD:
 		data = awacs_burgundy_read_volume(MASK_ADDR_BURGUNDY_VOLCD);
-		return IOCTL_OUT(arg, data);
+		rc = IOCTL_OUT(arg, data);
+		break;
 	case SOUND_MIXER_WRITE_RECLEV:
 		IOCTL_IN(arg, data);
 		data = awacs_volume_setter(data, 0, 0, 4);
-		return IOCTL_OUT(arg, data);
+		rc = IOCTL_OUT(arg, data);
+		break;
 	case SOUND_MIXER_READ_RECLEV:
 		data = awacs_get_volume(awacs_reg[0], 4);
-		return IOCTL_OUT(arg, data);
+		rc = IOCTL_OUT(arg, data);
+		break;
 	case SOUND_MIXER_OUTMASK:
+	case SOUND_MIXER_OUTSRC:
+	default:
+		rc = -EINVAL;
+	}
+	
+	return rc;
+}
+
+static int tumbler_mixer_ioctl(u_int cmd, u_long arg)
+{
+	int data;
+	int rc;
+
+	/* We are, we are, we are... Tumbler (and very dumb) */
+	/* Ok, we're not THAT dumb anymore, but still pretty dumb :-) */
+
+	switch(cmd) {
+	case SOUND_MIXER_READ_DEVMASK:
+		data =  SOUND_MASK_VOLUME | SOUND_MASK_ALTPCM |
+			SOUND_MASK_BASS | SOUND_MASK_TREBLE |
+			SOUND_MASK_PCM;
+		rc = IOCTL_OUT(arg, data);
+		break;
+	case SOUND_MIXER_READ_RECMASK:
+		data = 0;
+		rc = IOCTL_OUT(arg, data);
+		break;
+	case SOUND_MIXER_READ_RECSRC:
+		data = 0;
+		rc = IOCTL_OUT(arg, data);
+		break;
+	case SOUND_MIXER_WRITE_RECSRC:
+		IOCTL_IN(arg, data);
+		data =0;
+		rc = IOCTL_OUT(arg, data);
+		break;
+	case SOUND_MIXER_READ_STEREODEVS:
+		data = SOUND_MASK_VOLUME;
+		rc = IOCTL_OUT(arg, data);
+		break;
+	case SOUND_MIXER_READ_CAPS:
+		rc = IOCTL_OUT(arg, 0);
+		break;
+	case SOUND_MIXER_WRITE_BASS:
+		IOCTL_IN(arg, data);
+		tumbler_set_bass(data);
+	case SOUND_MIXER_READ_BASS:
+		tumbler_get_bass(&data);
+		rc = IOCTL_OUT(arg, data);
+		break;
+	case SOUND_MIXER_WRITE_TREBLE:
+		IOCTL_IN(arg, data);
+		tumbler_set_treble(data);
+		/* Fall through */
+	case SOUND_MIXER_READ_TREBLE:
+		tumbler_get_treble(&data);
+		rc = IOCTL_OUT(arg, data);
+		break;
+	case SOUND_MIXER_WRITE_PCM:
+		IOCTL_IN(arg, data);
+		tumbler_set_pcm_lvl(data);
+		/* Fall through */
+	case SOUND_MIXER_READ_PCM:
+		tumbler_get_pcm_lvl(&data);
+		IOCTL_OUT(arg, data);
+		break;
+	case SOUND_MIXER_WRITE_VOLUME:
+		IOCTL_IN(arg, data);
+		tumbler_set_volume(data, data);
+		/* Fall through */
+	case SOUND_MIXER_READ_VOLUME:
+		tumbler_get_volume(& data, &data);
+		rc = IOCTL_OUT(arg, data);
 		break;
+	case SOUND_MIXER_WRITE_ALTPCM:	/* really bell volume */
+		IOCTL_IN(arg, data);
+		beep_vol = data & 0xff;
+		/* fall through */
+	case SOUND_MIXER_READ_ALTPCM:
+		rc = IOCTL_OUT(arg, beep_vol);
+		break;
+	case SOUND_MIXER_OUTMASK:
 	case SOUND_MIXER_OUTSRC:
+	default:
+		rc = -EINVAL;
+	}
+
+	return rc;
+}
+
+static int daca_mixer_ioctl(u_int cmd, u_long arg)
+{
+	int data;
+	int rc;
+
+	/* And the DACA's no genius either! */
+
+	switch(cmd) {
+	case SOUND_MIXER_READ_DEVMASK:
+		data = SOUND_MASK_VOLUME;
+		rc = IOCTL_OUT(arg, data);
+		break;
+	case SOUND_MIXER_READ_RECMASK:
+		data = 0;
+		rc = IOCTL_OUT(arg, data);
+		break;
+	case SOUND_MIXER_READ_RECSRC:
+		data = 0;
+		rc = IOCTL_OUT(arg, data);
+		break;
+	case SOUND_MIXER_WRITE_RECSRC:
+		IOCTL_IN(arg, data);
+		data =0;
+		rc = IOCTL_OUT(arg, data);
 		break;
+	case SOUND_MIXER_READ_STEREODEVS:
+		data = SOUND_MASK_VOLUME;
+		rc = IOCTL_OUT(arg, data);
+		break;
+	case SOUND_MIXER_READ_CAPS:
+		rc = IOCTL_OUT(arg, 0);
+		break;
+	case SOUND_MIXER_WRITE_VOLUME:
+		IOCTL_IN(arg, data);
+		daca_set_volume(data, data);
+		/* Fall through */
+	case SOUND_MIXER_READ_VOLUME:
+		daca_get_volume(& data, &data);
+		rc = IOCTL_OUT(arg, data);
+		break;
+	case SOUND_MIXER_OUTMASK:
+	case SOUND_MIXER_OUTSRC:
+	default:
+		rc = -EINVAL;
 	}
-	return -EINVAL;
+	return rc;
 }
 
 static int PMacMixerIoctl(u_int cmd, u_long arg)
 {
-	/* Different IOCTLS for burgundy*/
-	if (awacs_revision >= AWACS_BURGUNDY)
-		return burgundy_mixer_ioctl(cmd, arg);
-	return awacs_mixer_ioctl(cmd, arg);
+	int rc;
+	
+	/* Different IOCTLS for burgundy and, eventually, DACA & Tumbler */
+
+	TRY_LOCK();
+	
+	switch (awacs_revision){
+		case AWACS_BURGUNDY:
+			rc = burgundy_mixer_ioctl(cmd, arg);
+			break ;
+		case AWACS_DACA:
+			rc = daca_mixer_ioctl(cmd, arg);
+			break;
+		case AWACS_TUMBLER:
+			rc = tumbler_mixer_ioctl(cmd, arg);
+			break ;
+		default: /* ;-)) */
+			rc = awacs_mixer_ioctl(cmd, arg);
+	}
+
+	UNLOCK();
+	
+	return rc;
+}
+
+static void PMacMixerInit(void)
+{
+	switch (awacs_revision) {
+		case AWACS_TUMBLER:
+		  printk("AE-Init tumbler mixer\n");
+		  break ;
+		  
+		case AWACS_DACA:
+		case AWACS_BURGUNDY:
+			break ;	/* don't know yet */
+		case AWACS_AWACS:
+		case AWACS_SCREAMER:
+		default:
+			awacs_mixer_init() ;
+			break ;
+	}
 }
 
+/* Write/Read sq setup functions:
+   Check to see if we have enough (or any) dbdma cmd buffers for the
+   user's fragment settings.  If not, allocate some. If this fails we will
+   point at the beep buffer - as an emergency provision - to stop dma tromping
+   on some random bit of memory (if someone lets it go anyway).
+   The command buffers are then set up to point to the fragment buffers
+   (allocated elsewhere).  We need n+1 commands the last of which holds
+   a NOP + loop to start.
+*/
 
-static void PMacWriteSqSetup(void)
+static int PMacWriteSqSetup(void)
 {
-	int i;
+	int i, count = 600 ;
 	volatile struct dbdma_cmd *cp;
 
+	LOCK();
+	
+	/* stop the controller from doing any output - if it isn't already.
+	   it _should_ be before this is called anyway */
+
+	out_le32(&awacs_txdma->control, (RUN|PAUSE|FLUSH|WAKE) << 16);
+	while ((in_le32(&awacs_txdma->status) & RUN) && count--)
+		udelay(1);
+#ifdef DEBUG_DMASOUND
+if (count <= 0)
+	printk("dmasound_pmac: write sq setup: timeout waiting for dma to stop\n");
+#endif
+
+	if ((write_sq.max_count + 1) > number_of_tx_cmd_buffers) {
+		if (awacs_tx_cmd_space)
+			kfree(awacs_tx_cmd_space);
+		number_of_tx_cmd_buffers = 0;
+
+		/* we need nbufs + 1 (for the loop) and we should request + 1
+		   again because the DBDMA_ALIGN might pull the start up by up
+		   to sizeof(struct dbdma_cmd) - 4.
+		*/
+
+		awacs_tx_cmd_space = kmalloc
+			((write_sq.max_count + 1 + 1) * sizeof(struct dbdma_cmd),
+			 GFP_KERNEL);
+		if (awacs_tx_cmd_space == NULL) {
+			/* don't leave it dangling - nasty but better than a
+			   random address */
+			out_le32(&awacs_txdma->cmdptr, virt_to_bus(beep_dbdma_cmd));
+			printk(KERN_ERR
+			   "dmasound_pmac: can't allocate dbdma cmd buffers"
+			   ", driver disabled\n");
+			UNLOCK();
+			return -ENOMEM;
+		}
+		awacs_tx_cmds = (volatile struct dbdma_cmd *)
+			DBDMA_ALIGN(awacs_tx_cmd_space);
+		number_of_tx_cmd_buffers = write_sq.max_count + 1;
+	}
+
 	cp = awacs_tx_cmds;
-	memset((void *)cp, 0, (write_sq.numBufs+1) * sizeof(struct dbdma_cmd));
-	for (i = 0; i < write_sq.numBufs; ++i, ++cp) {
+	memset((void *)cp, 0, (write_sq.max_count+1) * sizeof(struct dbdma_cmd));
+	for (i = 0; i < write_sq.max_count; ++i, ++cp) {
 		st_le32(&cp->phy_addr, virt_to_bus(write_sq.buffers[i]));
 	}
 	st_le16(&cp->command, DBDMA_NOP + BR_ALWAYS);
 	st_le32(&cp->cmd_dep, virt_to_bus(awacs_tx_cmds));
-	out_le32(&awacs_txdma->control, (RUN|PAUSE|FLUSH|WAKE) << 16);
+	/* point the controller at the command stack - ready to go */
 	out_le32(&awacs_txdma->cmdptr, virt_to_bus(awacs_tx_cmds));
+	UNLOCK();
+	return 0;
 }
 
-static void PMacReadSqSetup(void)
+static int PMacReadSqSetup(void)
 {
-	int i;
+	int i, count = 600;
 	volatile struct dbdma_cmd *cp;
 
+	LOCK();
+	
+	/* stop the controller from doing any input - if it isn't already.
+	   it _should_ be before this is called anyway */
+	
+	out_le32(&awacs_rxdma->control, (RUN|PAUSE|FLUSH|WAKE) << 16);
+	while ((in_le32(&awacs_rxdma->status) & RUN) && count--)
+		udelay(1);
+#ifdef DEBUG_DMASOUND
+if (count <= 0)
+	printk("dmasound_pmac: read sq setup: timeout waiting for dma to stop\n");
+#endif
+
+	if ((read_sq.max_count+1) > number_of_rx_cmd_buffers ) {
+		if (awacs_rx_cmd_space)
+			kfree(awacs_rx_cmd_space);
+		number_of_rx_cmd_buffers = 0;
+
+		/* we need nbufs + 1 (for the loop) and we should request + 1 again
+		   because the DBDMA_ALIGN might pull the start up by up to
+		   sizeof(struct dbdma_cmd) - 4 (assuming kmalloc aligns 32 bits).
+		*/
+
+		awacs_rx_cmd_space = kmalloc
+			((read_sq.max_count + 1 + 1) * sizeof(struct dbdma_cmd),
+			 GFP_KERNEL);
+		if (awacs_rx_cmd_space == NULL) {
+			/* don't leave it dangling - nasty but better than a
+			   random address */
+			out_le32(&awacs_rxdma->cmdptr, virt_to_bus(beep_dbdma_cmd));
+			printk(KERN_ERR
+			   "dmasound_pmac: can't allocate dbdma cmd buffers"
+			   ", driver disabled\n");
+			UNLOCK();
+			return -ENOMEM;
+		}
+		awacs_rx_cmds = (volatile struct dbdma_cmd *)
+			DBDMA_ALIGN(awacs_rx_cmd_space);
+		number_of_rx_cmd_buffers = read_sq.max_count + 1 ;
+	}
 	cp = awacs_rx_cmds;
-	memset((void *)cp, 0, (read_sq.numBufs+1) * sizeof(struct dbdma_cmd));
+	memset((void *)cp, 0, (read_sq.max_count+1) * sizeof(struct dbdma_cmd));
 
 	/* Set dma buffers up in a loop */
-	for (i = 0; i < read_sq.numBufs; i++,cp++) {
+	for (i = 0; i < read_sq.max_count; i++,cp++) {
 		st_le32(&cp->phy_addr, virt_to_bus(read_sq.buffers[i]));
 		st_le16(&cp->command, INPUT_MORE + INTR_ALWAYS);
 		st_le16(&cp->req_count, read_sq.block_size);
@@ -1907,37 +2299,105 @@
 	*/
 	st_le16(&cp->command, DBDMA_NOP + BR_ALWAYS);
 	st_le32(&cp->cmd_dep, virt_to_bus(awacs_rx_cmds));
-
-	/* Don't start until the first read is done.
-	 * This will also abort any operations in progress if the DMA
-	 * happens to be running (and it shouldn't).
-	 */
-	out_le32(&awacs_rxdma->control, (RUN|PAUSE|FLUSH|WAKE) << 16);
+	/* point the controller at the command stack - ready to go */
 	out_le32(&awacs_rxdma->cmdptr, virt_to_bus(awacs_rx_cmds));
 
+	UNLOCK();
+	return 0;
 }
 
+/* TODO: this needs work to guarantee that when it returns DMA has stopped
+   but in a more elegant way than is done here....
+*/
+
 static void PMacAbortRead(void)
 {
 	int i;
 	volatile struct dbdma_cmd *cp;
 
+	LOCK();
+	/* give it a chance to update the output and provide the IRQ
+	   that is expected.
+	*/
+
+	out_le32(&awacs_rxdma->control, ((FLUSH) << 16) + FLUSH );
+
 	cp = awacs_rx_cmds;
-	for (i = 0; i < read_sq.numBufs; i++,cp++)
+	for (i = 0; i < read_sq.max_count; i++,cp++)
 		st_le16(&cp->command, DBDMA_STOP);
 	/*
 	 * We should probably wait for the thing to stop before we
-	 * release the memory
+	 * release the memory.
 	 */
+
+	wait_ms(100) ; /* give it a (small) chance to act */
+
+	/* apply the sledgehammer approach - just stop it now */
+
+	out_le32(&awacs_rxdma->control, (RUN|PAUSE|FLUSH|WAKE) << 16);
+	UNLOCK();
 }
 
+extern char *get_afmt_string(int);
+static int PMacStateInfo(char *b, size_t sp)
+{
+	int i, len = 0;
+	len = sprintf(b,"HW rates: ");
+	switch (awacs_revision){
+		case AWACS_DACA:
+		case AWACS_BURGUNDY:
+			len += sprintf(b,"44100 ") ;
+			break ;
+		case AWACS_TUMBLER:
+			for (i=0; i<2; i++){
+				if (tumbler_freqs_ok[i])
+					len += sprintf(b+len,"%d ", tumbler_freqs[i]) ;
+			}
+			break ;
+
+		case AWACS_AWACS:
+		case AWACS_SCREAMER:
+		default:
+			for (i=0; i<8; i++){
+				if (awacs_freqs_ok[i])
+					len += sprintf(b+len,"%d ", awacs_freqs[i]) ;
+			}
+			break ;
+	}
+	len += sprintf(b+len,"s/sec\n") ;
+	if (len < sp) {
+		len += sprintf(b+len,"HW AFMTS: ");
+		i = AFMT_U16_BE ;
+		while (i) {
+			if (i & dmasound.mach.hardware_afmts)
+				len += sprintf(b+len,"%s ",
+					get_afmt_string(i & dmasound.mach.hardware_afmts));
+			i >>= 1 ;
+		}
+		len += sprintf(b+len,"\n") ;
+	}
+	return len ;
+}
 
 /*** Machine definitions *****************************************************/
 
+static SETTINGS def_hard = {
+	format: AFMT_S16_BE,
+	stereo: 1,
+	size: 16,
+	speed: 44100
+} ;
+
+static SETTINGS def_soft = {
+	format: AFMT_S16_BE,
+	stereo: 1,
+	size: 16,
+	speed: 44100
+} ;
 
 static MACHINE machPMac = {
 	name:		awacs_name,
-	name2:		"AWACS",
+	name2:		"PowerMac Built-in Sound",
 	open:		PMacOpen,
 	release:	PMacRelease,
 	dma_alloc:	PMacAlloc,
@@ -1951,233 +2411,669 @@
 	setFormat:	PMacSetFormat,
 	setVolume:	PMacSetVolume,
 	play:		PMacPlay,
-	record:		PMacRecord,
+	record:		NULL,		/* default to no record */
+	mixer_init:	PMacMixerInit,
 	mixer_ioctl:	PMacMixerIoctl,
 	write_sq_setup:	PMacWriteSqSetup,
 	read_sq_setup:	PMacReadSqSetup,
+	state_info:	PMacStateInfo,
 	abort_read:	PMacAbortRead,
-	min_dsp_speed:	8000
+	min_dsp_speed:	7350,
+	max_dsp_speed:	44100,
+	version:	((DMASOUND_AWACS_REVISION<<8) + DMASOUND_AWACS_EDITION)
 };
 
 
 /*** Config & Setup **********************************************************/
 
+/* Check for pmac models that we care about in terms of special actions.
+*/
 
-int __init dmasound_awacs_init(void)
+void __init
+set_model(void)
 {
-	struct device_node *np;
+	/* portables/lap-tops */
 
-	if (_machine != _MACH_Pmac)
-		return -ENODEV;
+	if (machine_is_compatible("AAPL,3400/2400") ||
+	    machine_is_compatible("AAPL,3500"))	{
+		is_pbook_3X00 = 1 ;
+	}
+	if (machine_is_compatible("PowerBook1,1")  || /* lombard */
+	    machine_is_compatible("AAPL,PowerBook1998")){ /* wallstreet */
+		is_pbook_g3 = 1 ;
+		return ;
+	}
+}
 
-	awacs_subframe = 0;
-	awacs_revision = 0;
-	np = find_devices("awacs");
-	if (np == 0) {
-		/*
-		 * powermac G3 models have a node called "davbus"
-		 * with a child called "sound".
-		 */
-		struct device_node *sound;
-		np = find_devices("davbus");
-		sound = find_devices("sound");
-		if (sound != 0 && sound->parent == np) {
-			unsigned int *prop, l, i;
-			prop = (unsigned int *)
-				get_property(sound, "sub-frame", 0);
-			if (prop != 0 && *prop >= 0 && *prop < 16)
-				awacs_subframe = *prop;
-			if (device_is_compatible(sound, "burgundy"))
-				awacs_revision = AWACS_BURGUNDY;
-			/* This should be verified on older screamers */
-			if (device_is_compatible(sound, "screamer"))
-				awacs_is_screamer = 1;
-			prop = (unsigned int *)get_property(sound, "device-id", 0);
-			if (prop != 0)
-				awacs_device_id = *prop;
-			awacs_has_iic = (find_devices("perch") != NULL);
-
-			/* look for a property saying what sample rates
-			   are available */
-			for (i = 0; i < 8; ++i)
-				awacs_freqs_ok[i] = 0;
-			prop = (unsigned int *) get_property
-				(sound, "sample-rates", &l);
-			if (prop == 0)
-				prop = (unsigned int *) get_property
-					(sound, "output-frame-rates", &l);
-			if (prop != 0) {
-				for (l /= sizeof(int); l > 0; --l) {
-					/* sometimes the rate is in the
-					   high-order 16 bits (?) */
-					unsigned int r = *prop++;
-					if (r >= 0x10000)
-						r >>= 16;
-					for (i = 0; i < 8; ++i) {
-						if (r == awacs_freqs[i]) {
-							awacs_freqs_ok[i] = 1;
-							break;
-						}
-					}
+/* Get the OF node that tells us about the registers, interrupts etc. to use
+   for sound IO.
+
+   On most machines the sound IO OF node is the 'davbus' node.  On newer pmacs
+   with DACA (& Tumbler) the node to use is i2s-a.  On much older machines i.e.
+   before 9500 there is no davbus node and we have to use the 'awacs' property.
+
+  In the latter case we signal this by setting the codec value - so that the
+  code that looks for chip properties knows how to go about it.
+*/
+
+static struct device_node
+__init *get_snd_io_node(void)
+{
+	struct device_node *np = NULL;
+
+	/* set up awacs_node for early OF which doesn't have a full set of
+	 * properties on davbus
+	*/
+
+	awacs_node = find_devices("awacs");
+	if (awacs_node)
+		awacs_revision = AWACS_AWACS;
+
+	/* powermac models after 9500 (other than those which use DACA or
+	 * Tumbler) have a node called "davbus".
+	 */
+	np = find_devices("davbus");
+	/*
+	 * if we didn't find a davbus device, try 'i2s-a' since
+	 * this seems to be what iBooks (& Tumbler) have.
+	 */
+	if (np == NULL)
+		np = find_devices("i2s-a");
+
+	/* if we didn't find this - perhaps we are on an early model
+	 * which _only_ has an 'awacs' node
+	*/
+	if (np == NULL && awacs_node)
+		np = awacs_node ;
+
+	/* if we failed all these return null - this will cause the
+	 * driver to give up...
+	*/
+	return np ;
+}
+
+/* Get the OF node that contains the info about the sound chip, inputs s-rates
+   etc.
+   This node does not exist (or contains much reduced info) on earlier machines
+   we have to deduce the info other ways for these.
+*/
+
+static struct device_node
+__init *get_snd_info_node(struct device_node *io)
+{
+	struct device_node *info;
+
+	info = find_devices("sound");
+	while (info != 0 && info->parent != io)
+		info = info->next;
+
+	return info ;
+}
+
+/* Find out what type of codec we have.
+*/
+
+static int
+__init get_codec_type(struct device_node *info)
+{
+	/* already set if pre-davbus model and info will be NULL */
+	int codec = awacs_revision ;
+
+	if (info) {
+		/* must do awacs first to allow screamer to overide it */
+		if (device_is_compatible(info, "awacs"))
+			codec = AWACS_AWACS ;
+		if (device_is_compatible(info, "screamer"))
+			codec = AWACS_SCREAMER;
+		if (device_is_compatible(info, "burgundy"))
+			codec = AWACS_BURGUNDY ;
+		if (device_is_compatible(info, "daca"))
+			codec = AWACS_DACA;
+		if (device_is_compatible(info, "tumbler"))
+			codec = AWACS_TUMBLER;
+	}
+	return codec ;
+}
+
+/* find out what type, if any, of expansion card we have
+*/
+static void
+__init get_expansion_type(void)
+{
+	if (find_devices("perch") != NULL)
+		has_perch = 1;
+
+	if (find_devices("pb-ziva-pc") != NULL)
+		has_ziva = 1;
+	/* need to work out how we deal with iMac SRS module */
+}
+
+/* set up frame rates.
+ * I suspect that these routines don't quite go about it the right way:
+ * - where there is more than one rate - I think that the first property
+ * value is the number of rates.
+ * TODO: check some more device trees and modify accordingly
+ *       Set dmasound.mach.max_dsp_rate on the basis of these routines.
+*/
+
+static void
+__init init_awacs_frame_rates(unsigned int *prop, unsigned int l)
+{
+	int i ;
+	if (prop) {
+		for (i=0; i<8; i++)
+			awacs_freqs_ok[i] = 0 ;
+		for (l /= sizeof(int); l > 0; --l) {
+			unsigned int r = *prop++;
+			/* Apple 'Fixed' format */
+			if (r >= 0x10000)
+				r >>= 16;
+			for (i = 0; i < 8; ++i) {
+				if (r == awacs_freqs[i]) {
+					awacs_freqs_ok[i] = 1;
+					break;
 				}
-			} else {
-				/* assume just 44.1k is OK */
-				awacs_freqs_ok[0] = 1;
 			}
 		}
 	}
-	if (np != NULL && np->n_addrs >= 3 && np->n_intrs >= 3) {
-		int vol;
-		dmasound.mach = machPMac;
-
-		awacs = (volatile struct awacs_regs *)
-			ioremap(np->addrs[0].address, 0x80);
-		awacs_txdma = (volatile struct dbdma_regs *)
-			ioremap(np->addrs[1].address, 0x100);
-		awacs_rxdma = (volatile struct dbdma_regs *)
-			ioremap(np->addrs[2].address, 0x100);
-
-		awacs_irq = np->intrs[0].line;
-		awacs_tx_irq = np->intrs[1].line;
-		awacs_rx_irq = np->intrs[2].line;
+	/* else we assume that all the rates are available */
+}
 
-		awacs_tx_cmd_space = kmalloc((write_sq.numBufs + 4) * sizeof(struct dbdma_cmd),
-					     GFP_KERNEL);
-		if (awacs_tx_cmd_space == NULL) {
-			printk(KERN_ERR "DMA sound driver: Not enough buffer memory, driver disabled!\n");
-			return -ENOMEM;
+static void
+__init init_tumbler_frame_rates(unsigned int *prop, unsigned int l)
+{
+	int i ;
+	if (prop) {
+		for (i=0; i<2; i++)
+			tumbler_freqs_ok[i] = 0;
+		for (l /= sizeof(int); l > 0; --l) {
+			unsigned int r = *prop++;
+			/* Apple 'Fixed' format */
+			if (r >= 0x10000)
+				r >>= 16;
+			for (i = 0; i < 2; ++i) {
+				if (r == tumbler_freqs[i]) {
+					tumbler_freqs_ok[i] = 1;
+					break;
+				}
+			}
 		}
-		awacs_node = np;
-#ifdef CONFIG_PMAC_PBOOK
-		if (machine_is_compatible("PowerBook1,1")
-		    || machine_is_compatible("AAPL,PowerBook1998")) {
-			pmu_suspend();
-			feature_set(np, FEATURE_Sound_CLK_enable);
-			feature_set(np, FEATURE_Sound_power);
-			/* Shorter delay will not work */
-			mdelay(1000);
-			pmu_resume();
+	}
+	/* else we assume that all the rates are available */
+}
+
+static void
+__init init_burgundy_frame_rates(unsigned int *prop, unsigned int l)
+{
+	int temp[9] ;
+	int i = 0 ;
+	if (prop) {
+		for (l /= sizeof(int); l > 0; --l) {
+			unsigned int r = *prop++;
+			/* Apple 'Fixed' format */
+			if (r >= 0x10000)
+				r >>= 16;
+			temp[i] = r ;
+			i++ ; if(i>=9) i=8;
+		}
+	}
+#ifdef DEBUG_DMASOUND
+if (i > 1){
+	int j;
+	printk("dmasound_pmac: burgundy with multiple frame rates\n");
+	for(j=0; j<i; j++)
+		printk("%d ", temp[j]) ;
+	printk("\n") ;
+}
+#endif
+}
+
+static void
+__init init_daca_frame_rates(unsigned int *prop, unsigned int l)
+{
+	int temp[9] ;
+	int i = 0 ;
+	if (prop) {
+		for (l /= sizeof(int); l > 0; --l) {
+			unsigned int r = *prop++;
+			/* Apple 'Fixed' format */
+			if (r >= 0x10000)
+				r >>= 16;
+			temp[i] = r ;
+			i++ ; if(i>=9) i=8;
+
+		}
+	}
+#ifdef DEBUG_DMASOUND
+if (i > 1){
+	int j;
+	printk("dmasound_pmac: DACA with multiple frame rates\n");
+	for(j=0; j<i; j++)
+		printk("%d ", temp[j]) ;
+	printk("\n") ;
+}
+#endif
+}
+
+static void
+__init init_frame_rates(unsigned int *prop, unsigned int l)
+{
+	switch (awacs_revision){
+		case AWACS_TUMBLER:
+			init_tumbler_frame_rates(prop, l);
+			break ;
+		case AWACS_DACA:
+			init_daca_frame_rates(prop, l);
+			break ;
+		case AWACS_BURGUNDY:
+			init_burgundy_frame_rates(prop, l);
+			break ;
+		default: /* ;-))) */
+			init_awacs_frame_rates(prop, l);
+			break ;
+	}
+}
+
+/* find things/machines that can't do mac-io byteswap
+*/
+
+static void
+__init set_hw_byteswap(struct device_node *io)
+{
+	struct device_node *mio ;
+	unsigned int *p, rev = 0 ;
+
+	/* if seems that Keylargo (at least rev2) can't byte-swap  */
+
+	for (mio = io->parent; mio ; mio = mio->parent) {
+		if (strcmp(mio->name, "mac-io") == 0) {
+			if (device_is_compatible(mio, "Keylargo")){
+				p = (unsigned int *)
+					get_property(mio, "revision-id", 0);
+				if (p)
+					rev = *p ;
+			}
+			break;
 		}
+	}
+	if (rev >= 2) {
+		hw_can_byteswap = 0;
+#ifdef DEBUG_DMASOUND
+printk("dmasound_pmac: found Keylargo rev 2 or later - H/W byte-swap disabled\n") ;
 #endif
-		awacs_tx_cmds = (volatile struct dbdma_cmd *)
-			DBDMA_ALIGN(awacs_tx_cmd_space);
+	}
+}
 
+/* Allocate the resources necessary for beep generation.  This cannot be (quite)
+   done statically (yet) because we cannot do virt_to_bus() on static vars when
+   the code is loaded as a module.
+
+   for the sake of saving the possibility that two allocations will incur the
+   overhead of two pull-ups in DBDMA_ALIGN() we allocate the 'emergency' dmdma
+   command here as well... even tho' it is not part of the beep process.
+*/
+
+int32_t
+__init setup_beep(void)
+{
+	/* Initialize beep stuff */
+	/* want one cmd buffer for beeps, and a second one for emergencies
+	   - i.e. dbdma error conditions.
+	   ask for three to allow for pull up in DBDMA_ALIGN().
+	*/
+	beep_dbdma_cmd_space =
+		kmalloc((2 + 1) * sizeof(struct dbdma_cmd), GFP_KERNEL);
+	if(beep_dbdma_cmd_space == NULL) {
+		printk(KERN_ERR "dmasound_pmac: no beep dbdma cmd space\n") ;
+		return -ENOMEM ;
+	}
+	beep_dbdma_cmd = (volatile struct dbdma_cmd *)
+			DBDMA_ALIGN(beep_dbdma_cmd_space);
+	/* set up emergency dbdma cmd */
+	emergency_dbdma_cmd = beep_dbdma_cmd+1 ;
+	beep_buf = (short *) kmalloc(BEEP_BUFLEN * 4, GFP_KERNEL);
+	if (beep_buf == NULL) {
+		printk(KERN_ERR "dmasound_pmac: no memory for beep buffer\n");
+		if( beep_dbdma_cmd_space ) kfree(beep_dbdma_cmd_space) ;
+		return -ENOMEM ;
+	}
+	/* OK, we should be safe to claim the mksound vector now */
+	orig_mksound = kd_mksound;
+	kd_mksound = awacs_mksound;
+	return 0 ;
+}
 
-		awacs_rx_cmd_space = kmalloc((read_sq.numBufs + 4) * sizeof(struct dbdma_cmd),
-					     GFP_KERNEL);
-		if (awacs_rx_cmd_space == NULL) {
-		  printk("DMA sound driver: No memory for input");
+int __init dmasound_awacs_init(void)
+{
+	struct device_node *io = NULL, *info = NULL;
+	int vol, res;
+
+	if (_machine != _MACH_Pmac)
+		return -ENODEV;
+
+	awacs_subframe = 0;
+	awacs_revision = 0;
+	hw_can_byteswap = 1 ; /* most can */
+
+	/* look for models we need to handle specially */
+	set_model() ;
+
+	/* find the OF node that tells us about the dbdma stuff
+	*/
+	io = get_snd_io_node();
+	if (io == NULL) {
+#ifdef DEBUG_DMASOUND
+printk("dmasound_pmac: couldn't find sound io OF node\n");
+#endif
+		return -ENODEV ;
+	}
+
+	/* find the OF node that tells us about the sound sub-system
+	 * this doesn't exist on pre-davbus machines (earlier than 9500)
+	*/
+	if (awacs_revision != AWACS_AWACS) { /* set for pre-davbus */
+		info = get_snd_info_node(io) ;
+		if (info == NULL){
+#ifdef DEBUG_DMASOUND
+printk("dmasound_pmac: couldn't find 'sound' OF node\n");
+#endif
+			return -ENODEV ;
 		}
-		awacs_rx_cmds = (volatile struct dbdma_cmd *)
-		  DBDMA_ALIGN(awacs_rx_cmd_space);
+	}
 
+	awacs_revision = get_codec_type(info) ;
+	if (awacs_revision == 0) {
+#ifdef DEBUG_DMASOUND
+printk("dmasound_pmac: couldn't find a Codec we can handle\n");
+#endif
+		return -ENODEV ; /* we don't know this type of h/w */
+	}
 
+	/* set up perch, ziva, SRS or whatever else we have as sound
+	 *  expansion.
+	*/
+	get_expansion_type();
 
-		awacs_reg[0] = MASK_MUX_CD;
-		/* FIXME: Only machines with external SRS module need MASK_PAROUT */
-		awacs_reg[1] = MASK_LOOPTHRU;
-		if (awacs_has_iic || awacs_device_id == 0x5 || /*awacs_device_id == 0x8
-			|| */awacs_device_id == 0xb)
-			awacs_reg[1] |= MASK_PAROUT;
-		/* get default volume from nvram */
-		vol = (~nvram_read_byte(0x1308) & 7) << 1;
-		awacs_reg[2] = vol + (vol << 6);
-		awacs_reg[4] = vol + (vol << 6);
-		awacs_reg[5] = 0;
-		awacs_reg[6] = 0;
-		awacs_reg[7] = 0;
-		out_le32(&awacs->control, 0x11);
-		awacs_write(awacs_reg[0] + MASK_ADDR0);
-		awacs_write(awacs_reg[1] + MASK_ADDR1);
-		awacs_write(awacs_reg[2] + MASK_ADDR2);
-		awacs_write(awacs_reg[4] + MASK_ADDR4);
-		if (awacs_is_screamer) {
-			awacs_write(awacs_reg[5] + MASK_ADDR5);
-			awacs_write(awacs_reg[6] + MASK_ADDR6);
-			awacs_write(awacs_reg[7] + MASK_ADDR7);
-		}
-
-		/* Initialize recent versions of the awacs */
-		if (awacs_revision == 0) {
-			awacs_revision =
-				(in_le32(&awacs->codec_stat) >> 12) & 0xf;
-			if (awacs_revision == 3) {
-				mdelay(100);
-				awacs_write(0x6000);
-				mdelay(2);
-				awacs_write(awacs_reg[1] + MASK_ADDR1);
-				awacs_enable_amp(100 * 0x101);
+	/* we've now got enough information to make up the audio topology.
+	 * we will map the sound part of mac-io now so that we can probe for
+	 * other info if necessary (early AWACS we want to read chip ids)
+	 */
+
+	if (io->n_addrs < 3 || io->n_intrs < 3) {
+		/* OK - maybe we need to use the 'awacs' node (on earlier
+		 * machines).
+		*/
+		if (awacs_node) {
+			io = awacs_node ;
+			if (io->n_addrs < 3 || io->n_intrs < 3) {
+				printk("dmasound_pmac: can't use %s"
+					" (%d addrs, %d intrs)\n",
+		      		 io->full_name, io->n_addrs, io->n_intrs);
+				return -ENODEV;
 			}
+		} else {
+			printk("dmasound_pmac: can't use %s (%d addrs, %d intrs)\n",
+		 	      io->full_name, io->n_addrs, io->n_intrs);
 		}
-		if (awacs_revision >= AWACS_BURGUNDY)
+	}
+
+	if (!request_OF_resource(io, 0, NULL)) {
+		printk(KERN_ERR "dmasound: can't request IO resource !\n");
+		return -ENODEV;
+	}
+	if (!request_OF_resource(io, 1, " (tx dma)")) {
+		release_OF_resource(io, 0);
+		printk(KERN_ERR "dmasound: can't request TX DMA resource !\n");
+		return -ENODEV;
+	}
+
+	if (!request_OF_resource(io, 2, " (rx dma)")) {
+		release_OF_resource(io, 0);
+		release_OF_resource(io, 1);
+		printk(KERN_ERR "dmasound: can't request RX DMA resource !\n");
+		return -ENODEV;
+	}
+
+	/* all OF versions I've seen use this value */
+	awacs = (volatile struct awacs_regs *)
+		ioremap(io->addrs[0].address, 0x1000);
+	awacs_txdma = (volatile struct dbdma_regs *)
+		ioremap(io->addrs[1].address, 0x100);
+	awacs_rxdma = (volatile struct dbdma_regs *)
+		ioremap(io->addrs[2].address, 0x100);
+
+#ifdef CONFIG_PMAC_PBOOK
+	/* first of all make sure that the chip is powered up....*/
+	pmac_call_feature(PMAC_FTR_SOUND_CHIP_ENABLE, io, 0, 1);
+	if (awacs_revision == AWACS_SCREAMER)
+		awacs_recalibrate();
+#endif
+	awacs_irq = io->intrs[0].line;
+	awacs_tx_irq = io->intrs[1].line;
+	awacs_rx_irq = io->intrs[2].line;
+
+	awacs_node = io;
+
+	/* if we have an awacs or screamer - probe the chip to make
+	 * sure we have the right revision.
+	*/
+
+	if (awacs_revision <= AWACS_SCREAMER){
+		uint32_t temp, rev, mfg ;
+		/* find out the awacs revision from the chip */
+		temp = in_le32(&awacs->codec_stat);
+		rev = (temp >> 12) & 0xf;
+		mfg = (temp >>  8) & 0xf;
+#ifdef DEBUG_DMASOUND
+printk("dmasound_pmac: Awacs/Screamer Codec Mfct: %d Rev %d\n", mfg, rev);
+#endif
+		if (rev >= AWACS_SCREAMER)
+			awacs_revision = AWACS_SCREAMER ;
+		else
+			awacs_revision = rev ;
+	}
+
+	dmasound.mach = machPMac;
+
+	/* find out other bits & pieces from OF, these may be present
+	   only on some models ... so be careful.
+	*/
+
+	/* in the absence of a frame rates property we will use the defaults
+	*/
+
+	if (info) {
+		unsigned int *prop, l;
+
+		sound_device_id = 0;
+		/* device ID appears post g3 b&w */
+		prop = (unsigned int *)get_property(info, "device-id", 0);
+		if (prop != 0)
+			sound_device_id = *prop;
+
+		/* look for a property saying what sample rates
+		   are available */
+
+		prop = (unsigned int *)get_property(info, "sample-rates", &l);
+		if (prop == 0)
+			prop = (unsigned int *) get_property
+				(info, "output-frame-rates", &l);
+
+		/* if it's there use it to set up frame rates */
+		init_frame_rates(prop, l) ;
+	}
+	
+	out_le32(&awacs->control, 0x11); /* set everything quiesent */
+
+	set_hw_byteswap(io) ; /* figure out if the h/w can do it */
+
+	/* get default volume from nvram
+	 * vol = (~nvram_read_byte(0x1308) & 7) << 1;
+	*/
+	vol = ((pmac_xpram_read( 8 ) & 7 ) << 1 );
+	/* set up tracking values */
+	spk_vol = vol * 100 ;
+	spk_vol /= 7 ; /* get set value to a percentage */
+	spk_vol |= (spk_vol << 8) ; /* equal left & right */
+ 	line_vol = passthru_vol = spk_vol ;
+
+	/* fill regs that are shared between AWACS & Burgundy */
+
+	awacs_reg[2] = vol + (vol << 6);
+	awacs_reg[4] = vol + (vol << 6);
+	awacs_reg[5] = vol + (vol << 6); /* screamer has loopthru vol control */
+	awacs_reg[6] = 0; /* maybe should be vol << 3 for PCMCIA speaker */
+	awacs_reg[7] = 0;
+
+	awacs_reg[0] = MASK_MUX_CD;
+	awacs_reg[1] = MASK_LOOPTHRU;
+
+	/* FIXME: Only machines with external SRS module need MASK_PAROUT */
+	if (has_perch || sound_device_id == 0x5
+	    || /*sound_device_id == 0x8 ||*/ sound_device_id == 0xb)
+		awacs_reg[1] |= MASK_PAROUT0 | MASK_PAROUT1;
+
+	switch (awacs_revision) {
+		case AWACS_TUMBLER:
+#ifdef CONFIG_KMOD
+			request_module("i2c-keywest");
+#endif /* CONFIG_KMOD */	
+			awacs_tumbler_init();
+			tas_init();
+			break ;
+		case AWACS_DACA:
+#ifdef CONFIG_KMOD
+			request_module("i2c-keywest");
+#endif /* CONFIG_KMOD */
+			daca_init();
+			break ;		/* dont know how yet */
+		case AWACS_BURGUNDY:
 			awacs_burgundy_init();
+			break ;
+		case AWACS_SCREAMER:
+		case AWACS_AWACS:
+		default:
+			load_awacs() ;
+			break ;
+	}
+
+	/* enable/set-up external modules - when we know how */
+
+	if (has_perch)
+		awacs_enable_amp(100 * 0x101);
+
+	/* Reset dbdma channels */
+	out_le32(&awacs_txdma->control, (RUN|PAUSE|FLUSH|WAKE|DEAD) << 16);
+	while (in_le32(&awacs_txdma->status) & RUN)
+		udelay(1);
+	out_le32(&awacs_rxdma->control, (RUN|PAUSE|FLUSH|WAKE|DEAD) << 16);
+	while (in_le32(&awacs_rxdma->status) & RUN)
+		udelay(1);
+
+	/* Initialize beep stuff */
+	if ((res=setup_beep()))
+		return res ;
 
-		/* Initialize beep stuff */
-		beep_dbdma_cmd = awacs_tx_cmds + (write_sq.numBufs + 1);
-		orig_mksound = kd_mksound;
-		kd_mksound = awacs_mksound;
-		beep_buf = (short *) kmalloc(BEEP_BUFLEN * 4, GFP_KERNEL);
-		if (beep_buf == NULL)
-			printk(KERN_WARNING "dmasound: no memory for "
-			       "beep buffer\n");
 #ifdef CONFIG_PMAC_PBOOK
-		pmu_register_sleep_notifier(&awacs_sleep_notifier);
+	pmu_register_sleep_notifier(&awacs_sleep_notifier);
 #endif /* CONFIG_PMAC_PBOOK */
 
-		/* Powerbooks have odd ways of enabling inputs such as
-		   an expansion-bay CD or sound from an internal modem
-		   or a PC-card modem. */
-		if (machine_is_compatible("AAPL,3400/2400")
-			|| machine_is_compatible("AAPL,3500")) {
-			is_pbook_3400 = 1;
-			/*
-			 * Enable CD and PC-card sound inputs.
-			 * This is done by reading from address
-			 * f301a000, + 0x10 to enable the expansion-bay
-			 * CD sound input, + 0x80 to enable the PC-card
-			 * sound input.  The 0x100 enables the SCSI bus
-			 * terminator power.
-			 */
-			latch_base = (unsigned char *) ioremap
-				(0xf301a000, 0x1000);
-			in_8(latch_base + 0x190);
-		} else if (machine_is_compatible("PowerBook1,1")
-			   || machine_is_compatible("AAPL,PowerBook1998")) {
-			struct device_node* mio;
-			macio_base = 0;
-			is_pbook_G3 = 1;
-			for (mio = np->parent; mio; mio = mio->parent) {
-				if (strcmp(mio->name, "mac-io") == 0
-				    && mio->n_addrs > 0) {
-					macio_base = (unsigned char *) ioremap
-						(mio->addrs[0].address, 0x40);
-					break;
-				}
+	/* Powerbooks have odd ways of enabling inputs such as
+	   an expansion-bay CD or sound from an internal modem
+	   or a PC-card modem. */
+	if (is_pbook_3X00) {
+		/*
+		 * Enable CD and PC-card sound inputs.
+		 * This is done by reading from address
+		 * f301a000, + 0x10 to enable the expansion-bay
+		 * CD sound input, + 0x80 to enable the PC-card
+		 * sound input.  The 0x100 enables the SCSI bus
+		 * terminator power.
+		 */
+		latch_base = (unsigned char *) ioremap (0xf301a000, 0x1000);
+		in_8(latch_base + 0x190);
+
+	} else if (is_pbook_g3) {
+		struct device_node* mio;
+		macio_base = 0;
+		for (mio = io->parent; mio; mio = mio->parent) {
+			if (strcmp(mio->name, "mac-io") == 0
+			    && mio->n_addrs > 0) {
+				macio_base = (unsigned char *) ioremap
+					(mio->addrs[0].address, 0x40);
+				break;
 			}
-			/*
-			 * Enable CD sound input.
-			 * The relevant bits for writing to this byte are 0x8f.
-			 * I haven't found out what the 0x80 bit does.
-			 * For the 0xf bits, writing 3 or 7 enables the CD
-			 * input, any other value disables it.  Values
-			 * 1, 3, 5, 7 enable the microphone.  Values 0, 2,
-			 * 4, 6, 8 - f enable the input from the modem.
-			 */
-			if (macio_base)
-				out_8(macio_base + 0x37, 3);
-		}
-		sprintf(awacs_name, "PowerMac (AWACS rev %d) ",
-			awacs_revision);
-		return dmasound_init();
+		}
+		/*
+		 * Enable CD sound input.
+		 * The relevant bits for writing to this byte are 0x8f.
+		 * I haven't found out what the 0x80 bit does.
+		 * For the 0xf bits, writing 3 or 7 enables the CD
+		 * input, any other value disables it.  Values
+		 * 1, 3, 5, 7 enable the microphone.  Values 0, 2,
+		 * 4, 6, 8 - f enable the input from the modem.
+		 *  -- paulus.
+		 */
+		if (macio_base)
+			out_8(macio_base + 0x37, 3);
+	}
+
+	if (hw_can_byteswap)
+ 		dmasound.mach.hardware_afmts = (AFMT_S16_BE | AFMT_S16_LE) ;
+ 	else
+		dmasound.mach.hardware_afmts = AFMT_S16_BE ;
+
+	/* shut out chips that do output only.
+	   may need to extend this to machines which have no inputs - even tho'
+	   they use screamer - IIRC one of the powerbooks is like this.
+	*/
+
+	if (awacs_revision != AWACS_TUMBLER && awacs_revision != AWACS_DACA) {
+		dmasound.mach.capabilities = DSP_CAP_DUPLEX ;
+		dmasound.mach.record = PMacRecord ;
+	}
+
+	dmasound.mach.default_hard = def_hard ;
+	dmasound.mach.default_soft = def_soft ;
+
+	switch (awacs_revision) {
+		case AWACS_BURGUNDY:
+			sprintf(awacs_name, "PowerMac Burgundy ") ;
+			break ;
+		case AWACS_DACA:
+			sprintf(awacs_name, "PowerMac DACA ") ;
+			break ;
+		case AWACS_TUMBLER:
+			sprintf(awacs_name, "PowerMac Tumbler ") ;
+			break ;
+		case AWACS_SCREAMER:
+			sprintf(awacs_name, "PowerMac Screamer ") ;
+			break ;
+		case AWACS_AWACS:
+		default:
+			sprintf(awacs_name, "PowerMac AWACS rev %d ", awacs_revision) ;
+			break ;
 	}
-	return -ENODEV;
+
+	return dmasound_init();
 }
 
 static void __exit dmasound_awacs_cleanup(void)
 {
+	switch (awacs_revision) {
+		case AWACS_TUMBLER:
+			awacs_tumbler_cleanup();
+			tas_cleanup();
+			break ;
+		case AWACS_DACA:
+			daca_cleanup();
+			break;
+	}
 	dmasound_deinit();
 }
 
+MODULE_DESCRIPTION("PowerMac built-in audio driver.");
+MODULE_LICENSE("GPL");
+
 module_init(dmasound_awacs_init);
 module_exit(dmasound_awacs_cleanup);
-MODULE_LICENSE("GPL");
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/sound/dmasound/dmasound_core.c linux/drivers/sound/dmasound/dmasound_core.c
--- linux.orig/drivers/sound/dmasound/dmasound_core.c	Mon Jan  7 23:41:09 2002
+++ linux/drivers/sound/dmasound/dmasound_core.c	Wed Dec 26 16:47:48 2001
@@ -1,4 +1,3 @@
-
 /*
  *  linux/drivers/sound/dmasound/dmasound_core.c
  *
@@ -103,38 +102,113 @@
  *	2000/3/25	Geert Uytterhoeven:
  *			  - Integration of dmasound_q40
  *			  - Small clean ups
+ *
+ *	2001/01/26 [1.0] Iain Sandoe
+ *			  - make /dev/sndstat show revision & edition info.
+ *			  - since dmasound.mach.sq_setup() can fail on pmac
+ *			    its type has been changed to int and the returns
+ *			    are checked.
+ *		   [1.1]  - stop missing translations from being called.
+ *	2001/02/08 [1.2]  - remove unused translation tables & move machine-
+ *			    specific tables to low-level.
+ *			  - return correct info. for SNDCTL_DSP_GETFMTS.
+ *		   [1.3]  - implement SNDCTL_DSP_GETCAPS fully.
+ *		   [1.4]  - make /dev/sndstat text length usage deterministic.
+ *			  - make /dev/sndstat call to low-level
+ *			    dmasound.mach.state_info() pass max space to ll driver.
+ *			  - tidy startup banners and output info.
+ *		   [1.5]  - tidy up a little (removed some unused #defines in
+ *			    dmasound.h)
+ *			  - fix up HAS_RECORD conditionalisation.
+ *			  - add record code in places it is missing...
+ *			  - change buf-sizes to bytes to allow < 1kb for pmac
+ *			    if user param entry is < 256 the value is taken to
+ *			    be in kb > 256 is taken to be in bytes.
+ *			  - make default buff/frag params conditional on
+ *			    machine to allow smaller values for pmac.
+ *			  - made the ioctls, read & write comply with the OSS
+ *			    rules on setting params.
+ *			  - added parsing of _setup() params for record.
+ *	2001/04/04 [1.6]  - fix bug where sample rates higher than maximum were
+ *			    being reported as OK.
+ *			  - fix open() to return -EBUSY as per OSS doc. when
+ *			    audio is in use - this is independent of O_NOBLOCK.
+ *			  - fix bug where SNDCTL_DSP_POST was blocking.
  */
 
+ /* Record capability notes 30/01/2001:
+  * At present these observations apply only to pmac LL driver (the only one
+  * that can do record, at present).  However, if other LL drivers for machines
+  * with record are added they may apply.
+  *
+  * The fragment parameters for the record and play channels are separate.
+  * However, if the driver is opened O_RDWR there is no way (in the current OSS
+  * API) to specify their values independently for the record and playback
+  * channels.  Since the only common factor between the input & output is the
+  * sample rate (on pmac) it should be possible to open /dev/dspX O_WRONLY and
+  * /dev/dspY O_RDONLY.  The input & output channels could then have different
+  * characteristics (other than the first that sets sample rate claiming the
+  * right to set it for ever).  As it stands, the format, channels, number of
+  * bits & sample rate are assumed to be common.  In the future perhaps these
+  * should be the responsibility of the LL driver - and then if a card really
+  * does not share items between record & playback they can be specified
+  * separately.
+*/
+
+/* Thread-safeness of shared_resources notes: 31/01/2001
+ * If the user opens O_RDWR and then splits record & play between two threads
+ * both of which inherit the fd - and then starts changing things from both
+ * - we will have difficulty telling.
+ *
+ * It's bad application coding - but ...
+ * TODO: think about how to sort this out... without bogging everything down in
+ * semaphores.
+ *
+ * Similarly, the OSS spec says "all changes to parameters must be between
+ * open() and the first read() or write(). - and a bit later on (by
+ * implication) "between SNDCTL_DSP_RESET and the first read() or write() after
+ * it".  If the app is multi-threaded and this rule is broken between threads
+ * we will have trouble spotting it - and the fault will be rather obscure :-(
+ *
+ * We will try and put out at least a kmsg if we see it happen... but I think
+ * it will be quite hard to trap it with an -EXXX return... because we can't
+ * see the fault until after the damage is done.
+*/
 
 #include <linux/module.h>
 #include <linux/slab.h>
 #include <linux/sound.h>
 #include <linux/init.h>
 #include <linux/soundcard.h>
+#include <linux/poll.h>
 #include <linux/smp_lock.h>
 
 #include <asm/uaccess.h>
 
 #include "dmasound.h"
 
+#define DMASOUND_CORE_REVISION 1
+#define DMASOUND_CORE_EDITION 6
 
     /*
      *  Declarations
      */
 
 int dmasound_catchRadius = 0;
-static unsigned int numWriteBufs = 4;
-static unsigned int writeBufSize = 32;	/* in KB! */
+static unsigned int numWriteBufs = DEFAULT_N_BUFFERS;
+static unsigned int writeBufSize = DEFAULT_BUFF_SIZE ;	/* in bytes */
 #ifdef HAS_RECORD
-static unsigned int numReadBufs = 4;
-static unsigned int readBufSize = 32;	/* in KB! */
+static unsigned int numReadBufs = DEFAULT_N_BUFFERS;
+static unsigned int readBufSize = DEFAULT_BUFF_SIZE;	/* in bytes */
 #endif
 
 MODULE_PARM(dmasound_catchRadius, "i");
 MODULE_PARM(numWriteBufs, "i");
 MODULE_PARM(writeBufSize, "i");
+#ifdef HAS_RECORD
 MODULE_PARM(numReadBufs, "i");
 MODULE_PARM(readBufSize, "i");
+#endif
 MODULE_LICENSE("GPL");
 
 #ifdef MODULE
@@ -144,246 +218,18 @@
 static int irq_installed = 0;
 #endif /* MODULE */
 
+/* control over who can modify resources shared between play/record */
+static mode_t shared_resource_owner = 0 ;
+static int shared_resources_initialised = 0 ;
 
     /*
-     *  Conversion tables
+     *  Common stuff
      */
 
-#ifdef HAS_8BIT_TABLES
-/* 8 bit mu-law */
-
-char dmasound_ulaw2dma8[] = {
-	-126,	-122,	-118,	-114,	-110,	-106,	-102,	-98,
-	-94,	-90,	-86,	-82,	-78,	-74,	-70,	-66,
-	-63,	-61,	-59,	-57,	-55,	-53,	-51,	-49,
-	-47,	-45,	-43,	-41,	-39,	-37,	-35,	-33,
-	-31,	-30,	-29,	-28,	-27,	-26,	-25,	-24,
-	-23,	-22,	-21,	-20,	-19,	-18,	-17,	-16,
-	-16,	-15,	-15,	-14,	-14,	-13,	-13,	-12,
-	-12,	-11,	-11,	-10,	-10,	-9,	-9,	-8,
-	-8,	-8,	-7,	-7,	-7,	-7,	-6,	-6,
-	-6,	-6,	-5,	-5,	-5,	-5,	-4,	-4,
-	-4,	-4,	-4,	-4,	-3,	-3,	-3,	-3,
-	-3,	-3,	-3,	-3,	-2,	-2,	-2,	-2,
-	-2,	-2,	-2,	-2,	-2,	-2,	-2,	-2,
-	-1,	-1,	-1,	-1,	-1,	-1,	-1,	-1,
-	-1,	-1,	-1,	-1,	-1,	-1,	-1,	-1,
-	-1,	-1,	-1,	-1,	-1,	-1,	-1,	0,
-	125,	121,	117,	113,	109,	105,	101,	97,
-	93,	89,	85,	81,	77,	73,	69,	65,
-	62,	60,	58,	56,	54,	52,	50,	48,
-	46,	44,	42,	40,	38,	36,	34,	32,
-	30,	29,	28,	27,	26,	25,	24,	23,
-	22,	21,	20,	19,	18,	17,	16,	15,
-	15,	14,	14,	13,	13,	12,	12,	11,
-	11,	10,	10,	9,	9,	8,	8,	7,
-	7,	7,	6,	6,	6,	6,	5,	5,
-	5,	5,	4,	4,	4,	4,	3,	3,
-	3,	3,	3,	3,	2,	2,	2,	2,
-	2,	2,	2,	2,	1,	1,	1,	1,
-	1,	1,	1,	1,	1,	1,	1,	1,
-	0,	0,	0,	0,	0,	0,	0,	0,
-	0,	0,	0,	0,	0,	0,	0,	0,
-	0,	0,	0,	0,	0,	0,	0,	0
-};
-
-/* 8 bit A-law */
-
-char dmasound_alaw2dma8[] = {
-	-22,	-21,	-24,	-23,	-18,	-17,	-20,	-19,
-	-30,	-29,	-32,	-31,	-26,	-25,	-28,	-27,
-	-11,	-11,	-12,	-12,	-9,	-9,	-10,	-10,
-	-15,	-15,	-16,	-16,	-13,	-13,	-14,	-14,
-	-86,	-82,	-94,	-90,	-70,	-66,	-78,	-74,
-	-118,	-114,	-126,	-122,	-102,	-98,	-110,	-106,
-	-43,	-41,	-47,	-45,	-35,	-33,	-39,	-37,
-	-59,	-57,	-63,	-61,	-51,	-49,	-55,	-53,
-	-2,	-2,	-2,	-2,	-2,	-2,	-2,	-2,
-	-2,	-2,	-2,	-2,	-2,	-2,	-2,	-2,
-	-1,	-1,	-1,	-1,	-1,	-1,	-1,	-1,
-	-1,	-1,	-1,	-1,	-1,	-1,	-1,	-1,
-	-6,	-6,	-6,	-6,	-5,	-5,	-5,	-5,
-	-8,	-8,	-8,	-8,	-7,	-7,	-7,	-7,
-	-3,	-3,	-3,	-3,	-3,	-3,	-3,	-3,
-	-4,	-4,	-4,	-4,	-4,	-4,	-4,	-4,
-	21,	20,	23,	22,	17,	16,	19,	18,
-	29,	28,	31,	30,	25,	24,	27,	26,
-	10,	10,	11,	11,	8,	8,	9,	9,
-	14,	14,	15,	15,	12,	12,	13,	13,
-	86,	82,	94,	90,	70,	66,	78,	74,
-	118,	114,	126,	122,	102,	98,	110,	106,
-	43,	41,	47,	45,	35,	33,	39,	37,
-	59,	57,	63,	61,	51,	49,	55,	53,
-	1,	1,	1,	1,	1,	1,	1,	1,
-	1,	1,	1,	1,	1,	1,	1,	1,
-	0,	0,	0,	0,	0,	0,	0,	0,
-	0,	0,	0,	0,	0,	0,	0,	0,
-	5,	5,	5,	5,	4,	4,	4,	4,
-	7,	7,	7,	7,	6,	6,	6,	6,
-	2,	2,	2,	2,	2,	2,	2,	2,
-	3,	3,	3,	3,	3,	3,	3,	3
-};
-#endif /* HAS_8BIT_TABLES */
-
-#ifdef HAS_16BIT_TABLES
-
-/* 16 bit mu-law */
-
-short dmasound_ulaw2dma16[] = {
-	-32124,	-31100,	-30076,	-29052,	-28028,	-27004,	-25980,	-24956,
-	-23932,	-22908,	-21884,	-20860,	-19836,	-18812,	-17788,	-16764,
-	-15996,	-15484,	-14972,	-14460,	-13948,	-13436,	-12924,	-12412,
-	-11900,	-11388,	-10876,	-10364,	-9852,	-9340,	-8828,	-8316,
-	-7932,	-7676,	-7420,	-7164,	-6908,	-6652,	-6396,	-6140,
-	-5884,	-5628,	-5372,	-5116,	-4860,	-4604,	-4348,	-4092,
-	-3900,	-3772,	-3644,	-3516,	-3388,	-3260,	-3132,	-3004,
-	-2876,	-2748,	-2620,	-2492,	-2364,	-2236,	-2108,	-1980,
-	-1884,	-1820,	-1756,	-1692,	-1628,	-1564,	-1500,	-1436,
-	-1372,	-1308,	-1244,	-1180,	-1116,	-1052,	-988,	-924,
-	-876,	-844,	-812,	-780,	-748,	-716,	-684,	-652,
-	-620,	-588,	-556,	-524,	-492,	-460,	-428,	-396,
-	-372,	-356,	-340,	-324,	-308,	-292,	-276,	-260,
-	-244,	-228,	-212,	-196,	-180,	-164,	-148,	-132,
-	-120,	-112,	-104,	-96,	-88,	-80,	-72,	-64,
-	-56,	-48,	-40,	-32,	-24,	-16,	-8,	0,
-	32124,	31100,	30076,	29052,	28028,	27004,	25980,	24956,
-	23932,	22908,	21884,	20860,	19836,	18812,	17788,	16764,
-	15996,	15484,	14972,	14460,	13948,	13436,	12924,	12412,
-	11900,	11388,	10876,	10364,	9852,	9340,	8828,	8316,
-	7932,	7676,	7420,	7164,	6908,	6652,	6396,	6140,
-	5884,	5628,	5372,	5116,	4860,	4604,	4348,	4092,
-	3900,	3772,	3644,	3516,	3388,	3260,	3132,	3004,
-	2876,	2748,	2620,	2492,	2364,	2236,	2108,	1980,
-	1884,	1820,	1756,	1692,	1628,	1564,	1500,	1436,
-	1372,	1308,	1244,	1180,	1116,	1052,	988,	924,
-	876,	844,	812,	780,	748,	716,	684,	652,
-	620,	588,	556,	524,	492,	460,	428,	396,
-	372,	356,	340,	324,	308,	292,	276,	260,
-	244,	228,	212,	196,	180,	164,	148,	132,
-	120,	112,	104,	96,	88,	80,	72,	64,
-	56,	48,	40,	32,	24,	16,	8,	0,
-};
-
-/* 16 bit A-law */
-
-short dmasound_alaw2dma16[] = {
-	-5504,	-5248,	-6016,	-5760,	-4480,	-4224,	-4992,	-4736,
-	-7552,	-7296,	-8064,	-7808,	-6528,	-6272,	-7040,	-6784,
-	-2752,	-2624,	-3008,	-2880,	-2240,	-2112,	-2496,	-2368,
-	-3776,	-3648,	-4032,	-3904,	-3264,	-3136,	-3520,	-3392,
-	-22016,	-20992,	-24064,	-23040,	-17920,	-16896,	-19968,	-18944,
-	-30208,	-29184,	-32256,	-31232,	-26112,	-25088,	-28160,	-27136,
-	-11008,	-10496,	-12032,	-11520,	-8960,	-8448,	-9984,	-9472,
-	-15104,	-14592,	-16128,	-15616,	-13056,	-12544,	-14080,	-13568,
-	-344,	-328,	-376,	-360,	-280,	-264,	-312,	-296,
-	-472,	-456,	-504,	-488,	-408,	-392,	-440,	-424,
-	-88,	-72,	-120,	-104,	-24,	-8,	-56,	-40,
-	-216,	-200,	-248,	-232,	-152,	-136,	-184,	-168,
-	-1376,	-1312,	-1504,	-1440,	-1120,	-1056,	-1248,	-1184,
-	-1888,	-1824,	-2016,	-1952,	-1632,	-1568,	-1760,	-1696,
-	-688,	-656,	-752,	-720,	-560,	-528,	-624,	-592,
-	-944,	-912,	-1008,	-976,	-816,	-784,	-880,	-848,
-	5504,	5248,	6016,	5760,	4480,	4224,	4992,	4736,
-	7552,	7296,	8064,	7808,	6528,	6272,	7040,	6784,
-	2752,	2624,	3008,	2880,	2240,	2112,	2496,	2368,
-	3776,	3648,	4032,	3904,	3264,	3136,	3520,	3392,
-	22016,	20992,	24064,	23040,	17920,	16896,	19968,	18944,
-	30208,	29184,	32256,	31232,	26112,	25088,	28160,	27136,
-	11008,	10496,	12032,	11520,	8960,	8448,	9984,	9472,
-	15104,	14592,	16128,	15616,	13056,	12544,	14080,	13568,
-	344,	328,	376,	360,	280,	264,	312,	296,
-	472,	456,	504,	488,	408,	392,	440,	424,
-	88,	72,	120,	104,	24,	8,	56,	40,
-	216,	200,	248,	232,	152,	136,	184,	168,
-	1376,	1312,	1504,	1440,	1120,	1056,	1248,	1184,
-	1888,	1824,	2016,	1952,	1632,	1568,	1760,	1696,
-	688,	656,	752,	720,	560,	528,	624,	592,
-	944,	912,	1008,	976,	816,	784,	880,	848,
-};
-#endif /* HAS_16BIT_TABLES */
-
-
-#ifdef HAS_14BIT_TABLES
-
-    /*
-     *  Unused for now. Where are the MSB parts anyway??
-     */
-
-/* 14 bit mu-law (LSB) */
-
-char dmasound_ulaw2dma14l[] = {
-	33,	33,	33,	33,	33,	33,	33,	33,
-	33,	33,	33,	33,	33,	33,	33,	33,
-	33,	33,	33,	33,	33,	33,	33,	33,
-	33,	33,	33,	33,	33,	33,	33,	33,
-	1,	1,	1,	1,	1,	1,	1,	1,
-	1,	1,	1,	1,	1,	1,	1,	1,
-	49,	17,	49,	17,	49,	17,	49,	17,
-	49,	17,	49,	17,	49,	17,	49,	17,
-	41,	57,	9,	25,	41,	57,	9,	25,
-	41,	57,	9,	25,	41,	57,	9,	25,
-	37,	45,	53,	61,	5,	13,	21,	29,
-	37,	45,	53,	61,	5,	13,	21,	29,
-	35,	39,	43,	47,	51,	55,	59,	63,
-	3,	7,	11,	15,	19,	23,	27,	31,
-	34,	36,	38,	40,	42,	44,	46,	48,
-	50,	52,	54,	56,	58,	60,	62,	0,
-	31,	31,	31,	31,	31,	31,	31,	31,
-	31,	31,	31,	31,	31,	31,	31,	31,
-	31,	31,	31,	31,	31,	31,	31,	31,
-	31,	31,	31,	31,	31,	31,	31,	31,
-	63,	63,	63,	63,	63,	63,	63,	63,
-	63,	63,	63,	63,	63,	63,	63,	63,
-	15,	47,	15,	47,	15,	47,	15,	47,
-	15,	47,	15,	47,	15,	47,	15,	47,
-	23,	7,	55,	39,	23,	7,	55,	39,
-	23,	7,	55,	39,	23,	7,	55,	39,
-	27,	19,	11,	3,	59,	51,	43,	35,
-	27,	19,	11,	3,	59,	51,	43,	35,
-	29,	25,	21,	17,	13,	9,	5,	1,
-	61,	57,	53,	49,	45,	41,	37,	33,
-	30,	28,	26,	24,	22,	20,	18,	16,
-	14,	12,	10,	8,	6,	4,	2,	0
-};
-
-/* 14 bit A-law (LSB) */
-
-char dmasound_alaw2dma14l[] = {
-	32,	32,	32,	32,	32,	32,	32,	32,
-	32,	32,	32,	32,	32,	32,	32,	32,
-	16,	48,	16,	48,	16,	48,	16,	48,
-	16,	48,	16,	48,	16,	48,	16,	48,
-	0,	0,	0,	0,	0,	0,	0,	0,
-	0,	0,	0,	0,	0,	0,	0,	0,
-	0,	0,	0,	0,	0,	0,	0,	0,
-	0,	0,	0,	0,	0,	0,	0,	0,
-	42,	46,	34,	38,	58,	62,	50,	54,
-	10,	14,	2,	6,	26,	30,	18,	22,
-	42,	46,	34,	38,	58,	62,	50,	54,
-	10,	14,	2,	6,	26,	30,	18,	22,
-	40,	56,	8,	24,	40,	56,	8,	24,
-	40,	56,	8,	24,	40,	56,	8,	24,
-	20,	28,	4,	12,	52,	60,	36,	44,
-	20,	28,	4,	12,	52,	60,	36,	44,
-	32,	32,	32,	32,	32,	32,	32,	32,
-	32,	32,	32,	32,	32,	32,	32,	32,
-	48,	16,	48,	16,	48,	16,	48,	16,
-	48,	16,	48,	16,	48,	16,	48,	16,
-	0,	0,	0,	0,	0,	0,	0,	0,
-	0,	0,	0,	0,	0,	0,	0,	0,
-	0,	0,	0,	0,	0,	0,	0,	0,
-	0,	0,	0,	0,	0,	0,	0,	0,
-	22,	18,	30,	26,	6,	2,	14,	10,
-	54,	50,	62,	58,	38,	34,	46,	42,
-	22,	18,	30,	26,	6,	2,	14,	10,
-	54,	50,	62,	58,	38,	34,	46,	42,
-	24,	8,	56,	40,	24,	8,	56,	40,
-	24,	8,	56,	40,	24,	8,	56,	40,
-	44,	36,	60,	52,	12,	4,	28,	20,
-	44,	36,	60,	52,	12,	4,	28,	20
-};
-#endif /* HAS_14BIT_TABLES */
-
+static long long sound_lseek(struct file *file, long long offset, int orig)
+{
+	return -ESPIPE;
+}
 
     /*
      *  Mid level stuff
@@ -393,15 +239,7 @@
 
 static inline void sound_silence(void)
 {
-	/* update hardware settings one more */
-	dmasound.mach.init();
-
-	dmasound.mach.silence();
-}
-
-static inline void sound_init(void)
-{
-	dmasound.mach.init();
+	dmasound.mach.silence(); /* _MUST_ stop DMA */
 }
 
 static inline int sound_set_format(int format)
@@ -414,8 +252,17 @@
 	if (speed < 0)
 		return dmasound.soft.speed;
 
+	/* trap out-of-range speed settings.
+	   at present we allow (arbitrarily) low rates - using soft
+	   up-conversion - but we can't allow > max because there is
+	   no soft down-conversion.
+	*/
+	if (dmasound.mach.max_dsp_speed &&
+	   (speed > dmasound.mach.max_dsp_speed))
+		speed = dmasound.mach.max_dsp_speed ;
+
 	dmasound.soft.speed = speed;
-	dmasound.mach.init();
+
 	if (dmasound.minDev == SND_DEV_DSP)
 		dmasound.dsp.speed = dmasound.soft.speed;
 
@@ -432,7 +279,6 @@
 	dmasound.soft.stereo = stereo;
 	if (dmasound.minDev == SND_DEV_DSP)
 		dmasound.dsp.stereo = stereo;
-	dmasound.mach.init();
 
 	return stereo;
 }
@@ -471,10 +317,14 @@
 	    default:
 		return 0;
 	}
-	return ct_func(userPtr, userCount, frame, frameUsed, frameLeft);
+	/* if the user has requested a non-existent translation don't try
+	   to call it but just return 0 bytes moved
+	*/
+	if (ct_func)
+		return ct_func(userPtr, userCount, frame, frameUsed, frameLeft);
+	return 0;
 }
 
-
     /*
      *  /dev/mixer abstraction
      */
@@ -559,6 +409,11 @@
 struct sound_queue dmasound_read_sq;
 #endif
 
+static void sq_reset_output(void) ;
+#ifdef HAS_RECORD
+static void sq_reset_input(void) ;
+#endif
+
 static int sq_allocate_buffers(struct sound_queue *sq, int num, int size)
 {
 	int i;
@@ -588,8 +443,6 @@
 	int i;
 
 	if (sq->buffers) {
-		if (sq != &write_sq && dmasound.mach.abort_read)
-			dmasound.mach.abort_read();
 		for (i = 0; i < sq->numBufs; i++)
 			dmasound.mach.dma_free(sq->buffers[i], sq->bufSize);
 		kfree(sq->buffers);
@@ -597,15 +450,84 @@
 	}
 }
 
-static void sq_setup(struct sound_queue *sq, int max_count, int max_active,
-		     int block_size)
+
+static int sq_setup(struct sound_queue *sq)
 {
-	void (*setup_func)(void);
+	int (*setup_func)(void);
+	int hard_frame ;
+
+	if (sq->locked) { /* are we already set? - and not changeable */
+#ifdef DEBUG_DMASOUND
+printk("dmasound_core: tried to sq_setup a locked queue\n") ;
+#endif
+		return -EINVAL ;
+	}
+	sq->locked = 1 ; /* don't think we have a race prob. here _check_ */
 
-	sq->max_count = max_count;
-	sq->max_active = max_active;
-	sq->block_size = block_size;
+	/* make sure that the parameters are set up
+	   This should have been done already...
+	*/
+
+	dmasound.mach.init();
 
+	/* OK.  If the user has set fragment parameters explicitly, then we
+	   should leave them alone... as long as they are valid.
+	   Invalid user fragment params can occur if we allow the whole buffer
+	   to be used when the user requests the fragments sizes (with no soft
+	   x-lation) and then the user subsequently sets a soft x-lation that
+	   requires increased internal buffering.
+
+	   Othwerwise (if the user did not set them) OSS says that we should
+	   select frag params on the basis of 0.5 s output & 0.1 s input
+	   latency. (TODO.  For now we will copy in the defaults.)
+	*/
+
+	if (sq->user_frags <= 0) {
+		sq->max_count = sq->numBufs ;
+		sq->max_active = sq->numBufs ;
+		sq->block_size = sq->bufSize;
+		/* set up the user info */
+		sq->user_frags = sq->numBufs ;
+		sq->user_frag_size = sq->bufSize ;
+		sq->user_frag_size *=
+			(dmasound.soft.size * (dmasound.soft.stereo+1) ) ;
+		sq->user_frag_size /=
+			(dmasound.hard.size * (dmasound.hard.stereo+1) ) ;
+	} else {
+		/* work out requested block size */
+		sq->block_size = sq->user_frag_size ;
+		sq->block_size *=
+			(dmasound.hard.size * (dmasound.hard.stereo+1) ) ;
+		sq->block_size /=
+			(dmasound.soft.size * (dmasound.soft.stereo+1) ) ;
+		/* the user wants to write frag-size chunks */
+		sq->block_size *= dmasound.hard.speed ;
+		sq->block_size /= dmasound.soft.speed ;
+		/* this only works for size values which are powers of 2 */
+		hard_frame =
+			(dmasound.hard.size * (dmasound.hard.stereo+1))/8 ;
+		sq->block_size +=  (hard_frame - 1) ;
+		sq->block_size &= ~(hard_frame - 1) ; /* make sure we are aligned */
+		/* let's just check for obvious mistakes */
+		if ( sq->block_size <= 0 || sq->block_size > sq->bufSize) {
+#ifdef DEBUG_DMASOUND
+printk("dmasound_core: invalid frag size (user set %d)\n", sq->user_frag_size) ;
+#endif
+			sq->block_size = sq->bufSize ;
+		}
+		if ( sq->user_frags <= sq->numBufs ) {
+			sq->max_count = sq->user_frags ;
+			/* if user has set max_active - then use it */
+			sq->max_active = (sq->max_active <= sq->max_count) ?
+				sq->max_active : sq->max_count ;
+		} else {
+#ifdef DEBUG_DMASOUND
+printk("dmasound_core: invalid frag count (user set %d)\n", sq->user_frags) ;
+#endif
+			sq->max_count =
+			sq->max_active = sq->numBufs ;
+		}
+	}
 	sq->front = sq->count = sq->rear_size = 0;
 	sq->syncing = 0;
 	sq->active = 0;
@@ -613,12 +535,16 @@
 	if (sq == &write_sq) {
 	    sq->rear = -1;
 	    setup_func = dmasound.mach.write_sq_setup;
-	} else {
+	}
+#ifdef HAS_RECORD
+	else {
 	    sq->rear = 0;
 	    setup_func = dmasound.mach.read_sq_setup;
 	}
+#endif
 	if (setup_func)
-	    setup_func();
+	    return setup_func();
+	return 0 ;
 }
 
 static inline void sq_play(void)
@@ -632,6 +558,7 @@
 	ssize_t uWritten = 0;
 	u_char *dest;
 	ssize_t uUsed, bUsed, bLeft;
+	unsigned long flags ;
 
 	/* ++TeSche: Is something like this necessary?
 	 * Hey, that's an honest question! Or does any other part of the
@@ -640,11 +567,52 @@
 	if (uLeft == 0)
 		return 0;
 
+	/* implement any changes we have made to the soft/hard params.
+	   this is not satisfactory really, all we have done up to now is to
+	   say what we would like - there hasn't been any real checking of capability
+	*/
+
+	if (shared_resources_initialised == 0) {
+		dmasound.mach.init() ;
+		shared_resources_initialised = 1 ;
+	}
+
+	/* set up the sq if it is not already done. This may seem a dumb place
+	   to do it - but it is what OSS requires.  It means that write() can
+	   return memory allocation errors.  To avoid this possibility use the
+	   GETBLKSIZE or GETOSPACE ioctls (after you've fiddled with all the
+	   params you want to change) - these ioctls also force the setup.
+	*/
+
+	if (write_sq.locked == 0) {
+		if ((uWritten = sq_setup(&write_sq)) < 0) return uWritten ;
+		uWritten = 0 ;
+	}
+
+/* FIXME: I think that this may be the wrong behaviour when we get strapped
+	for time and the cpu is close to being (or actually) behind in sending data.
+	- because we've lost the time that the N samples, already in the buffer,
+	would have given us to get here with the next lot from the user.
+*/
 	/* The interrupt doesn't start to play the last, incomplete frame.
 	 * Thus we can append to it without disabling the interrupts! (Note
 	 * also that write_sq.rear isn't affected by the interrupt.)
 	 */
 
+	/* as of 1.6 this behaviour changes if SNDCTL_DSP_POST has been issued:
+	   this will mimic the behaviour of syncing and allow the sq_play() to
+	   queue a partial fragment.  Since sq_play() may/will be called from
+	   the IRQ handler - at least on Pmac we have to deal with it.
+	   The strategy - possibly not optimum - is to kill _POST status if we
+	   get here.  This seems, at least, reasonable - in the sense that POST
+	   is supposed to indicate that we might not write before the queue
+	   is drained - and if we get here in time then it does not apply.
+	*/
+
+	save_flags(flags) ; cli() ;
+	write_sq.syncing &= ~2 ; /* take out POST status */
+	restore_flags(flags) ;
+
 	if (write_sq.count > 0 &&
 	    (bLeft = write_sq.block_size-write_sq.rear_size) > 0) {
 		dest = write_sq.buffers[write_sq.rear];
@@ -655,12 +623,12 @@
 			return uUsed;
 		src += uUsed;
 		uWritten += uUsed;
-		uLeft -= uUsed;
+		uLeft = (uUsed <= uLeft) ? (uLeft - uUsed) : 0 ; /* paranoia */
 		write_sq.rear_size = bUsed;
 	}
 
-	do {
-		while (write_sq.count == write_sq.max_active) {
+	while (uLeft) {
+		while (write_sq.count >= write_sq.max_active) {
 			sq_play();
 			if (write_sq.open_mode & O_NONBLOCK)
 				return uWritten > 0 ? uWritten : -EAGAIN;
@@ -685,19 +653,45 @@
 			break;
 		src += uUsed;
 		uWritten += uUsed;
-		uLeft -= uUsed;
+		uLeft = (uUsed <= uLeft) ? (uLeft - uUsed) : 0 ; /* paranoia */
 		if (bUsed) {
 			write_sq.rear = (write_sq.rear+1) % write_sq.max_count;
 			write_sq.rear_size = bUsed;
 			write_sq.count++;
 		}
-	} while (bUsed);   /* uUsed may have been 0 */
+	} /* uUsed may have been 0 */
 
 	sq_play();
 
 	return uUsed < 0? uUsed: uWritten;
 }
 
+static unsigned int sq_poll(struct file *file, struct poll_table_struct *wait)
+{
+	unsigned int mask = 0;
+	int retVal;
+	
+	if (write_sq.locked == 0) {
+		if ((retVal = sq_setup(&write_sq)) < 0)
+			return retVal;
+		return 0;
+	}
+	if (file->f_mode & FMODE_WRITE )
+		poll_wait(file, &write_sq.action_queue, wait);
+#ifdef HAS_RECORD
+	if (file->f_mode & FMODE_READ)
+		poll_wait(file, &read_sq.action_queue, wait);
+	if (file->f_mode & FMODE_READ)
+		if (read_sq.block_size - read_sq.rear_size > 0)
+			mask |= POLLIN | POLLRDNORM;
+#endif
+	if (file->f_mode & FMODE_WRITE)
+		if (write_sq.count < write_sq.max_active || write_sq.block_size - write_sq.rear_size > 0)
+			mask |= POLLOUT | POLLWRNORM;
+	return mask;
+
+}
+
 #ifdef HAS_RECORD
     /*
      *  Here is how the values are used for reading.
@@ -707,9 +701,12 @@
      *  The value 'rear' indicates the buffer the DMA is currently filling.
      *  When 'front' == 'rear' the buffer "ring" is empty (we always have an
      *  empty available).  The 'rear_size' is used to track partial offsets
-     *  into the current buffer.  Right now, I just keep the DMA running.  If
-     *  the reader can't keep up, the interrupt tosses the oldest buffer.  We
-     *  could also shut down the DMA in this case.
+     *  into the buffer we are currently returning to the user.
+
+     *  This level (> [1.5]) doesn't care what strategy the LL driver uses with
+     *  DMA on over-run.  It can leave it running (and keep active == 1) or it
+     *  can kill it and set active == 0 in which case this routine will spot
+     *  it and restart the DMA.
      */
 
 static ssize_t sq_read(struct file *file, char *dst, size_t uLeft,
@@ -721,8 +718,25 @@
 	if (uLeft == 0)
 		return 0;
 
-	if (!read_sq.active && dmasound.mach.record)
-		dmasound.mach.record();	/* Kick off the record process. */
+	/* cater for the compatibility mode - record compiled in but no LL */
+	if (dmasound.mach.record == NULL)
+		return -EINVAL ;
+
+	/* see comment in sq_write()
+	*/
+
+	if( shared_resources_initialised == 0) {
+		dmasound.mach.init() ;
+		shared_resources_initialised = 1 ;
+	}
+
+	/* set up the sq if it is not already done. see comments in sq_write().
+	*/
+
+	if (read_sq.locked == 0) {
+		if ((uRead = sq_setup(&read_sq)) < 0)
+			return uRead ;
+	}
 
 	uRead = 0;
 
@@ -730,6 +744,13 @@
 	*/
 	while (uLeft > 0) {
 
+		/* we happened to get behind and the LL driver killed DMA
+		   then we should set it going again.  This also sets it
+		   going the first time through.
+		*/
+		if ( !read_sq.active )
+			dmasound.mach.record();
+
 		/* When front == rear, the DMA is not done yet.
 		*/
 		while (read_sq.front == read_sq.rear) {
@@ -774,14 +795,16 @@
 	sq->busy = 0;
 }
 
+#if 0 /* blocking open() */
 static inline void sq_wake_up(struct sound_queue *sq, struct file *file,
 			      mode_t mode)
 {
 	if (file->f_mode & mode) {
-		sq->busy = 0;
+		sq->busy = 0; /* CHECK: IS THIS OK??? */
 		WAKE_UP(sq->open_queue);
 	}
 }
+#endif
 
 static int sq_open2(struct sound_queue *sq, struct file *file, mode_t mode,
 		    int numbufs, int bufsize)
@@ -790,6 +813,7 @@
 
 	if (file->f_mode & mode) {
 		if (sq->busy) {
+#if 0 /* blocking open() */
 			rc = -EBUSY;
 			if (file->f_flags & O_NONBLOCK)
 				return rc;
@@ -800,84 +824,188 @@
 					return rc;
 			}
 			rc = 0;
+#else
+			/* OSS manual says we will return EBUSY regardless
+			   of O_NOBLOCK.
+			*/
+			return -EBUSY ;
+#endif
 		}
 		sq->busy = 1; /* Let's play spot-the-race-condition */
 
-		if (sq_allocate_buffers(sq, numbufs, bufsize)) {
+		/* allocate the default number & size of buffers.
+		   (i.e. specified in _setup() or as module params)
+		   can't be changed at the moment - but _could_ be perhaps
+		   in the setfragments ioctl.
+		*/
+		if (( rc = sq_allocate_buffers(sq, numbufs, bufsize))) {
+#if 0 /* blocking open() */
 			sq_wake_up(sq, file, mode);
+#else
+			sq->busy = 0 ;
+#endif
 			return rc;
 		}
 
-		sq_setup(sq, numbufs, numbufs, bufsize);
 		sq->open_mode = file->f_mode;
 	}
 	return rc;
 }
 
 #define write_sq_init_waitqueue()	sq_init_waitqueue(&write_sq)
+#if 0 /* blocking open() */
 #define write_sq_wake_up(file)		sq_wake_up(&write_sq, file, FMODE_WRITE)
+#endif
 #define write_sq_release_buffers()	sq_release_buffers(&write_sq)
 #define write_sq_open(file)	\
-	sq_open2(&write_sq, file, FMODE_WRITE, numWriteBufs, writeBufSize << 10)
+	sq_open2(&write_sq, file, FMODE_WRITE, numWriteBufs, writeBufSize )
 
 #ifdef HAS_RECORD
 #define read_sq_init_waitqueue()	sq_init_waitqueue(&read_sq)
+#if 0 /* blocking open() */
 #define read_sq_wake_up(file)		sq_wake_up(&read_sq, file, FMODE_READ)
+#endif
 #define read_sq_release_buffers()	sq_release_buffers(&read_sq)
 #define read_sq_open(file)	\
-	sq_open2(&read_sq, file, FMODE_READ, numReadBufs, readBufSize << 10)
-#else /* !HAS_RECORD */
-#define read_sq_init_waitqueue()	do {} while (0)
-#define read_sq_wake_up(file)		do {} while (0)
-#define read_sq_release_buffers()	do {} while (0)
-#define read_sq_open(file)		(0)
-#endif /* !HAS_RECORD */
+	sq_open2(&read_sq, file, FMODE_READ, numReadBufs, readBufSize )
+#endif
 
 static int sq_open(struct inode *inode, struct file *file)
 {
 	int rc;
 
 	dmasound.mach.open();
-	if ((rc = write_sq_open(file)) || (rc = read_sq_open(file))) {
+
+	if ((rc = write_sq_open(file))) { /* checks the f_mode */
 		dmasound.mach.release();
 		return rc;
 	}
+#ifdef HAS_RECORD
+	if (dmasound.mach.record) {
+		if ((rc = read_sq_open(file))) { /* checks the f_mode */
+			dmasound.mach.release();
+			return rc;
+		}
+	} else { /* no record function installed; in compat mode */
+		if (file->f_mode & FMODE_READ) {
+			/* TODO: if O_RDWR, release any resources grabbed by write part */
+			dmasound.mach.release() ;
+			/* I think this is what is required by open(2) */
+			return -ENXIO ;
+		}
+	}
+#else /* !HAS_RECORD */
+	if (file->f_mode & FMODE_READ) {
+		/* TODO: if O_RDWR, release any resources grabbed by write part */
+		dmasound.mach.release() ;
+		return -ENXIO ; /* I think this is what is required by open(2) */
+	}
+#endif /* HAS_RECORD */
 
 	if (dmasound.mach.sq_open)
-	    dmasound.mach.sq_open();
+	    dmasound.mach.sq_open(file->f_mode);
+
+	/* CHECK whether this is sensible - in the case that dsp0 could be opened
+	  O_RDONLY and dsp1 could be opened O_WRONLY
+	*/
+
 	dmasound.minDev = MINOR(inode->i_rdev) & 0x0f;
-	dmasound.soft = dmasound.dsp;
-	dmasound.hard = dmasound.dsp;
-	sound_init();
-	if ((MINOR(inode->i_rdev) & 0x0f) == SND_DEV_AUDIO) {
+
+	/* OK. - we should make some attempt at consistency. At least the H'ware
+	   options should be set with a valid mode.  We will make it that the LL
+	   driver must supply defaults for hard & soft params.
+	*/
+
+	if (shared_resource_owner == 0) {
+		/* you can make this AFMT_U8/mono/8K if you want to mimic old
+		   OSS behaviour - while we still have soft translations ;-) */
+		dmasound.soft = dmasound.mach.default_soft ;
+		dmasound.dsp = dmasound.mach.default_soft ;
+		dmasound.hard = dmasound.mach.default_hard ;
+	}
+
+#ifndef DMASOUND_STRICT_OSS_COMPLIANCE
+	/* none of the current LL drivers can actually do this "native" at the moment
+	   OSS does not really require us to supply /dev/audio if we can't do it.
+	*/
+	if (dmasound.minDev == SND_DEV_AUDIO) {
 		sound_set_speed(8000);
 		sound_set_stereo(0);
 		sound_set_format(AFMT_MU_LAW);
 	}
-
-#if 0
-	if (file->f_mode == FMODE_READ && dmasound.mach.record) {
-		/* Start dma'ing straight away */
-		dmasound.mach.record();
-	}
 #endif
 
 	return 0;
 }
 
-static void sq_reset(void)
+static void sq_reset_output(void)
 {
-	sound_silence();
+	sound_silence(); /* this _must_ stop DMA, we might be about to lose the buffers */
 	write_sq.active = 0;
 	write_sq.count = 0;
-	write_sq.front = (write_sq.rear+1) % write_sq.max_count;
+	write_sq.rear_size = 0;
+	/* write_sq.front = (write_sq.rear+1) % write_sq.max_count;*/
+	write_sq.front = 0 ;
+	write_sq.rear = -1 ; /* same as for set-up */
+
+	/* OK - we can unlock the parameters and fragment settings */
+	write_sq.locked = 0 ;
+	write_sq.user_frags = 0 ;
+	write_sq.user_frag_size = 0 ;
+}
+
+#ifdef HAS_RECORD
+
+static void sq_reset_input(void)
+{
+	if (dmasound.mach.record && read_sq.active) {
+		if (dmasound.mach.abort_read) { /* this routine must really be present */
+			read_sq.syncing = 1 ;
+			/* this can use the read_sq.sync_queue to sleep if
+			   necessary - it should not return until DMA
+			   is really stopped - because we might deallocate
+			   the buffers as the next action...
+			*/
+			dmasound.mach.abort_read() ;
+		} else {
+			printk(KERN_ERR
+			"dmasound_core: %s has no abort_read()!! all bets are off\n",
+				dmasound.mach.name) ;
+		}
+	}
+	read_sq.syncing =
+	read_sq.active =
+	read_sq.front =
+	read_sq.count =
+	read_sq.rear = 0 ;
+
+	/* OK - we can unlock the parameters and fragment settings */
+	read_sq.locked = 0 ;
+	read_sq.user_frags = 0 ;
+	read_sq.user_frag_size = 0 ;
+}
+
+#endif
+
+static void sq_reset(void)
+{
+	sq_reset_output() ;
+#ifdef HAS_RECORD
+	sq_reset_input() ;
+#endif
+	/* we could consider resetting the shared_resources_owner here... but I
+	   think it is probably still rather non-obvious to application writer
+	*/
+
+	/* we release everything else though */
+	shared_resources_initialised = 0 ;
 }
 
 static int sq_fsync(struct file *filp, struct dentry *dentry)
 {
 	int rc = 0;
 
-	write_sq.syncing = 1;
+	write_sq.syncing |= 1;
 	sq_play();	/* there may be an incomplete frame waiting */
 
 	while (write_sq.active) {
@@ -886,13 +1014,14 @@
 			/* While waiting for audio output to drain, an
 			 * interrupt occurred.  Stop audio output immediately
 			 * and clear the queue. */
-			sq_reset();
+			sq_reset_output();
 			rc = -EINTR;
 			break;
 		}
 	}
 
-	write_sq.syncing = 0;
+	/* flag no sync regardless of whether we had a DSP_POST or not */
+	write_sq.syncing = 0 ;
 	return rc;
 }
 
@@ -901,33 +1030,132 @@
 	int rc = 0;
 
 	lock_kernel();
-	if (write_sq.busy)
-		rc = sq_fsync(file, file->f_dentry);
-	dmasound.soft = dmasound.dsp;
-	dmasound.hard = dmasound.dsp;
-	sound_silence();
 
-	write_sq_release_buffers();
-	read_sq_release_buffers();
-	dmasound.mach.release();
+#ifdef HAS_RECORD
+	/* probably best to do the read side first - so that time taken to do it
+	   overlaps with playing any remaining output samples.
+	*/
+	if (file->f_mode & FMODE_READ) {
+		sq_reset_input() ; /* make sure dma is stopped and all is quiet */
+		read_sq_release_buffers();
+		read_sq.busy = 0;
+	}
+#endif
 
-	/* There is probably a DOS atack here. They change the mode flag. */
-	/* XXX add check here */
-	read_sq_wake_up(file);
-	write_sq_wake_up(file);
+	if (file->f_mode & FMODE_WRITE) {
+		if (write_sq.busy)
+			rc = sq_fsync(file, file->f_dentry);
+
+		sq_reset_output() ; /* make sure dma is stopped and all is quiet */
+		write_sq_release_buffers();
+		write_sq.busy = 0;
+	}
+
+	if (file->f_mode & shared_resource_owner) { /* it's us that has them */
+		shared_resource_owner = 0 ;
+		shared_resources_initialised = 0 ;
+		dmasound.hard = dmasound.mach.default_hard ;
+	}
+
+	dmasound.mach.release();
 
+#if 0 /* blocking open() */
 	/* Wake up a process waiting for the queue being released.
 	 * Note: There may be several processes waiting for a call
 	 * to open() returning. */
+
+	/* Iain: hmm I don't understand this next comment ... */
+	/* There is probably a DOS atack here. They change the mode flag. */
+	/* XXX add check here,*/
+#ifdef HAS_RECORD
+	read_sq_wake_up(file); /* checks f_mode */
+#endif
+	write_sq_wake_up(file); /* checks f_mode */
+#endif /* blocking open() */
+
 	unlock_kernel();
 
 	return rc;
 }
 
+/* here we see if we have a right to modify format, channels, size and so on
+   if no-one else has claimed it already then we do...
+
+   TODO: We might change this to mask O_RDWR such that only one or the other channel
+   is the owner - if we have problems.
+*/
+
+static int shared_resources_are_mine(mode_t md)
+{
+	if (shared_resource_owner)
+		return (shared_resource_owner & md ) ;
+	else {
+		shared_resource_owner = md ;
+		return 1 ;
+	}
+}
+
+/* if either queue is locked we must deny the right to change shared params
+*/
+
+static int queues_are_quiescent(void)
+{
+#ifdef HAS_RECORD
+	if (dmasound.mach.record)
+		if (read_sq.locked)
+			return 0 ;
+#endif
+	if (write_sq.locked)
+		return 0 ;
+	return 1 ;
+}
+
+/* check and set a queue's fragments per user's wishes...
+   we will check against the pre-defined literals and the actual sizes.
+   This is a bit fraught - because soft translations can mess with our
+   buffer requirements *after* this call - OSS says "call setfrags first"
+*/
+
+/* It is possible to replace all the -EINVAL returns with an override that
+   just puts the allowable value in.  This may be what many OSS apps require
+*/
+
+static int set_queue_frags(struct sound_queue *sq, int bufs, int size)
+{
+	if (sq->locked) {
+#ifdef DEBUG_DMASOUND
+printk("dmasound_core: tried to set_queue_frags on a locked queue\n") ;
+#endif
+		return -EINVAL ;
+	}
+
+	if ((size < MIN_FRAG_SIZE) || (size > MAX_FRAG_SIZE))
+		return -EINVAL ;
+	size = (1<<size) ; /* now in bytes */
+	if (size > sq->bufSize)
+		return -EINVAL ; /* this might still not work */
+
+	if (bufs <= 0)
+		return -EINVAL ;
+	if (bufs > sq->numBufs) /* the user is allowed say "don't care" with 0x7fff */
+		bufs = sq->numBufs ;
+
+	/* there is, currently, no way to specify max_active separately
+	   from max_count.  This could be a LL driver issue - I guess
+	   if there is a requirement for these values to be different then
+	  we will have to pass that info. up to this level.
+	*/
+	sq->user_frags =
+	sq->max_active = bufs ;
+	sq->user_frag_size = size ;
+
+	return 0 ;
+}
+
 static int sq_ioctl(struct inode *inode, struct file *file, u_int cmd,
 		    u_long arg)
 {
-	int val;
+	int val, result;
 	u_long fmt;
 	int data;
 	int size, nbufs;
@@ -937,85 +1165,167 @@
 	case SNDCTL_DSP_RESET:
 		sq_reset();
 		return 0;
+		break ;
+	case SNDCTL_DSP_GETFMTS:
+		fmt = dmasound.mach.hardware_afmts ; /* this is what OSS says.. */
+		return IOCTL_OUT(arg, fmt);
+		break ;
+	case SNDCTL_DSP_GETBLKSIZE:
+		/* this should tell the caller about bytes that the app can
+		   read/write - the app doesn't care about our internal buffers.
+		   We force sq_setup() here as per OSS 1.1 (which should
+		   compute the values necessary).
+		   Since there is no mechanism to specify read/write separately, for
+		   fds opened O_RDWR, the write_sq values will, arbitrarily, overwrite
+		   the read_sq ones.
+		*/
+		size = 0 ;
+#ifdef HAS_RECORD
+		if (dmasound.mach.record && (file->f_mode & FMODE_READ)) {
+			if ( !read_sq.locked )
+				sq_setup(&read_sq) ; /* set params */
+			size = read_sq.user_frag_size ;
+		}
+#endif
+		if (file->f_mode & FMODE_WRITE) {
+			if ( !write_sq.locked )
+				sq_setup(&write_sq) ;
+			size = write_sq.user_frag_size ;
+		}
+		return IOCTL_OUT(arg, size);
+		break ;
 	case SNDCTL_DSP_POST:
+		/* all we are going to do is to tell the LL that any
+		   partial frags can be queued for output.
+		   The LL will have to clear this flag when last output
+		   is queued.
+		*/
+		write_sq.syncing |= 0x2 ;
+		sq_play() ;
+		return 0 ;
 	case SNDCTL_DSP_SYNC:
-		return sq_fsync(file, file->f_dentry);
-
-		/* ++TeSche: before changing any of these it's
-		 * probably wise to wait until sound playing has
-		 * settled down. */
+		/* This call, effectively, has the same behaviour as SNDCTL_DSP_RESET
+		   except that it waits for output to finish before resetting
+		   everything - read, however, is killed imediately.
+		*/
+		result = 0 ;
+#ifdef HAS_RECORD
+		if ((file->f_mode & FMODE_READ) && dmasound.mach.record)
+			sq_reset_input() ;
+#endif
+		if (file->f_mode & FMODE_WRITE) {
+			result = sq_fsync(file, file->f_dentry);
+			sq_reset_output() ;
+		}
+		/* if we are the shared resource owner then release them */
+		if (file->f_mode & shared_resource_owner)
+			shared_resources_initialised = 0 ;
+		return result ;
+		break ;
 	case SNDCTL_DSP_SPEED:
-		sq_fsync(file, file->f_dentry);
-		IOCTL_IN(arg, data);
-		return IOCTL_OUT(arg, sound_set_speed(data));
+		/* changing this on the fly will have wierd effects on the sound.
+		   Where there are rate conversions implemented in soft form - it
+		   will cause the _ctx_xxx() functions to be substituted.
+		   However, there doesn't appear to be any reason to dis-allow it from
+		   a driver pov.
+		*/
+		if (shared_resources_are_mine(file->f_mode)) {
+			IOCTL_IN(arg, data);
+			data = sound_set_speed(data) ;
+			shared_resources_initialised = 0 ;
+			return IOCTL_OUT(arg, data);
+		} else
+			return -EINVAL ;
+		break ;
+	/* OSS says these next 4 actions are undefined when the device is
+	   busy/active - we will just return -EINVAL.
+	   To be allowed to change one - (a) you have to own the right
+	    (b) the queue(s) must be quiescent
+	*/
 	case SNDCTL_DSP_STEREO:
-		sq_fsync(file, file->f_dentry);
-		IOCTL_IN(arg, data);
-		return IOCTL_OUT(arg, sound_set_stereo(data));
+		if (shared_resources_are_mine(file->f_mode) &&
+		    queues_are_quiescent()) {
+			IOCTL_IN(arg, data);
+			shared_resources_initialised = 0 ;
+			return IOCTL_OUT(arg, sound_set_stereo(data));
+		} else
+			return -EINVAL ;
+		break ;
 	case SOUND_PCM_WRITE_CHANNELS:
-		sq_fsync(file, file->f_dentry);
-		IOCTL_IN(arg, data);
-		return IOCTL_OUT(arg, sound_set_stereo(data-1)+1);
+		if (shared_resources_are_mine(file->f_mode) &&
+		    queues_are_quiescent()) {
+			IOCTL_IN(arg, data);
+			/* the user might ask for 20 channels, we will return 1 or 2 */
+			shared_resources_initialised = 0 ;
+			return IOCTL_OUT(arg, sound_set_stereo(data-1)+1);
+		} else
+			return -EINVAL ;
+		break ;
 	case SNDCTL_DSP_SETFMT:
-		sq_fsync(file, file->f_dentry);
-		IOCTL_IN(arg, data);
-		return IOCTL_OUT(arg, sound_set_format(data));
-	case SNDCTL_DSP_GETFMTS:
-		fmt = 0;
-		if (dmasound.trans_write) {
-			if (dmasound.trans_write->ct_ulaw)
-				fmt |= AFMT_MU_LAW;
-			if (dmasound.trans_write->ct_alaw)
-				fmt |= AFMT_A_LAW;
-			if (dmasound.trans_write->ct_s8)
-				fmt |= AFMT_S8;
-			if (dmasound.trans_write->ct_u8)
-				fmt |= AFMT_U8;
-			if (dmasound.trans_write->ct_s16be)
-				fmt |= AFMT_S16_BE;
-			if (dmasound.trans_write->ct_u16be)
-				fmt |= AFMT_U16_BE;
-			if (dmasound.trans_write->ct_s16le)
-				fmt |= AFMT_S16_LE;
-			if (dmasound.trans_write->ct_u16le)
-				fmt |= AFMT_U16_LE;
-		}
-		return IOCTL_OUT(arg, fmt);
-	case SNDCTL_DSP_GETBLKSIZE:
-		size = write_sq.block_size
-			* dmasound.soft.size * (dmasound.soft.stereo + 1)
-			/ (dmasound.hard.size * (dmasound.hard.stereo + 1));
-		return IOCTL_OUT(arg, size);
+		if (shared_resources_are_mine(file->f_mode) &&
+		    queues_are_quiescent()) {
+		    	int format;
+			IOCTL_IN(arg, data);
+			shared_resources_initialised = 0 ;
+			format = sound_set_format(data);
+			result = IOCTL_OUT(arg, format);
+			if (result < 0)
+				return result;
+			if (format != data)
+				return -EINVAL;
+			return 0;
+		} else
+			return -EINVAL ;
+		break ;
 	case SNDCTL_DSP_SUBDIVIDE:
+		return -EINVAL ;
 		break;
 	case SNDCTL_DSP_SETFRAGMENT:
-		if (write_sq.count || write_sq.active || write_sq.syncing)
-			return -EINVAL;
-		IOCTL_IN(arg, size);
-		nbufs = size >> 16;
-		if (nbufs < 2 || nbufs > write_sq.numBufs)
-			nbufs = write_sq.numBufs;
-		size &= 0xffff;
-		if (size >= 8 && size <= 29) {
-			size = 1 << size;
-			size *= dmasound.hard.size * (dmasound.hard.stereo + 1);
-			size /= dmasound.soft.size * (dmasound.soft.stereo + 1);
-			if (size > write_sq.bufSize)
-				size = write_sq.bufSize;
-		} else
-			size = write_sq.bufSize;
-		sq_setup(&write_sq, write_sq.numBufs, nbufs, size);
-		return IOCTL_OUT(arg,write_sq.bufSize | write_sq.numBufs << 16);
+		/* we can do this independently for the two queues - with the
+		   proviso that for fds opened O_RDWR we cannot separate the
+		   actions and both queues will be set per the last call.
+		   NOTE: this does *NOT* actually set the queue up - merely
+		   registers our intentions.
+		*/
+		IOCTL_IN(arg, data);
+		result = 0 ;
+		nbufs = (data >> 16) & 0x7fff ; /* 0x7fff is 'use maximum' */
+		size = data & 0xffff;
+#ifdef HAS_RECORD
+		if ((file->f_mode & FMODE_READ) && dmasound.mach.record) {
+			result = set_queue_frags(&read_sq, nbufs, size) ;
+			if (result)
+				return result ;
+		}
+#endif
+		if (file->f_mode & FMODE_WRITE) {
+			result = set_queue_frags(&write_sq, nbufs, size) ;
+			if (result)
+				return result ;
+		}
+		/* NOTE: this return value is irrelevant - OSS specifically says that
+		   the value is 'random' and that the user _must_ check the actual
+		   frags values using SNDCTL_DSP_GETBLKSIZE or similar */
+		return IOCTL_OUT(arg, data);
+		break ;
 	case SNDCTL_DSP_GETOSPACE:
-		info.fragments = write_sq.max_active - write_sq.count;
-		info.fragstotal = write_sq.max_active;
-		info.fragsize = write_sq.block_size;
-		info.bytes = info.fragments * info.fragsize;
-		if (copy_to_user((void *)arg, &info, sizeof(info)))
-			return -EFAULT;
-		return 0;
+		/*
+		*/
+		if (file->f_mode & FMODE_WRITE) {
+			if ( !write_sq.locked )
+				sq_setup(&write_sq) ;
+			info.fragments = write_sq.max_active - write_sq.count;
+			info.fragstotal = write_sq.max_active;
+			info.fragsize = write_sq.user_frag_size;
+			info.bytes = info.fragments * info.fragsize;
+			if (copy_to_user((void *)arg, &info, sizeof(info)))
+				return -EFAULT;
+			return 0;
+		} else
+			return -EINVAL ;
+		break ;
 	case SNDCTL_DSP_GETCAPS:
-		val = 1;        /* Revision level of this ioctl() */
+		val = dmasound.mach.capabilities & 0xffffff00;
 		return IOCTL_OUT(arg,val);
 
 	default:
@@ -1029,42 +1339,48 @@
 	owner:		THIS_MODULE,
 	llseek:		no_llseek,
 	write:		sq_write,
+	poll:		sq_poll,
 	ioctl:		sq_ioctl,
 	open:		sq_open,
 	release:	sq_release,
 #ifdef HAS_RECORD
-	read:		sq_read,
+	read:		NULL	/* default to no read for compat mode */
 #endif
 };
 
-static void __init sq_init(void)
+static int __init sq_init(void)
 {
 #ifndef MODULE
 	int sq_unit;
 #endif
+
+#ifdef HAS_RECORD
+	if (dmasound.mach.record)
+		sq_fops.read = sq_read ;
+#endif
 	sq_unit = register_sound_dsp(&sq_fops, -1);
-	if (sq_unit < 0)
-		return;
+	if (sq_unit < 0) {
+		printk(KERN_ERR "dmasound_core: couldn't register fops\n") ;
+		return sq_unit ;
+	}
 
 	write_sq_init_waitqueue();
+#ifdef HAS_RECORD
 	read_sq_init_waitqueue();
+#endif
 
-	/* whatever you like as startup mode for /dev/dsp,
-	 * (/dev/audio hasn't got a startup mode). note that
-	 * once changed a new open() will *not* restore these!
+	/* These parameters will be restored for every clean open()
+	 * in the case of multiple open()s (e.g. dsp0 & dsp1) they
+	 * will be set so long as the shared resources have no owner.
 	 */
-	dmasound.dsp.format = AFMT_U8;
-	dmasound.dsp.stereo = 0;
-	dmasound.dsp.size = 8;
-
-	/* set minimum rate possible without expanding */
-	dmasound.dsp.speed = dmasound.mach.min_dsp_speed;
-
-	/* before the first open to /dev/dsp this wouldn't be set */
-	dmasound.soft = dmasound.dsp;
-	dmasound.hard = dmasound.dsp;
 
-	sound_silence();
+	if (shared_resource_owner == 0) {
+		dmasound.soft = dmasound.mach.default_soft ;
+		dmasound.hard = dmasound.mach.default_hard ;
+		dmasound.dsp = dmasound.mach.default_soft ;
+		shared_resources_initialised = 0 ;
+	}
+	return 0 ;
 }
 
 
@@ -1072,12 +1388,68 @@
      *  /dev/sndstat
      */
 
+/* we allow more space for record-enabled because there are extra output lines.
+   the number here must include the amount we are prepared to give to the low-level
+   driver.
+*/
+
+#ifdef HAS_RECORD
+#define STAT_BUFF_LEN 1024
+#else
+#define STAT_BUFF_LEN 768
+#endif
+
+/* this is how much space we will allow the low-level driver to use
+   in the stat buffer.  Currently, 2 * (80 character line + <NL>).
+   We do not police this (it is up to the ll driver to be honest).
+*/
+
+#define LOW_LEVEL_STAT_ALLOC 162
+
 static struct {
     int busy;
-    char buf[512];	/* state.buf should not overflow! */
+    char buf[STAT_BUFF_LEN];	/* state.buf should not overflow! */
     int len, ptr;
 } state;
 
+/* publish this function for use by low-level code, if required */
+
+char *get_afmt_string(int afmt)
+{
+        switch(afmt) {
+            case AFMT_MU_LAW:
+                return "mu-law";
+                break;
+            case AFMT_A_LAW:
+                return "A-law";
+                break;
+            case AFMT_U8:
+                return "unsigned 8 bit";
+                break;
+            case AFMT_S8:
+                return "signed 8 bit";
+                break;
+            case AFMT_S16_BE:
+                return "signed 16 bit BE";
+                break;
+            case AFMT_U16_BE:
+                return "unsigned 16 bit BE";
+                break;
+            case AFMT_S16_LE:
+                return "signed 16 bit LE";
+                break;
+            case AFMT_U16_LE:
+                return "unsigned 16 bit LE";
+                break;
+	    case 0:
+		return "format not set" ;
+		break ;
+            default:
+                break ;
+        }
+        return "ERROR: Unsupported AFMT_XXXX code" ;
+}
+
 static int state_open(struct inode *inode, struct file *file)
 {
 	char *buffer = state.buf;
@@ -1090,52 +1462,76 @@
 	state.ptr = 0;
 	state.busy = 1;
 
-	len += sprintf(buffer+len, "%sDMA sound driver:\n", dmasound.mach.name);
+	len += sprintf(buffer+len, "%sDMA sound driver rev %03d :\n",
+		dmasound.mach.name, (DMASOUND_CORE_REVISION<<4) +
+		((dmasound.mach.version>>8) & 0x0f));
+	len += sprintf(buffer+len,
+		"Core driver edition %02d.%02d : %s driver edition %02d.%02d\n",
+		DMASOUND_CORE_REVISION, DMASOUND_CORE_EDITION, dmasound.mach.name2,
+		(dmasound.mach.version >> 8), (dmasound.mach.version & 0xff)) ;
+
+	/* call the low-level module to fill in any stat info. that it has
+	   if present.  Maximum buffer usage is specified.
+	*/
 
-	len += sprintf(buffer+len, "\tsound.format = 0x%x",
-		       dmasound.soft.format);
-	switch (dmasound.soft.format) {
-	    case AFMT_MU_LAW:
-		len += sprintf(buffer+len, " (mu-law)");
-		break;
-	    case AFMT_A_LAW:
-		len += sprintf(buffer+len, " (A-law)");
-		break;
-	    case AFMT_U8:
-		len += sprintf(buffer+len, " (unsigned 8 bit)");
-		break;
-	    case AFMT_S8:
-		len += sprintf(buffer+len, " (signed 8 bit)");
-		break;
-	    case AFMT_S16_BE:
-		len += sprintf(buffer+len, " (signed 16 bit big)");
-		break;
-	    case AFMT_U16_BE:
-		len += sprintf(buffer+len, " (unsigned 16 bit big)");
-		break;
-	    case AFMT_S16_LE:
-		len += sprintf(buffer+len, " (signed 16 bit little)");
-		break;
-	    case AFMT_U16_LE:
-		len += sprintf(buffer+len, " (unsigned 16 bit little)");
-		break;
-	}
-	len += sprintf(buffer+len, "\n");
-	len += sprintf(buffer+len, "\tsound.speed = %dHz (phys. %dHz)\n",
-		       dmasound.soft.speed, dmasound.hard.speed);
-	len += sprintf(buffer+len, "\tsound.stereo = 0x%x (%s)\n",
-		       dmasound.soft.stereo,
-		       dmasound.soft.stereo ? "stereo" : "mono");
 	if (dmasound.mach.state_info)
-	    len += dmasound.mach.state_info(buffer);
-	len += sprintf(buffer+len, "\tsq.block_size = %d sq.max_count = %d"
-		       " sq.max_active = %d\n",
-		       write_sq.block_size, write_sq.max_count,
-		       write_sq.max_active);
-	len += sprintf(buffer+len, "\tsq.count = %d sq.rear_size = %d\n",
-		       write_sq.count, write_sq.rear_size);
-	len += sprintf(buffer+len, "\tsq.active = %d sq.syncing = %d\n",
-		       write_sq.active, write_sq.syncing);
+		len += dmasound.mach.state_info(buffer+len,
+			(size_t) LOW_LEVEL_STAT_ALLOC) ;
+
+	/* make usage of the state buffer as deterministic as poss.
+	   exceptional conditions could cause overrun - and this is flagged as
+	   a kernel error.
+	*/
+
+	/* formats and settings */
+
+	len += sprintf(buffer+len,"\t\t === Formats & settings ===\n") ;
+	len += sprintf(buffer+len,"Parameter %20s%20s\n","soft","hard") ;
+	len += sprintf(buffer+len,"Format   :%20s%20s\n",
+		get_afmt_string(dmasound.soft.format),
+		get_afmt_string(dmasound.hard.format));
+
+	len += sprintf(buffer+len,"Samp Rate:%14d s/sec%14d s/sec\n",
+		       dmasound.soft.speed, dmasound.hard.speed);
+
+	len += sprintf(buffer+len,"Channels :%20s%20s\n",
+		       dmasound.soft.stereo ? "stereo" : "mono",
+		       dmasound.hard.stereo ? "stereo" : "mono" );
+
+	/* sound queue status */
+
+	len += sprintf(buffer+len,"\t\t === Sound Queue status ===\n");
+	len += sprintf(buffer+len,"Allocated:%8s%6s\n","Buffers","Size") ;
+	len += sprintf(buffer+len,"%9s:%8d%6d\n",
+		"write", write_sq.numBufs, write_sq.bufSize) ;
+#ifdef HAS_RECORD
+	if (dmasound.mach.record)
+		len += sprintf(buffer+len,"%9s:%8d%6d\n",
+			"read", read_sq.numBufs, read_sq.bufSize) ;
+#endif
+	len += sprintf(buffer+len,
+		"Current  : MaxFrg FragSiz MaxAct Frnt Rear "
+		"Cnt RrSize A B S L  xruns\n") ;
+	len += sprintf(buffer+len,"%9s:%7d%8d%7d%5d%5d%4d%7d%2d%2d%2d%2d%7d\n",
+		"write", write_sq.max_count, write_sq.block_size,
+		write_sq.max_active, write_sq.front, write_sq.rear,
+		write_sq.count, write_sq.rear_size, write_sq.active,
+		write_sq.busy, write_sq.syncing, write_sq.locked, write_sq.xruns) ;
+#ifdef HAS_RECORD
+	if (dmasound.mach.record)
+		len += sprintf(buffer+len,"%9s:%7d%8d%7d%5d%5d%4d%7d%2d%2d%2d%2d%7d\n",
+			"read", read_sq.max_count, read_sq.block_size,
+			read_sq.max_active, read_sq.front, read_sq.rear,
+			read_sq.count, read_sq.rear_size, read_sq.active,
+			read_sq.busy, read_sq.syncing, read_sq.locked, read_sq.xruns) ;
+#endif
+#ifdef DEBUG_DMASOUND
+printk("dmasound: stat buffer used %d bytes\n", len) ;
+#endif
+
+	if (len >= STAT_BUFF_LEN)
+		printk(KERN_ERR "dmasound_core: stat buffer overflowed!\n");
+
 	state.len = len;
 	return 0;
 }
@@ -1171,15 +1567,16 @@
 	release:	state_release,
 };
 
-static void __init state_init(void)
+static int __init state_init(void)
 {
 #ifndef MODULE
 	int state_unit;
 #endif
 	state_unit = register_sound_special(&state_fops, SND_DEV_STATUS);
 	if (state_unit < 0)
-		return;
+		return state_unit ;
 	state.busy = 0;
+	return 0 ;
 }
 
 
@@ -1191,6 +1588,7 @@
 
 int __init dmasound_init(void)
 {
+	int res ;
 #ifdef MODULE
 	if (irq_installed)
 		return -EBUSY;
@@ -1199,10 +1597,12 @@
 	/* Set up sound queue, /dev/audio and /dev/dsp. */
 
 	/* Set default settings. */
-	sq_init();
+	if ((res = sq_init()) < 0)
+		return res ;
 
 	/* Set up /dev/sndstat. */
-	state_init();
+	if ((res = state_init()) < 0)
+		return res ;
 
 	/* Set up /dev/mixer. */
 	mixer_init();
@@ -1215,8 +1615,21 @@
 	irq_installed = 1;
 #endif
 
-	printk(KERN_INFO "DMA sound driver installed, using %d buffers of %dk.\n",
-	       numWriteBufs, writeBufSize);
+	printk(KERN_INFO "%s DMA sound driver rev %03d installed\n",
+		dmasound.mach.name, (DMASOUND_CORE_REVISION<<4) +
+		((dmasound.mach.version>>8) & 0x0f));
+	printk(KERN_INFO
+		"Core driver edition %02d.%02d : %s driver edition %02d.%02d\n",
+		DMASOUND_CORE_REVISION, DMASOUND_CORE_EDITION, dmasound.mach.name2,
+		(dmasound.mach.version >> 8), (dmasound.mach.version & 0xff)) ;
+	printk(KERN_INFO "Write will use %4d fragments of %7d bytes as default\n",
+		numWriteBufs, writeBufSize) ;
+#ifdef HAS_RECORD
+	if (dmasound.mach.record)
+		printk(KERN_INFO
+			"Read  will use %4d fragments of %7d bytes as default\n",
+			numReadBufs, readBufSize) ;
+#endif
 
 	return 0;
 }
@@ -1228,6 +1641,7 @@
 	if (irq_installed) {
 		sound_silence();
 		dmasound.mach.irqcleanup();
+		irq_installed = 0;
 	}
 
 	write_sq_release_buffers();
@@ -1245,29 +1659,60 @@
 
 static int __init dmasound_setup(char *str)
 {
-	int ints[6];
+	int ints[6], size;
 
 	str = get_options(str, ARRAY_SIZE(ints), ints);
 
 	/* check the bootstrap parameter for "dmasound=" */
 
+	/* FIXME: other than in the most naive of cases there is no sense in these
+	 *	  buffers being other than powers of two.  This is not checked yet.
+	 */
+
 	switch (ints[0]) {
+#ifdef HAS_RECORD
+        case 5:
+                if ((ints[5] < 0) || (ints[5] > MAX_CATCH_RADIUS))
+                        printk("dmasound_setup: illegal catch radius, using default = %d\n", catchRadius);
+                else
+                        catchRadius = ints[5];
+                /* fall through */
+        case 4:
+                if (ints[4] < MIN_BUFFERS)
+                        printk("dmasound_setup: illegal number of read buffers, using default = %d\n",
+                                 numReadBufs);
+                else
+                        numReadBufs = ints[4];
+                /* fall through */
+        case 3:
+		if ((size = ints[3]) < 256)  /* check for small buffer specs */
+			size <<= 10 ;
+                if (size < MIN_BUFSIZE || size > MAX_BUFSIZE)
+                        printk("dmasound_setup: illegal read buffer size, using default = %d\n", readBufSize);
+                else
+                        readBufSize = size;
+                /* fall through */
+#else
 	case 3:
 		if ((ints[3] < 0) || (ints[3] > MAX_CATCH_RADIUS))
 			printk("dmasound_setup: illegal catch radius, using default = %d\n", catchRadius);
 		else
 			catchRadius = ints[3];
 		/* fall through */
+#endif
 	case 2:
 		if (ints[1] < MIN_BUFFERS)
 			printk("dmasound_setup: illegal number of buffers, using default = %d\n", numWriteBufs);
 		else
 			numWriteBufs = ints[1];
-		if (ints[2] < MIN_BUFSIZE || ints[2] > MAX_BUFSIZE)
-			printk("dmasound_setup: illegal buffer size, using default = %dK\n", writeBufSize);
-		else
-			writeBufSize = ints[2];
-		break;
+		/* fall through */
+	case 1:
+		if ((size = ints[2]) < 256) /* check for small buffer specs */
+			size <<= 10 ;
+                if (size < MIN_BUFSIZE || size > MAX_BUFSIZE)
+                        printk("dmasound_setup: illegal write buffer size, using default = %d\n", writeBufSize);
+                else
+                        writeBufSize = size;
 	case 0:
 		break;
 	default:
@@ -1281,6 +1726,85 @@
 
 #endif /* !MODULE */
 
+    /*
+     *  Conversion tables
+     */
+
+#ifdef HAS_8BIT_TABLES
+/* 8 bit mu-law */
+
+char dmasound_ulaw2dma8[] = {
+	-126,	-122,	-118,	-114,	-110,	-106,	-102,	-98,
+	-94,	-90,	-86,	-82,	-78,	-74,	-70,	-66,
+	-63,	-61,	-59,	-57,	-55,	-53,	-51,	-49,
+	-47,	-45,	-43,	-41,	-39,	-37,	-35,	-33,
+	-31,	-30,	-29,	-28,	-27,	-26,	-25,	-24,
+	-23,	-22,	-21,	-20,	-19,	-18,	-17,	-16,
+	-16,	-15,	-15,	-14,	-14,	-13,	-13,	-12,
+	-12,	-11,	-11,	-10,	-10,	-9,	-9,	-8,
+	-8,	-8,	-7,	-7,	-7,	-7,	-6,	-6,
+	-6,	-6,	-5,	-5,	-5,	-5,	-4,	-4,
+	-4,	-4,	-4,	-4,	-3,	-3,	-3,	-3,
+	-3,	-3,	-3,	-3,	-2,	-2,	-2,	-2,
+	-2,	-2,	-2,	-2,	-2,	-2,	-2,	-2,
+	-1,	-1,	-1,	-1,	-1,	-1,	-1,	-1,
+	-1,	-1,	-1,	-1,	-1,	-1,	-1,	-1,
+	-1,	-1,	-1,	-1,	-1,	-1,	-1,	0,
+	125,	121,	117,	113,	109,	105,	101,	97,
+	93,	89,	85,	81,	77,	73,	69,	65,
+	62,	60,	58,	56,	54,	52,	50,	48,
+	46,	44,	42,	40,	38,	36,	34,	32,
+	30,	29,	28,	27,	26,	25,	24,	23,
+	22,	21,	20,	19,	18,	17,	16,	15,
+	15,	14,	14,	13,	13,	12,	12,	11,
+	11,	10,	10,	9,	9,	8,	8,	7,
+	7,	7,	6,	6,	6,	6,	5,	5,
+	5,	5,	4,	4,	4,	4,	3,	3,
+	3,	3,	3,	3,	2,	2,	2,	2,
+	2,	2,	2,	2,	1,	1,	1,	1,
+	1,	1,	1,	1,	1,	1,	1,	1,
+	0,	0,	0,	0,	0,	0,	0,	0,
+	0,	0,	0,	0,	0,	0,	0,	0,
+	0,	0,	0,	0,	0,	0,	0,	0
+};
+
+/* 8 bit A-law */
+
+char dmasound_alaw2dma8[] = {
+	-22,	-21,	-24,	-23,	-18,	-17,	-20,	-19,
+	-30,	-29,	-32,	-31,	-26,	-25,	-28,	-27,
+	-11,	-11,	-12,	-12,	-9,	-9,	-10,	-10,
+	-15,	-15,	-16,	-16,	-13,	-13,	-14,	-14,
+	-86,	-82,	-94,	-90,	-70,	-66,	-78,	-74,
+	-118,	-114,	-126,	-122,	-102,	-98,	-110,	-106,
+	-43,	-41,	-47,	-45,	-35,	-33,	-39,	-37,
+	-59,	-57,	-63,	-61,	-51,	-49,	-55,	-53,
+	-2,	-2,	-2,	-2,	-2,	-2,	-2,	-2,
+	-2,	-2,	-2,	-2,	-2,	-2,	-2,	-2,
+	-1,	-1,	-1,	-1,	-1,	-1,	-1,	-1,
+	-1,	-1,	-1,	-1,	-1,	-1,	-1,	-1,
+	-6,	-6,	-6,	-6,	-5,	-5,	-5,	-5,
+	-8,	-8,	-8,	-8,	-7,	-7,	-7,	-7,
+	-3,	-3,	-3,	-3,	-3,	-3,	-3,	-3,
+	-4,	-4,	-4,	-4,	-4,	-4,	-4,	-4,
+	21,	20,	23,	22,	17,	16,	19,	18,
+	29,	28,	31,	30,	25,	24,	27,	26,
+	10,	10,	11,	11,	8,	8,	9,	9,
+	14,	14,	15,	15,	12,	12,	13,	13,
+	86,	82,	94,	90,	70,	66,	78,	74,
+	118,	114,	126,	122,	102,	98,	110,	106,
+	43,	41,	47,	45,	35,	33,	39,	37,
+	59,	57,	63,	61,	51,	49,	55,	53,
+	1,	1,	1,	1,	1,	1,	1,	1,
+	1,	1,	1,	1,	1,	1,	1,	1,
+	0,	0,	0,	0,	0,	0,	0,	0,
+	0,	0,	0,	0,	0,	0,	0,	0,
+	5,	5,	5,	5,	4,	4,	4,	4,
+	7,	7,	7,	7,	6,	6,	6,	6,
+	2,	2,	2,	2,	2,	2,	2,	2,
+	3,	3,	3,	3,	3,	3,	3,	3
+};
+#endif /* HAS_8BIT_TABLES */
 
     /*
      *  Visible symbols for modules
@@ -1300,14 +1824,4 @@
 EXPORT_SYMBOL(dmasound_ulaw2dma8);
 EXPORT_SYMBOL(dmasound_alaw2dma8);
 #endif
-#ifdef HAS_16BIT_TABLES
-EXPORT_SYMBOL(dmasound_ulaw2dma16);
-EXPORT_SYMBOL(dmasound_alaw2dma16);
-#endif
-#ifdef HAS_14BIT_TABLES
-EXPORT_SYMBOL(dmasound_ulaw2dma14l);
-EXPORT_SYMBOL(dmasound_ulaw2dma14h);
-EXPORT_SYMBOL(dmasound_alaw2dma14l);
-EXPORT_SYMBOL(dmasound_alaw2dma14h);
-#endif
-
+EXPORT_SYMBOL(get_afmt_string) ;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/sound/dmasound/dmasound_paula.c linux/drivers/sound/dmasound/dmasound_paula.c
--- linux.orig/drivers/sound/dmasound/dmasound_paula.c	Mon Jan  7 23:41:09 2002
+++ linux/drivers/sound/dmasound/dmasound_paula.c	Wed Dec 26 16:47:48 2001
@@ -1,11 +1,18 @@
-
 /*
  *  linux/drivers/sound/dmasound/dmasound_paula.c
  *
  *  Amiga `Paula' DMA Sound Driver
  *
  *  See linux/drivers/sound/dmasound/dmasound_core.c for copyright and credits
- */
+ *  prior to 28/01/2001
+ *
+ *  28/01/2001 [0.1] Iain Sandoe
+ *		     - added versioning
+ *		     - put in and populated the hardware_afmts field.
+ *             [0.2] - put in SNDCTL_DSP_GETCAPS value.
+ *	       [0.3] - put in constraint on state buffer usage.
+ *	       [0.4] - put in default hard/soft settings
+*/
 
 
 #include <linux/module.h>
@@ -23,6 +30,8 @@
 
 #include "dmasound.h"
 
+#define DMASOUND_PAULA_REVISION 0
+#define DMASOUND_PAULA_EDITION 4
 
    /*
     *	The minimum period for audio depends on htotal (for OCS/ECS/AGA)
@@ -114,7 +123,7 @@
 static void AmiMixerInit(void);
 static int AmiMixerIoctl(u_int cmd, u_long arg);
 static void AmiWriteSqSetup(void);
-static int AmiStateInfo(char *buffer);
+static int AmiStateInfo(char *buffer, size_t space);
 
 
 /*** Translations ************************************************************/
@@ -653,19 +662,36 @@
 }
 
 
-static int AmiStateInfo(char *buffer)
+static int AmiStateInfo(char *buffer, size_t space)
 {
 	int len = 0;
 	len += sprintf(buffer+len, "\tsound.volume_left = %d [0...64]\n",
 		       dmasound.volume_left);
 	len += sprintf(buffer+len, "\tsound.volume_right = %d [0...64]\n",
 		       dmasound.volume_right);
+	if (len >= space) {
+		printk(KERN_ERR "dmasound_paula: overlowed state buffer alloc.\n") ;
+		len = space ;
+	}
 	return len;
 }
 
 
 /*** Machine definitions *****************************************************/
 
+static SETTINGS def_hard = {
+	format: AFMT_S8,
+	stereo: 0,
+	size: 8,
+	speed: 8000
+} ;
+
+static SETTINGS def_soft = {
+	format: AFMT_U8,
+	stereo: 0,
+	size: 8,
+	speed: 8000
+} ;
 
 static MACHINE machAmiga = {
 	name:		"Amiga",
@@ -688,7 +714,10 @@
 	mixer_ioctl:	AmiMixerIoctl,
 	write_sq_setup:	AmiWriteSqSetup,
 	state_info:	AmiStateInfo,
-	min_dsp_speed:	8000
+	min_dsp_speed:	8000,
+	version:	((DMASOUND_PAULA_REVISION<<8) | DMASOUND_PAULA_EDITION),
+	hardware_afmts:	(AFMT_S8 | AFMT_S16_BE), /* h'ware-supported formats *only* here */
+        capabilities:   DSP_CAP_BATCH          /* As per SNDCTL_DSP_GETCAPS */
 };
 
 
@@ -704,6 +733,8 @@
 				    "dmasound [Paula]"))
 		return -EBUSY;
 	    dmasound.mach = machAmiga;
+	    dmasound.mach.default_hard = def_hard ;
+	    dmasound.mach.default_soft = def_soft ;
 	    err = dmasound_init();
 	    if (err)
 		release_mem_region(CUSTOM_PHYSADDR+0xa0, 0x40);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/sound/dmasound/dmasound_q40.c linux/drivers/sound/dmasound/dmasound_q40.c
--- linux.orig/drivers/sound/dmasound/dmasound_q40.c	Mon Jan  7 23:41:09 2002
+++ linux/drivers/sound/dmasound/dmasound_q40.c	Wed Dec 26 16:47:48 2001
@@ -1,10 +1,16 @@
-
 /*
  *  linux/drivers/sound/dmasound/dmasound_q40.c
  *
  *  Q40 DMA Sound Driver
  *
  *  See linux/drivers/sound/dmasound/dmasound_core.c for copyright and credits
+ *  prior to 28/01/2001
+ *
+ *  28/01/2001 [0.1] Iain Sandoe
+ *		     - added versioning
+ *		     - put in and populated the hardware_afmts field.
+ *             [0.2] - put in SNDCTL_DSP_GETCAPS value.
+ *	       [0.3] - put in default hard/soft settings.
  */
 
 
@@ -18,6 +24,8 @@
 
 #include "dmasound.h"
 
+#define DMASOUND_Q40_REVISION 0
+#define DMASOUND_Q40_EDITION 3
 
 static int expand_bal;	/* Balance factor for expanding (not volume!) */
 static int expand_data;	/* Data for expanding */
@@ -216,7 +224,7 @@
 	int bal = expand_bal;
 	int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
 	int utotal, ftotal;
- 
+
 	ftotal = frameLeft;
 	utotal = userCount;
 	while (frameLeft) {
@@ -390,7 +398,7 @@
 
 	q40_pp=start;
 	q40_sc=size;
-		
+
 	write_sq.front = (write_sq.front+1) % write_sq.max_count;
 	write_sq.active++;
 
@@ -446,7 +454,7 @@
             *DAC_LEFT=*q40_pp;
 	    *DAC_RIGHT=*q40_pp++;
 	    q40_sc --;
-	    master_outb(1,SAMPLE_CLEAR_REG);	    
+	    master_outb(1,SAMPLE_CLEAR_REG);
 	}else Q40Interrupt();
 }
 static void Q40Interrupt(void)
@@ -546,6 +554,19 @@
 
 /*** Machine definitions *****************************************************/
 
+static SETTINGS def_hard = {
+	format: AFMT_U8,
+	stereo: 0,
+	size: 8,
+	speed: 10000
+} ;
+
+static SETTINGS def_soft = {
+	format: AFMT_U8,
+	stereo: 0,
+	size: 8,
+	speed: 8000
+} ;
 
 static MACHINE machQ40 = {
 	name:		"Q40",
@@ -559,10 +580,13 @@
 	irqcleanup:	Q40IrqCleanUp,
 #endif /* MODULE */
 	init:		Q40Init,
-	silence:	Q40Silence, 
-	setFormat:	Q40SetFormat, 
+	silence:	Q40Silence,
+	setFormat:	Q40SetFormat,
 	setVolume:	Q40SetVolume,
-	play:		Q40Play
+	play:		Q40Play,
+	version:	((DMASOUND_Q40_REVISION<<8) | DMASOUND_Q40_EDITION),
+	hardware_afmts:	AFMT_U8, /* h'ware-supported formats *only* here */
+        capabilities:	DSP_CAP_BATCH  /* As per SNDCTL_DSP_GETCAPS */
 };
 
 
@@ -573,6 +597,8 @@
 {
 	if (MACH_IS_Q40) {
 	    dmasound.mach = machQ40;
+	    dmasound.mach.default_hard = def_hard ;
+	    dmasound.mach.default_soft = def_soft ;
 	    return dmasound_init();
 	} else
 	    return -ENODEV;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/sound/dmasound/tas3001c.c linux/drivers/sound/dmasound/tas3001c.c
--- linux.orig/drivers/sound/dmasound/tas3001c.c	Thu Jan  1 00:00:00 1970
+++ linux/drivers/sound/dmasound/tas3001c.c	Wed Dec 26 16:47:48 2001
@@ -0,0 +1,370 @@
+/*
+ * Driver for the i2c/i2s based TA3001C sound chip used
+ * on some Apple hardware. Also known as "tumbler".
+ *
+ *  This file is subject to the terms and conditions of the GNU General Public
+ *  License.  See the file COPYING in the main directory of this archive
+ *  for more details.
+ *
+ * Modified by Christopher C. Chimelis <chris@debian.org>:
+ *
+ *   TODO:
+ *   -----
+ *   * Enable DRC since the TiBook speakers are less than good
+ *   * Enable control over input line 2 (is this connected?)
+ *   * Play with the dual six-stage cascading biquad filtering to see how
+ *     we can use it to our advantage (currently not implemented)
+ *
+ *   Version 0.3:
+ *   ------------
+ *   * Fixed volume control
+ *   * Added bass and treble control
+ *   * Added PCM line level control (mixer 1 in the TAS manual)
+ *
+ */
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/proc_fs.h>
+#include <linux/ioport.h>
+#include <linux/sysctl.h>
+#include <linux/types.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <asm/uaccess.h>
+#include <asm/errno.h>
+#include <asm/io.h>
+#include <asm/prom.h>
+
+#include "dmasound.h"
+#include "tas3001c.h"
+
+#define I2C_DRIVERID_TAS (0xFEBA)
+
+#define TAS_VERSION	"0.3"
+#define TAS_DATE	"20011214"
+
+#define TAS_SETTING_MAX	100
+
+#define VOL_DEFAULT	(((TAS_SETTING_MAX*4)/5)<<0)
+#define INPUT_DEFAULT	(((TAS_SETTING_MAX*4)/5)<<0)
+#define BASS_DEFAULT	((TAS_SETTING_MAX/2)<<0)
+#define TREBLE_DEFAULT	((TAS_SETTING_MAX/2)<<0)
+
+static uint cur_left_vol;
+static uint cur_right_vol;
+static uint cur_pcm_lvl;
+static uint cur_treble;
+static uint cur_bass;
+
+static struct i2c_client * tumbler_client = NULL;
+
+static int tas_attach_adapter(struct i2c_adapter *adapter);
+
+static int tas_attach_adapter(struct i2c_adapter *adapter);
+static int tas_detect_client(struct i2c_adapter *adapter, int address);
+static int tas_detach_client(struct i2c_client *client);
+
+/* Unique ID allocation */
+static int tas_id;
+static int tas_initialized;
+
+static struct device_node* tas_node;
+static u8 tas_i2c_address = 0x34;
+
+
+struct tas_data {
+	int arf; /* place holder for future use */
+};
+
+struct i2c_driver tas_driver = {  
+	name:		"TAS3001C driver  V 0.3",
+	id:		I2C_DRIVERID_TAS,
+	flags:		I2C_DF_NOTIFY,
+	attach_adapter:	&tas_attach_adapter,
+	detach_client:	&tas_detach_client,
+	command:	NULL,
+	inc_use:	NULL, /* &tas_inc_use, */
+	dec_use:	NULL  /* &tas_dev_use  */
+};
+
+void
+tumbler_get_volume(uint * left_vol, uint  *right_vol)
+{
+	*left_vol = cur_left_vol;
+	*right_vol = cur_right_vol;
+}
+
+int
+tumbler_set_register(uint reg, uint size, char *block)
+{
+	if (i2c_smbus_write_block_data(tumbler_client, reg, size, block) < 0) {
+		printk("tas3001c: I2C write failed \n");  
+		return -1; 
+	}
+	return 0;
+}
+
+void
+tumbler_get_pcm_lvl(uint *pcm_lvl)
+{
+	*pcm_lvl = cur_pcm_lvl;
+}
+
+void
+tumbler_get_treble(uint *treble)
+{
+	*treble = cur_treble;
+}
+
+void
+tumbler_get_bass(uint *bass)
+{
+	*bass = cur_bass;
+}
+
+int
+tumbler_set_bass(uint bass)
+{
+	uint cur_bass_pers = bass;
+	char block;
+
+	if (!tumbler_client)
+		return -1;
+
+	bass &= 0xff;
+	if (bass > TAS_SETTING_MAX)
+		bass = TAS_SETTING_MAX;
+	bass = ((bass * 72) / TAS_SETTING_MAX) << 0;
+	bass = tas_bass_table[bass];
+	block = (bass >> 0)  & 0xff;
+
+	if (tumbler_set_register(TAS_SET_BASS, &block) < 0) {
+		printk("tas3001c: failed to set bass \n");  
+		return -1; 
+	}
+	cur_bass = cur_bass_pers;
+	return 0;
+}
+	
+int
+tumbler_set_treble(uint treble)
+{
+	uint cur_treble_pers = treble;
+	char block;
+
+	if (!tumbler_client)
+		return -1;
+
+	treble &= 0xff;
+	if (treble > TAS_SETTING_MAX)
+		treble = TAS_SETTING_MAX;
+	treble = ((treble * 72) / TAS_SETTING_MAX) << 0;
+	treble = tas_treble_table[treble];
+	block = (treble >> 0)  & 0xff;
+
+	if (tumbler_set_register(TAS_SET_TREBLE, &block) < 0) {
+		printk("tas3001c: failed to set treble \n");  
+		return -1; 
+	}
+	cur_treble = cur_treble_pers;
+	return 0;
+}
+
+int
+tumbler_set_pcm_lvl(uint pcm_lvl)
+{
+	uint pcm_lvl_pers = pcm_lvl;
+	unsigned char block[3];
+  
+	if (!tumbler_client)
+		return -1;
+
+	pcm_lvl &= 0xff;
+	if (pcm_lvl > TAS_SETTING_MAX)
+		pcm_lvl = TAS_SETTING_MAX;
+	pcm_lvl = ((pcm_lvl * 176) / TAS_SETTING_MAX) << 0;
+
+	pcm_lvl = tas_input_table[pcm_lvl];
+
+	block[0] = (pcm_lvl >> 16) & 0xff;
+	block[1] = (pcm_lvl >> 8)  & 0xff;
+	block[2] = (pcm_lvl >> 0)  & 0xff;
+
+	if (tumbler_set_register(TAS_SET_MIXER1, block) < 0) {
+		printk("tas3001c: failed to set input level \n");  
+		return -1; 
+	}
+	cur_pcm_lvl = pcm_lvl_pers;
+
+	return 0;
+}
+
+int
+tumbler_set_volume(uint left_vol, uint right_vol)
+{
+	uint left_vol_pers = left_vol;
+	uint right_vol_pers = right_vol;
+	unsigned char block[6];
+  
+	if (!tumbler_client)
+		return -1;
+
+	left_vol &= 0xff;
+	if (left_vol > TAS_SETTING_MAX)
+		left_vol = TAS_SETTING_MAX;
+	right_vol &= 0xff;
+	if (right_vol > TAS_SETTING_MAX)
+		right_vol = TAS_SETTING_MAX;
+	left_vol = ((left_vol * 176) / TAS_SETTING_MAX) << 0;
+	right_vol = ((right_vol * 176) / TAS_SETTING_MAX) << 0;
+
+	left_vol = tas_volume_table[left_vol];
+	right_vol = tas_volume_table[right_vol];
+
+	block[0] = (left_vol >> 16) & 0xff;
+	block[1] = (left_vol >> 8)  & 0xff;
+	block[2] = (left_vol >> 0)  & 0xff;
+
+	block[3] = (right_vol >> 16) & 0xff;
+	block[4] = (right_vol >> 8)  & 0xff;
+	block[5] = (right_vol >> 0)  & 0xff;
+
+	if (tumbler_set_register(TAS_SET_VOLUME, block) < 0) {
+		printk("tas3001c: failed to set volume \n");  
+		return -1; 
+	}
+	cur_left_vol = left_vol_pers;
+	cur_right_vol = right_vol_pers;
+
+	return 0;
+}
+
+static int
+tas_attach_adapter(struct i2c_adapter *adapter)
+{
+	if (!strncmp(adapter->name, "mac-io", 6))
+		tas_detect_client(adapter, tas_i2c_address);
+
+	return 0;
+}
+
+static int
+tas_init_client(struct i2c_client * new_client)
+{
+	/* Make sure something answers on the i2c bus
+	 */
+
+	if (i2c_smbus_write_byte_data(new_client, 1, (1<<6)+(2<<4)+(2<<2)+0) < 0)
+		return -1;
+
+	tumbler_client = new_client;
+	
+	tumbler_set_volume(VOL_DEFAULT, VOL_DEFAULT);
+	tumbler_set_pcm_lvl(INPUT_DEFAULT);
+	tumbler_set_bass(BASS_DEFAULT);
+	tumbler_set_treble(TREBLE_DEFAULT);
+	
+	return 0;
+}
+
+static int
+tas_detect_client(struct i2c_adapter *adapter, int address)
+{
+	int rc = 0;
+	struct i2c_client *new_client;
+	struct tas_data *data;
+	const char *client_name = "tas 3001c Digital Equalizer";
+
+	new_client = kmalloc(
+			sizeof(struct i2c_client) + sizeof(struct tas_data),
+			GFP_KERNEL);
+	if (!new_client) {
+		rc = -ENOMEM;
+		goto bail;
+	}
+  
+	/* This is tricky, but it will set the data to the right value. */
+	new_client->data = new_client + 1;
+	data = (struct tas_data *) (new_client->data);
+  
+	new_client->addr = address;
+	new_client->data = data;
+	new_client->adapter = adapter;
+	new_client->driver = &tas_driver;
+	new_client->flags = 0;
+
+	strcpy(new_client->name,client_name);
+
+	new_client->id = tas_id++; /* Automatically unique */
+
+	if (tas_init_client(new_client)) {
+		rc = -ENODEV;
+		goto bail;
+	}
+
+	/* Tell the i2c layer a new client has arrived */
+	if (i2c_attach_client(new_client)) {
+		rc = -ENODEV;
+		goto bail;
+	}
+bail:
+	if (rc && new_client)
+		kfree(new_client);
+	return rc;
+}
+
+static int
+tas_detach_client(struct i2c_client *client)
+{
+	if (client == tumbler_client)
+		tumbler_client = NULL;
+
+	i2c_detach_client(client);
+	kfree(client);
+	
+	return 0;
+}
+
+int
+tas_cleanup(void)
+{
+	if (!tas_initialized)
+		return -ENODEV;
+	i2c_del_driver(&tas_driver);
+	tas_initialized = 0;
+	
+	return 0;
+}
+
+int
+tas_init(void)
+{
+	int rc;
+	u32* paddr;
+
+	if (tas_initialized)
+		return 0;
+
+	tas_node = find_devices("deq");
+	if (tas_node == NULL)
+		return -ENODEV;
+
+	printk(KERN_INFO "tas3001c driver version %s (%s)\n",TAS_VERSION,TAS_DATE);
+	paddr = (u32 *)get_property(tas_node, "i2c-address", NULL);
+	if (paddr) {
+		tas_i2c_address = (*paddr) >> 1;
+		printk(KERN_INFO "using i2c address: 0x%x from device-tree\n",
+			tas_i2c_address);
+	} else    
+		printk(KERN_INFO "using i2c address: 0x%x (default)\n", tas_i2c_address);
+
+	if ((rc = i2c_add_driver(&tas_driver))) {
+		printk("tas3001c: Driver registration failed, module not inserted.\n");
+		tas_cleanup();
+		return rc;
+	}
+	tas_initialized = 1;
+	return 0;
+}
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/sound/dmasound/tas3001c.h linux/drivers/sound/dmasound/tas3001c.h
--- linux.orig/drivers/sound/dmasound/tas3001c.h	Thu Jan  1 00:00:00 1970
+++ linux/drivers/sound/dmasound/tas3001c.h	Wed Dec 26 16:47:48 2001
@@ -0,0 +1,248 @@
+/*
+ * Header file for the i2c/i2s based TA3001C sound chip used
+ * on some Apple hardware. Also known as "tumbler".
+ *
+ *  This file is subject to the terms and conditions of the GNU General Public
+ *  License.  See the file COPYING in the main directory of this archive
+ *  for more details.
+ *
+ * Written by Christopher C. Chimelis <chris@debian.org>
+ */
+
+#ifndef _tas3001c_h_
+#define _tas3001c_h_
+
+/*
+ * Macros that correspond to the registers that we write to
+ * when setting the various values.
+ */
+#define TAS_DRC			0x02	/* DRC		*/
+#define TAS_VOLUME		0x04	/* Volume	*/
+#define TAS_TREBLE		0x05	/* Treble	*/
+#define TAS_BASS		0x06	/* Bass		*/
+#define TAS_MIXER1		0x07	/* PCM line	*/
+#define TAS_MIXER2		0x08	/* Input (Unk)	*/
+
+/*
+ * Macros that define various arguments to tas_set_register()
+ */
+#define TAS_SET_DRC		TAS_DRC, 2
+#define TAS_SET_VOLUME		TAS_VOLUME, 6
+#define TAS_SET_TREBLE		TAS_TREBLE, 1
+#define TAS_SET_BASS		TAS_BASS, 1
+#define TAS_SET_MIXER1		TAS_MIXER1, 3
+#define TAS_SET_MIXER2		TAS_MIXER2, 3
+
+
+/*
+ * tas_volume_table contains lookup values for the volume settings
+ * for tumbler.  This is straight from the programming manual
+ * for the chip, however, it's zero-sourced for your shopping pleasure
+ * (meaning, you'll have to compute the difference between the desired
+ * dB and the index value of the proper setting.
+ *
+ * This table should've been replaced by the formula:
+ * 	dB = 20 log(x)
+ * but, since there's no log() or supporting functions like exp(),
+ * my implementation of the above won't work. Yeah, I could do it
+ * the hard way, but this table is just easier :-)
+ *
+ * For reference, -70 dB = tas_volume_table[0]
+ */
+
+static unsigned int tas_volume_table[] = {
+	0x00000015,	0x00000016,	0x00000017,	/* -70.0, -69.5, -69.0 */
+	0x00000019,	0x0000001a,	0x0000001c,	/* -68.5, -68.0, -67.5 */
+	0x0000001d,	0x0000001f,	0x00000021,	/* -67.0, -66.5, -66.0 */
+	0x00000023,	0x00000025,	0x00000027,	/* -65.5, -65.0, -64.5 */
+	0x00000029,	0x0000002c,	0x0000002e,	/* -64.0, -63.5, -63.0 */
+	0x00000031,	0x00000034,	0x00000037,	/* -62.5, -62.0, -61.5 */
+	0x0000003a,	0x0000003e,	0x00000042,	/* -61.0, -60.5, -60.0 */
+	0x00000045,	0x0000004a,	0x0000004e,	/* -59.5, -59.0, -58.5 */
+	0x00000053,	0x00000057,	0x0000005d,	/* -58.0, -57.5, -57.0 */
+	0x00000062,	0x00000068,	0x0000006e,	/* -56.5, -56.0, -55.5 */
+	0x00000075,	0x0000007b,	0x00000083,	/* -55.0, -54.5, -54.0 */
+	0x0000008b,	0x00000093,	0x0000009b,	/* -53.5, -53.0, -52.5 */
+	0x000000a5,	0x000000ae,	0x000000b9,	/* -52.0, -51.5, -51.0 */
+	0x000000c4,	0x000000cf,	0x000000dc,	/* -50.5, -50.0, -49.5 */
+	0x000000e9,	0x000000f6,	0x00000105,	/* -49.0, -48.5, -48.0 */
+	0x00000114,	0x00000125,	0x00000136,	/* -47.5, -47.0, -46.5 */
+	0x00000148,	0x0000015c,	0x00000171,	/* -46.0, -45.5, -45.0 */
+	0x00000186,	0x0000019e,	0x000001b6,	/* -44.5, -44.0, -43.5 */
+	0x000001d0,	0x000001eb,	0x00000209,	/* -43.0, -42.5, -42.0 */
+	0x00000227,	0x00000248,	0x0000026b,	/* -41.5, -41.0, -40.5 */
+	0x0000028f,	0x000002b6,	0x000002df,	/* -40.0, -39.5, -39.0 */
+	0x0000030b,	0x00000339,	0x0000036a,	/* -38.5, -38.0, -37.5 */
+	0x0000039e,	0x000003d5,	0x0000040f,	/* -37.0, -36.5, -36.0 */
+	0x0000044c,	0x0000048d,	0x000004d2,	/* -35.5, -35.0, -34.5 */
+	0x0000051c,	0x00000569,	0x000005bb,	/* -34.0, -33.5, -33.0 */
+	0x00000612,	0x0000066e,	0x000006d0,	/* -32.5, -32.0, -31.5 */
+	0x00000737,	0x000007a5,	0x00000818,	/* -31.0, -30.5, -30.0 */
+	0x00000893,	0x00000915,	0x0000099f,	/* -29.5, -29.0, -28.5 */
+	0x00000a31,	0x00000acc,	0x00000b6f,	/* -28.0, -27.5, -27.0 */
+	0x00000c1d,	0x00000cd5,	0x00000d97,	/* -26.5, -26.0, -25.5 */
+	0x00000e65,	0x00000f40,	0x00001027,	/* -25.0, -24.5, -24.0 */
+	0x0000111c,	0x00001220,	0x00001333,	/* -23.5, -23.0, -22.5 */
+	0x00001456,	0x0000158a,	0x000016d1,	/* -22.0, -21.5, -21.0 */
+	0x0000182b,	0x0000199a,	0x00001b1e,	/* -20.5, -20.0, -19.5 */
+	0x00001cb9,	0x00001e6d,	0x0000203a,	/* -19.0, -18.5, -18.0 */
+	0x00002223,	0x00002429,	0x0000264e,	/* -17.5, -17.0, -16.5 */
+	0x00002893,	0x00002afa,	0x00002d86,	/* -16.0, -15.5, -15.0 */
+	0x00003039,	0x00003314,	0x0000361b,	/* -14.5, -14.0, -13.5 */
+	0x00003950,	0x00003cb5,	0x0000404e,	/* -13.0, -12.5, -12.0 */
+	0x0000441d,	0x00004827,	0x00004c6d,	/* -11.5, -11.0, -10.5 */
+	0x000050f4,	0x000055c0,	0x00005ad5,	/* -10.0, -09.5, -09.0 */
+	0x00006037,	0x000065ea,	0x00006bf4,	/* -08.5, -08.0, -07.5 */
+	0x0000725a,	0x00007920,	0x0000804e,	/* -07.0, -06.5, -06.0 */
+	0x000087e8,	0x00008ff6,	0x0000987d,	/* -05.5, -05.0, -04.5 */
+	0x0000a186,	0x0000ab19,	0x0000b53c,	/* -04.0, -03.5, -03.0 */
+	0x0000bff9,	0x0000cb59,	0x0000d766,	/* -02.5, -02.0, -01.5 */
+	0x0000e429,	0x0000f1ae,	0x00010000,	/* -01.0, -00.5,  00.0 */
+	0x00010f2b,	0x00011f3d,	0x00013042,	/* +00.5, +01.0, +01.5 */
+	0x00014249,	0x00015562,	0x0001699c,	/* +02.0, +02.5, +03.0 */
+	0x00017f09,	0x000195bc,	0x0001adc6,	/* +03.5, +04.0, +04.5 */
+	0x0001c73d,	0x0001e237,	0x0001feca,	/* +05.0, +05.5, +06.0 */
+	0x00021d0e,	0x00023d1d,	0x00025f12,	/* +06.5, +07.0, +07.5 */
+	0x0002830b,	0x0002a925,	0x0002d182,	/* +08.0, +08.5, +09.0 */
+	0x0002fc42,	0x0003298b,	0x00035983,	/* +09.5, +10.0, +10.5 */
+	0x00038c53,	0x0003c225,	0x0003fb28,	/* +11.0, +11.5, +12.0 */
+	0x0004378b,	0x00047783,	0x0004bb44,	/* +12.5, +13.0, +13.5 */
+	0x0005030a,	0x00054f10,	0x00059f98,	/* +14.0, +14.5, +15.0 */
+	0x0005f4e5,	0x00064f40,	0x0006aef6,	/* +15.5, +16.0, +16.5 */
+	0x00071457,	0x00077fbb,	0x0007f17b	/* +17.0, +17.5, +18.0 */
+};
+
+/* tas_treble_table[] is a lookup table that holds the values to drop into
+ * the treble setting register on the TAS.  Again, there is a formula for
+ * this one, but we use this instead due to lack of real math functions
+ * in the kernel.
+ */
+static char tas_treble_table[] = {
+	0x96,		0x95,		0x94,	/* -18.0, -17.5, -17.0 */
+	0x93,		0x92,		0x91,	/* -16.5, -16.0, -15.5 */
+	0x90,		0x8f,		0x8e,	/* -15.0, -14.5, -14.0 */
+	0x8d,		0x8c,		0x8b,	/* -13.5, -13.0, -12.5 */
+	0x8a,		0x89,		0x88,	/* -12.0, -11.5, -11.0 */
+	0x87,		0x86,		0x85,	/* -10.5, -10.0, -09.5 */
+	0x84,		0x83,		0x82,	/* -09.0, -08.5, -08.0 */
+	0x81,		0x80,		0x7f,	/* -07.5, -07.0, -06.5 */
+	0x7e,		0x7d,		0x7c,	/* -06.0, -05.5, -05.0 */
+	0x7b,		0x7a,		0x79,	/* -04.5, -04.0, -03.5 */
+	0x78,		0x77,		0x76,	/* -03.0, -02.5, -02.0 */
+	0x75,		0x74,		0x73,	/* -01.5, -01.0, -00.5 */
+	0x72,		0x71,		0x70,	/*  00.0, +00.5, +01.0 */
+	0x6e,		0x6d,		0x6c,	/* +01.5, +02.0, +02.5 */
+	0x6b,		0x69,		0x68,	/* +03.0, +03.5, +04.0 */
+	0x66,		0x65,		0x63,	/* +04.5, +05.0, +05.5 */
+	0x62,		0x60,		0x5e,	/* +06.0, +06.5, +07.0 */
+	0x5c,		0x5a,		0x57,	/* +07.5, +08.0, +08.5 */
+	0x55,		0x52,		0x4f,	/* +09.0, +09.5, +10.0 */
+	0x4c,		0x49,		0x45,	/* +10.5, +11.0, +11.5 */
+	0x42,		0x3e,		0x3a,	/* +12.0, +12.5, +13.0 */
+	0x36,		0x32,		0x2d,	/* +13.5, +14.0, +14.5 */
+	0x28,		0x22,		0x1c,	/* +15.0, +15.5, +16.0 */
+	0x16,		0x10,		0x09,	/* +16.5, +17.0, +17.5 */
+	0x01					/* +18.0	       */
+};
+
+/* tas_bass_table[] is a lookup table that holds the values to drop into
+ * the bass setting register on the TAS.  Again, there is a formula for
+ * this one, but we use this instead due to lack of real math functions
+ * in the kernel.
+ */
+static char tas_bass_table[] = {
+	0x86,		0x82,		0x7f,	/* -18.0, -17.5, -17.0 */
+	0x7d,		0x7a,		0x78,	/* -16.5, -16.0, -15.5 */
+	0x76,		0x74,		0x72,	/* -15.0, -14.5, -14.0 */
+	0x70,		0x6e,		0x6d,   /* -13.5, -13.0, -12.5 */
+	0x6b,		0x69,		0x66,	/* -12.0, -11.5, -11.0 */
+	0x64,		0x61,		0x5f,	/* -10.5, -10.0, -09.5 */
+	0x5d,		0x5c,		0x5a,	/* -09.0, -08.5, -08.0 */
+	0x59,		0x58,		0x56,	/* -07.5, -07.0, -06.5 */
+	0x55,		0x54,		0x53,	/* -06.0, -05.5, -05.0 */
+	0x51,		0x4f,		0x4d,	/* -04.5, -04.0, -03.5 */
+	0x4b,		0x49,		0x46,	/* -03.0, -02.5, -02.0 */
+	0x44,		0x42,		0x40,	/* -01.5, -01.0, -00.5 */
+	0x3e,		0x3c,		0x3b,	/*  00.0, +00.5, +01.0 */
+	0x39,		0x38,		0x36,	/* +01.5, +02.0, +02.5 */
+	0x35,		0x33,		0x31,	/* +03.0, +03.5, +04.0 */
+	0x30,		0x2e,		0x2c,	/* +04.5, +05.0, +05.5 */
+	0x2b,		0x29,		0x28,	/* +06.0, +06.5, +07.0 */
+	0x26,		0x25,		0x23,	/* +07.5, +08.0, +08.5 */
+	0x21,		0x1f,		0x1c,	/* +09.0, +09.5, +10.0 */
+	0x19,		0x18,		0x17,	/* +10.5, +11.0, +11.5 */
+	0x16,		0x14,		0x13,	/* +12.0, +12.5, +13.0 */
+	0x12,		0x10,		0x0f,	/* +13.5, +14.0, +14.5 */
+	0x0d,		0x0b,		0x0a,	/* +15.0, +15.5, +16.0 */
+	0x08,		0x06,		0x03,	/* +16.5, +17.0, +17.5 */
+	0x01					/* +18.0	       */
+};
+
+/* tas_input_table[] is a lookup table that holds the values to drop into
+ * the setting registers on the TAS for "mixers 1 & 2" (which are the input
+ * lines).  Again, there is a formula for these, but we use this instead
+ * due to lack of real math functions in the kernel.
+ */
+static unsigned int tas_input_table[] = {
+	0x00014b,	0x00015f,	0x000174,	/* -70.0, -69.5, -69.0 */
+	0x00018a,	0x0001a1,	0x0001ba,	/* -68.5, -68.0, -67.5 */
+	0x0001d4,	0x0001f0,	0x00020d,	/* -67.0, -66.5, -66.0 */
+	0x00022c,	0x00024d,	0x000270,	/* -65.5, -65.0, -64.5 */
+	0x000295,	0x0002bc,	0x0002e6,	/* -64.0, -63.5, -63.0 */
+	0x000312,	0x000340,	0x000372,	/* -62.5, -62.0, -61.5 */
+	0x0003a6,	0x0003dd,	0x000418,	/* -61.0, -60.5, -60.0 */
+	0x000456,	0x000498,	0x0004de,	/* -59.5, -59.0, -58.5 */
+	0x000528,	0x000576,	0x0005c9,	/* -58.0, -57.5, -57.0 */
+	0x000620,	0x00067d,	0x0006e0,	/* -56.5, -56.0, -55.5 */
+	0x000748,	0x0007b7,	0x00082c,	/* -55.0, -54.5, -54.0 */
+	0x0008a8,	0x00092b,	0x0009b6,	/* -53.5, -53.0, -52.5 */
+	0x000a49,	0x000ae5,	0x000b8b,	/* -52.0, -51.5, -51.0 */
+	0x000c3a,	0x000cf3,	0x000db8,	/* -50.5, -50.0, -49.5 */
+	0x000e88,	0x000f64,	0x00104e,	/* -49.0, -48.5, -48.0 */
+	0x001145,	0x00124b,	0x001361,	/* -47.5, -47.0, -46.5 */
+	0x001487,	0x0015be,	0x001708,	/* -46.0, -45.5, -45.0 */
+	0x001865,	0x0019d8,	0x001b60,	/* -44.5, -44.0, -43.5 */
+	0x001cff,	0x001eb7,	0x002089,	/* -43.0, -42.5, -42.0 */
+	0x002276,	0x002481,	0x0026ab,	/* -41.5, -41.0, -40.5 */
+	0x0028f5,	0x002b63,	0x002df5,	/* -40.0, -39.5, -39.0 */
+	0x0030ae,	0x003390,	0x00369e,	/* -38.5, -38.0, -37.5 */
+	0x0039db,	0x003d49,	0x0040ea,	/* -37.0, -36.5, -36.0 */
+	0x0044c3,	0x0048d6,	0x004d27,	/* -35.5, -35.0, -34.5 */
+	0x0051b9,	0x005691,	0x005bb2,	/* -34.0, -33.5, -33.0 */
+	0x006121,	0x0066e3,	0x006cfb,	/* -32.5, -32.0, -31.5 */
+	0x007370,	0x007a48,	0x008186,	/* -31.0, -30.5, -30.0 */
+	0x008933,	0x009154,	0x0099f1,	/* -29.5, -29.0, -28.5 */
+	0x00a310,	0x00acba,	0x00b6f6,	/* -28.0, -27.5, -27.0 */
+	0x00c1cd,	0x00cd49,	0x00d973,	/* -26.5, -26.0, -25.5 */
+	0x00e655,	0x00f3fb,	0x010270,	/* -25.0, -24.5, -24.0 */
+	0x0111c0,	0x0121f9,	0x013328,	/* -23.5, -23.0, -22.5 */
+	0x01455b,	0x0158a2,	0x016d0e,	/* -22.0, -21.5, -21.0 */
+	0x0182af,	0x019999,	0x01b1de,	/* -20.5, -20.0, -19.5 */
+	0x01cb94,	0x01e6cf,	0x0203a7,	/* -19.0, -18.5, -18.0 */
+	0x022235,	0x024293,	0x0264db,	/* -17.5, -17.0, -16.5 */
+	0x02892c,	0x02afa3,	0x02d862,	/* -16.0, -15.5, -15.0 */
+	0x03038a,	0x033142,	0x0361af,	/* -14.5, -14.0, -13.5 */
+	0x0394fa,	0x03cb50,	0x0404de,	/* -13.0, -12.5, -12.0 */
+	0x0441d5,	0x048268,	0x04c6d0,	/* -11.5, -11.0, -10.5 */
+	0x050f44,	0x055c04,	0x05ad50,	/* -10.0, -09.5, -09.0 */
+	0x06036e,	0x065ea5,	0x06bf44,	/* -08.5, -08.0, -07.5 */
+	0x07259d,	0x079207,	0x0804dc,	/* -07.0, -06.5, -06.0 */
+	0x087e80,	0x08ff59,	0x0987d5,	/* -05.5, -05.0, -04.5 */
+	0x0a1866,	0x0ab189,	0x0b53be,	/* -04.0, -03.5, -03.0 */
+	0x0bff91,	0x0cb591,	0x0d765a,	/* -02.5, -02.0, -01.5 */
+	0x0e4290,	0x0f1adf,	0x100000,	/* -01.0, -00.5,  00.0 */
+	0x10f2b4,	0x11f3c9,	0x13041a,	/* +00.5, +01.0, +01.5 */
+	0x14248e,	0x15561a,	0x1699c0,	/* +02.0, +02.5, +03.0 */
+	0x17f094,	0x195bb8,	0x1adc61,	/* +03.5, +04.0, +04.5 */
+	0x1c73d5,	0x1e236d,	0x1fec98,	/* +05.0, +05.5, +06.0 */
+	0x21d0d9,	0x23d1cd,	0x25f125,	/* +06.5, +07.0, +07.5 */
+	0x2830af,	0x2a9254,	0x2d1818,	/* +08.0, +08.5, +09.0 */
+	0x2fc420,	0x3298b0,	0x35982f,	/* +09.5, +10.0, +10.5 */
+	0x38c528,	0x3c224c,	0x3fb278,	/* +11.0, +11.5, +12.0 */
+	0x437880,	0x477828,	0x4bb446,	/* +12.5, +13.0, +13.5 */
+	0x5030a1,	0x54f106,	0x59f980,	/* +14.0, +14.5, +15.0 */
+	0x5f4e52,	0x64f403,	0x6aef5d,	/* +15.5, +16.0, +16.5 */
+	0x714575,	0x77fbaa,	0x7f17af	/* +17.0, +17.5, +18.0 */
+};
+
+#endif /* _tas3001c_h_ */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/sound/dmasound/trans_16.c linux/drivers/sound/dmasound/trans_16.c
--- linux.orig/drivers/sound/dmasound/trans_16.c	Thu Jan  1 00:00:00 1970
+++ linux/drivers/sound/dmasound/trans_16.c	Wed Dec 26 16:47:48 2001
@@ -0,0 +1,679 @@
+/*
+ *  linux/drivers/sound/dmasound/trans_16.c
+ *
+ *  16 bit translation routines.  Only used by Power mac at present.
+ *
+ *  See linux/drivers/sound/dmasound/dmasound_core.c for copyright and
+ *  history prior to 08/02/2001.
+ *
+ *  08/02/2001 Iain Sandoe
+ *		split from dmasound_awacs.c
+ */
+
+#include <linux/soundcard.h>
+#include <asm/uaccess.h>
+#include "dmasound.h"
+
+static short dmasound_alaw2dma16[] ;
+static short dmasound_ulaw2dma16[] ;
+
+static ssize_t pmac_ct_law(const u_char *userPtr, size_t userCount,
+			   u_char frame[], ssize_t *frameUsed,
+			   ssize_t frameLeft);
+static ssize_t pmac_ct_s8(const u_char *userPtr, size_t userCount,
+			  u_char frame[], ssize_t *frameUsed,
+			  ssize_t frameLeft);
+static ssize_t pmac_ct_u8(const u_char *userPtr, size_t userCount,
+			  u_char frame[], ssize_t *frameUsed,
+			  ssize_t frameLeft);
+static ssize_t pmac_ct_s16(const u_char *userPtr, size_t userCount,
+			   u_char frame[], ssize_t *frameUsed,
+			   ssize_t frameLeft);
+static ssize_t pmac_ct_u16(const u_char *userPtr, size_t userCount,
+			   u_char frame[], ssize_t *frameUsed,
+			   ssize_t frameLeft);
+
+static ssize_t pmac_ctx_law(const u_char *userPtr, size_t userCount,
+			    u_char frame[], ssize_t *frameUsed,
+			    ssize_t frameLeft);
+static ssize_t pmac_ctx_s8(const u_char *userPtr, size_t userCount,
+			   u_char frame[], ssize_t *frameUsed,
+			   ssize_t frameLeft);
+static ssize_t pmac_ctx_u8(const u_char *userPtr, size_t userCount,
+			   u_char frame[], ssize_t *frameUsed,
+			   ssize_t frameLeft);
+static ssize_t pmac_ctx_s16(const u_char *userPtr, size_t userCount,
+			    u_char frame[], ssize_t *frameUsed,
+			    ssize_t frameLeft);
+static ssize_t pmac_ctx_u16(const u_char *userPtr, size_t userCount,
+			    u_char frame[], ssize_t *frameUsed,
+			    ssize_t frameLeft);
+
+static ssize_t pmac_ct_s16_read(const u_char *userPtr, size_t userCount,
+			   u_char frame[], ssize_t *frameUsed,
+			   ssize_t frameLeft);
+static ssize_t pmac_ct_u16_read(const u_char *userPtr, size_t userCount,
+			   u_char frame[], ssize_t *frameUsed,
+			   ssize_t frameLeft);
+
+/*** Translations ************************************************************/
+
+extern int expand_bal;	/* Balance factor for expanding (not volume!) */
+static int expand_data;	/* Data for expanding */
+
+static ssize_t pmac_ct_law(const u_char *userPtr, size_t userCount,
+			   u_char frame[], ssize_t *frameUsed,
+			   ssize_t frameLeft)
+{
+	short *table = dmasound.soft.format == AFMT_MU_LAW
+		? dmasound_ulaw2dma16 : dmasound_alaw2dma16;
+	ssize_t count, used;
+	short *p = (short *) &frame[*frameUsed];
+	int val, stereo = dmasound.soft.stereo;
+
+	frameLeft >>= 2;
+	if (stereo)
+		userCount >>= 1;
+	used = count = min_t(unsigned long, userCount, frameLeft);
+	while (count > 0) {
+		u_char data;
+		if (get_user(data, userPtr++))
+			return -EFAULT;
+		val = table[data];
+		*p++ = val;
+		if (stereo) {
+			if (get_user(data, userPtr++))
+				return -EFAULT;
+			val = table[data];
+		}
+		*p++ = val;
+		count--;
+	}
+	*frameUsed += used * 4;
+	return stereo? used * 2: used;
+}
+
+
+static ssize_t pmac_ct_s8(const u_char *userPtr, size_t userCount,
+			  u_char frame[], ssize_t *frameUsed,
+			  ssize_t frameLeft)
+{
+	ssize_t count, used;
+	short *p = (short *) &frame[*frameUsed];
+	int val, stereo = dmasound.soft.stereo;
+
+	frameLeft >>= 2;
+	if (stereo)
+		userCount >>= 1;
+	used = count = min_t(unsigned long, userCount, frameLeft);
+	while (count > 0) {
+		u_char data;
+		if (get_user(data, userPtr++))
+			return -EFAULT;
+		val = data << 8;
+		*p++ = val;
+		if (stereo) {
+			if (get_user(data, userPtr++))
+				return -EFAULT;
+			val = data << 8;
+		}
+		*p++ = val;
+		count--;
+	}
+	*frameUsed += used * 4;
+	return stereo? used * 2: used;
+}
+
+
+static ssize_t pmac_ct_u8(const u_char *userPtr, size_t userCount,
+			  u_char frame[], ssize_t *frameUsed,
+			  ssize_t frameLeft)
+{
+	ssize_t count, used;
+	short *p = (short *) &frame[*frameUsed];
+	int val, stereo = dmasound.soft.stereo;
+
+	frameLeft >>= 2;
+	if (stereo)
+		userCount >>= 1;
+	used = count = min_t(unsigned long, userCount, frameLeft);
+	while (count > 0) {
+		u_char data;
+		if (get_user(data, userPtr++))
+			return -EFAULT;
+		val = (data ^ 0x80) << 8;
+		*p++ = val;
+		if (stereo) {
+			if (get_user(data, userPtr++))
+				return -EFAULT;
+			val = (data ^ 0x80) << 8;
+		}
+		*p++ = val;
+		count--;
+	}
+	*frameUsed += used * 4;
+	return stereo? used * 2: used;
+}
+
+
+static ssize_t pmac_ct_s16(const u_char *userPtr, size_t userCount,
+			   u_char frame[], ssize_t *frameUsed,
+			   ssize_t frameLeft)
+{
+	ssize_t count, used;
+	int stereo = dmasound.soft.stereo;
+	short *fp = (short *) &frame[*frameUsed];
+
+	frameLeft >>= 2;
+	userCount >>= (stereo? 2: 1);
+	used = count = min_t(unsigned long, userCount, frameLeft);
+	if (!stereo) {
+		short *up = (short *) userPtr;
+		while (count > 0) {
+			short data;
+			if (get_user(data, up++))
+				return -EFAULT;
+			*fp++ = data;
+			*fp++ = data;
+			count--;
+		}
+	} else {
+		if (copy_from_user(fp, userPtr, count * 4))
+			return -EFAULT;
+	}
+	*frameUsed += used * 4;
+	return stereo? used * 4: used * 2;
+}
+
+static ssize_t pmac_ct_u16(const u_char *userPtr, size_t userCount,
+			   u_char frame[], ssize_t *frameUsed,
+			   ssize_t frameLeft)
+{
+	ssize_t count, used;
+	int mask = (dmasound.soft.format == AFMT_U16_LE? 0x0080: 0x8000);
+	int stereo = dmasound.soft.stereo;
+	short *fp = (short *) &frame[*frameUsed];
+	short *up = (short *) userPtr;
+
+	frameLeft >>= 2;
+	userCount >>= (stereo? 2: 1);
+	used = count = min_t(unsigned long, userCount, frameLeft);
+	while (count > 0) {
+		short data;
+		if (get_user(data, up++))
+			return -EFAULT;
+		data ^= mask;
+		*fp++ = data;
+		if (stereo) {
+			if (get_user(data, up++))
+				return -EFAULT;
+			data ^= mask;
+		}
+		*fp++ = data;
+		count--;
+	}
+	*frameUsed += used * 4;
+	return stereo? used * 4: used * 2;
+}
+
+
+static ssize_t pmac_ctx_law(const u_char *userPtr, size_t userCount,
+			    u_char frame[], ssize_t *frameUsed,
+			    ssize_t frameLeft)
+{
+	unsigned short *table = (unsigned short *)
+		(dmasound.soft.format == AFMT_MU_LAW
+		 ? dmasound_ulaw2dma16 : dmasound_alaw2dma16);
+	unsigned int data = expand_data;
+	unsigned int *p = (unsigned int *) &frame[*frameUsed];
+	int bal = expand_bal;
+	int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
+	int utotal, ftotal;
+	int stereo = dmasound.soft.stereo;
+
+	frameLeft >>= 2;
+	if (stereo)
+		userCount >>= 1;
+	ftotal = frameLeft;
+	utotal = userCount;
+	while (frameLeft) {
+		u_char c;
+		if (bal < 0) {
+			if (userCount == 0)
+				break;
+			if (get_user(c, userPtr++))
+				return -EFAULT;
+			data = table[c];
+			if (stereo) {
+				if (get_user(c, userPtr++))
+					return -EFAULT;
+				data = (data << 16) + table[c];
+			} else
+				data = (data << 16) + data;
+			userCount--;
+			bal += hSpeed;
+		}
+		*p++ = data;
+		frameLeft--;
+		bal -= sSpeed;
+	}
+	expand_bal = bal;
+	expand_data = data;
+	*frameUsed += (ftotal - frameLeft) * 4;
+	utotal -= userCount;
+	return stereo? utotal * 2: utotal;
+}
+
+static ssize_t pmac_ctx_s8(const u_char *userPtr, size_t userCount,
+			   u_char frame[], ssize_t *frameUsed,
+			   ssize_t frameLeft)
+{
+	unsigned int *p = (unsigned int *) &frame[*frameUsed];
+	unsigned int data = expand_data;
+	int bal = expand_bal;
+	int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
+	int stereo = dmasound.soft.stereo;
+	int utotal, ftotal;
+
+	frameLeft >>= 2;
+	if (stereo)
+		userCount >>= 1;
+	ftotal = frameLeft;
+	utotal = userCount;
+	while (frameLeft) {
+		u_char c;
+		if (bal < 0) {
+			if (userCount == 0)
+				break;
+			if (get_user(c, userPtr++))
+				return -EFAULT;
+			data = c << 8;
+			if (stereo) {
+				if (get_user(c, userPtr++))
+					return -EFAULT;
+				data = (data << 16) + (c << 8);
+			} else
+				data = (data << 16) + data;
+			userCount--;
+			bal += hSpeed;
+		}
+		*p++ = data;
+		frameLeft--;
+		bal -= sSpeed;
+	}
+	expand_bal = bal;
+	expand_data = data;
+	*frameUsed += (ftotal - frameLeft) * 4;
+	utotal -= userCount;
+	return stereo? utotal * 2: utotal;
+}
+
+
+static ssize_t pmac_ctx_u8(const u_char *userPtr, size_t userCount,
+			   u_char frame[], ssize_t *frameUsed,
+			   ssize_t frameLeft)
+{
+	unsigned int *p = (unsigned int *) &frame[*frameUsed];
+	unsigned int data = expand_data;
+	int bal = expand_bal;
+	int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
+	int stereo = dmasound.soft.stereo;
+	int utotal, ftotal;
+
+	frameLeft >>= 2;
+	if (stereo)
+		userCount >>= 1;
+	ftotal = frameLeft;
+	utotal = userCount;
+	while (frameLeft) {
+		u_char c;
+		if (bal < 0) {
+			if (userCount == 0)
+				break;
+			if (get_user(c, userPtr++))
+				return -EFAULT;
+			data = (c ^ 0x80) << 8;
+			if (stereo) {
+				if (get_user(c, userPtr++))
+					return -EFAULT;
+				data = (data << 16) + ((c ^ 0x80) << 8);
+			} else
+				data = (data << 16) + data;
+			userCount--;
+			bal += hSpeed;
+		}
+		*p++ = data;
+		frameLeft--;
+		bal -= sSpeed;
+	}
+	expand_bal = bal;
+	expand_data = data;
+	*frameUsed += (ftotal - frameLeft) * 4;
+	utotal -= userCount;
+	return stereo? utotal * 2: utotal;
+}
+
+
+static ssize_t pmac_ctx_s16(const u_char *userPtr, size_t userCount,
+			    u_char frame[], ssize_t *frameUsed,
+			    ssize_t frameLeft)
+{
+	unsigned int *p = (unsigned int *) &frame[*frameUsed];
+	unsigned int data = expand_data;
+	unsigned short *up = (unsigned short *) userPtr;
+	int bal = expand_bal;
+	int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
+	int stereo = dmasound.soft.stereo;
+	int utotal, ftotal;
+
+	frameLeft >>= 2;
+	userCount >>= (stereo? 2: 1);
+	ftotal = frameLeft;
+	utotal = userCount;
+	while (frameLeft) {
+		unsigned short c;
+		if (bal < 0) {
+			if (userCount == 0)
+				break;
+			if (get_user(data, up++))
+				return -EFAULT;
+			if (stereo) {
+				if (get_user(c, up++))
+					return -EFAULT;
+				data = (data << 16) + c;
+			} else
+				data = (data << 16) + data;
+			userCount--;
+			bal += hSpeed;
+		}
+		*p++ = data;
+		frameLeft--;
+		bal -= sSpeed;
+	}
+	expand_bal = bal;
+	expand_data = data;
+	*frameUsed += (ftotal - frameLeft) * 4;
+	utotal -= userCount;
+	return stereo? utotal * 4: utotal * 2;
+}
+
+
+static ssize_t pmac_ctx_u16(const u_char *userPtr, size_t userCount,
+			    u_char frame[], ssize_t *frameUsed,
+			    ssize_t frameLeft)
+{
+	int mask = (dmasound.soft.format == AFMT_U16_LE? 0x0080: 0x8000);
+	unsigned int *p = (unsigned int *) &frame[*frameUsed];
+	unsigned int data = expand_data;
+	unsigned short *up = (unsigned short *) userPtr;
+	int bal = expand_bal;
+	int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
+	int stereo = dmasound.soft.stereo;
+	int utotal, ftotal;
+
+	frameLeft >>= 2;
+	userCount >>= (stereo? 2: 1);
+	ftotal = frameLeft;
+	utotal = userCount;
+	while (frameLeft) {
+		unsigned short c;
+		if (bal < 0) {
+			if (userCount == 0)
+				break;
+			if (get_user(data, up++))
+				return -EFAULT;
+			data ^= mask;
+			if (stereo) {
+				if (get_user(c, up++))
+					return -EFAULT;
+				data = (data << 16) + (c ^ mask);
+			} else
+				data = (data << 16) + data;
+			userCount--;
+			bal += hSpeed;
+		}
+		*p++ = data;
+		frameLeft--;
+		bal -= sSpeed;
+	}
+	expand_bal = bal;
+	expand_data = data;
+	*frameUsed += (ftotal - frameLeft) * 4;
+	utotal -= userCount;
+	return stereo? utotal * 4: utotal * 2;
+}
+
+/* data in routines... */
+
+static ssize_t pmac_ct_s8_read(const u_char *userPtr, size_t userCount,
+			  u_char frame[], ssize_t *frameUsed,
+			  ssize_t frameLeft)
+{
+	ssize_t count, used;
+	short *p = (short *) &frame[*frameUsed];
+	int val, stereo = dmasound.soft.stereo;
+
+	frameLeft >>= 2;
+	if (stereo)
+		userCount >>= 1;
+	used = count = min_t(unsigned long, userCount, frameLeft);
+	while (count > 0) {
+		u_char data;
+
+		val = *p++;
+		data = val >> 8;
+		if (put_user(data, (u_char *)userPtr++))
+			return -EFAULT;
+		if (stereo) {
+			val = *p;
+			data = val >> 8;
+			if (put_user(data, (u_char *)userPtr++))
+				return -EFAULT;
+		}
+		p++;
+		count--;
+	}
+	*frameUsed += used * 4;
+	return stereo? used * 2: used;
+}
+
+
+static ssize_t pmac_ct_u8_read(const u_char *userPtr, size_t userCount,
+			  u_char frame[], ssize_t *frameUsed,
+			  ssize_t frameLeft)
+{
+	ssize_t count, used;
+	short *p = (short *) &frame[*frameUsed];
+	int val, stereo = dmasound.soft.stereo;
+
+	frameLeft >>= 2;
+	if (stereo)
+		userCount >>= 1;
+	used = count = min_t(unsigned long, userCount, frameLeft);
+	while (count > 0) {
+		u_char data;
+
+		val = *p++;
+		data = (val >> 8) ^ 0x80;
+		if (put_user(data, (u_char *)userPtr++))
+			return -EFAULT;
+		if (stereo) {
+			val = *p;
+			data = (val >> 8) ^ 0x80;
+			if (put_user(data, (u_char *)userPtr++))
+				return -EFAULT;
+		}
+		p++;
+		count--;
+	}
+	*frameUsed += used * 4;
+	return stereo? used * 2: used;
+}
+
+static ssize_t pmac_ct_s16_read(const u_char *userPtr, size_t userCount,
+			   u_char frame[], ssize_t *frameUsed,
+			   ssize_t frameLeft)
+{
+	ssize_t count, used;
+	int stereo = dmasound.soft.stereo;
+	short *fp = (short *) &frame[*frameUsed];
+
+	frameLeft >>= 2;
+	userCount >>= (stereo? 2: 1);
+	used = count = min_t(unsigned long, userCount, frameLeft);
+	if (!stereo) {
+		short *up = (short *) userPtr;
+		while (count > 0) {
+			short data;
+			data = *fp;
+			if (put_user(data, up++))
+				return -EFAULT;
+			fp+=2;
+			count--;
+		}
+	} else {
+		if (copy_to_user((u_char *)userPtr, fp, count * 4))
+			return -EFAULT;
+	}
+	*frameUsed += used * 4;
+	return stereo? used * 4: used * 2;
+}
+
+static ssize_t pmac_ct_u16_read(const u_char *userPtr, size_t userCount,
+			   u_char frame[], ssize_t *frameUsed,
+			   ssize_t frameLeft)
+{
+	ssize_t count, used;
+	int mask = (dmasound.soft.format == AFMT_U16_LE? 0x0080: 0x8000);
+	int stereo = dmasound.soft.stereo;
+	short *fp = (short *) &frame[*frameUsed];
+	short *up = (short *) userPtr;
+
+	frameLeft >>= 2;
+	userCount >>= (stereo? 2: 1);
+	used = count = min_t(unsigned long, userCount, frameLeft);
+	while (count > 0) {
+		int data;
+
+		data = *fp++;
+		data ^= mask;
+		if (put_user(data, up++))
+			return -EFAULT;
+		if (stereo) {
+			data = *fp;
+			data ^= mask;
+			if (put_user(data, up++))
+				return -EFAULT;
+		}
+		fp++;
+		count--;
+	}
+	*frameUsed += used * 4;
+	return stereo? used * 4: used * 2;
+}
+
+TRANS transAwacsNormal = {
+	ct_ulaw:	pmac_ct_law,
+	ct_alaw:	pmac_ct_law,
+	ct_s8:		pmac_ct_s8,
+	ct_u8:		pmac_ct_u8,
+	ct_s16be:	pmac_ct_s16,
+	ct_u16be:	pmac_ct_u16,
+	ct_s16le:	pmac_ct_s16,
+	ct_u16le:	pmac_ct_u16,
+};
+
+TRANS transAwacsExpand = {
+	ct_ulaw:	pmac_ctx_law,
+	ct_alaw:	pmac_ctx_law,
+	ct_s8:		pmac_ctx_s8,
+	ct_u8:		pmac_ctx_u8,
+	ct_s16be:	pmac_ctx_s16,
+	ct_u16be:	pmac_ctx_u16,
+	ct_s16le:	pmac_ctx_s16,
+	ct_u16le:	pmac_ctx_u16,
+};
+
+TRANS transAwacsNormalRead = {
+	ct_s8:		pmac_ct_s8_read,
+	ct_u8:		pmac_ct_u8_read,
+	ct_s16be:	pmac_ct_s16_read,
+	ct_u16be:	pmac_ct_u16_read,
+	ct_s16le:	pmac_ct_s16_read,
+	ct_u16le:	pmac_ct_u16_read,
+};
+
+/* translation tables */
+/* 16 bit mu-law */
+
+static short dmasound_ulaw2dma16[] = {
+	-32124,	-31100,	-30076,	-29052,	-28028,	-27004,	-25980,	-24956,
+	-23932,	-22908,	-21884,	-20860,	-19836,	-18812,	-17788,	-16764,
+	-15996,	-15484,	-14972,	-14460,	-13948,	-13436,	-12924,	-12412,
+	-11900,	-11388,	-10876,	-10364,	-9852,	-9340,	-8828,	-8316,
+	-7932,	-7676,	-7420,	-7164,	-6908,	-6652,	-6396,	-6140,
+	-5884,	-5628,	-5372,	-5116,	-4860,	-4604,	-4348,	-4092,
+	-3900,	-3772,	-3644,	-3516,	-3388,	-3260,	-3132,	-3004,
+	-2876,	-2748,	-2620,	-2492,	-2364,	-2236,	-2108,	-1980,
+	-1884,	-1820,	-1756,	-1692,	-1628,	-1564,	-1500,	-1436,
+	-1372,	-1308,	-1244,	-1180,	-1116,	-1052,	-988,	-924,
+	-876,	-844,	-812,	-780,	-748,	-716,	-684,	-652,
+	-620,	-588,	-556,	-524,	-492,	-460,	-428,	-396,
+	-372,	-356,	-340,	-324,	-308,	-292,	-276,	-260,
+	-244,	-228,	-212,	-196,	-180,	-164,	-148,	-132,
+	-120,	-112,	-104,	-96,	-88,	-80,	-72,	-64,
+	-56,	-48,	-40,	-32,	-24,	-16,	-8,	0,
+	32124,	31100,	30076,	29052,	28028,	27004,	25980,	24956,
+	23932,	22908,	21884,	20860,	19836,	18812,	17788,	16764,
+	15996,	15484,	14972,	14460,	13948,	13436,	12924,	12412,
+	11900,	11388,	10876,	10364,	9852,	9340,	8828,	8316,
+	7932,	7676,	7420,	7164,	6908,	6652,	6396,	6140,
+	5884,	5628,	5372,	5116,	4860,	4604,	4348,	4092,
+	3900,	3772,	3644,	3516,	3388,	3260,	3132,	3004,
+	2876,	2748,	2620,	2492,	2364,	2236,	2108,	1980,
+	1884,	1820,	1756,	1692,	1628,	1564,	1500,	1436,
+	1372,	1308,	1244,	1180,	1116,	1052,	988,	924,
+	876,	844,	812,	780,	748,	716,	684,	652,
+	620,	588,	556,	524,	492,	460,	428,	396,
+	372,	356,	340,	324,	308,	292,	276,	260,
+	244,	228,	212,	196,	180,	164,	148,	132,
+	120,	112,	104,	96,	88,	80,	72,	64,
+	56,	48,	40,	32,	24,	16,	8,	0,
+};
+
+/* 16 bit A-law */
+
+static short dmasound_alaw2dma16[] = {
+	-5504,	-5248,	-6016,	-5760,	-4480,	-4224,	-4992,	-4736,
+	-7552,	-7296,	-8064,	-7808,	-6528,	-6272,	-7040,	-6784,
+	-2752,	-2624,	-3008,	-2880,	-2240,	-2112,	-2496,	-2368,
+	-3776,	-3648,	-4032,	-3904,	-3264,	-3136,	-3520,	-3392,
+	-22016,	-20992,	-24064,	-23040,	-17920,	-16896,	-19968,	-18944,
+	-30208,	-29184,	-32256,	-31232,	-26112,	-25088,	-28160,	-27136,
+	-11008,	-10496,	-12032,	-11520,	-8960,	-8448,	-9984,	-9472,
+	-15104,	-14592,	-16128,	-15616,	-13056,	-12544,	-14080,	-13568,
+	-344,	-328,	-376,	-360,	-280,	-264,	-312,	-296,
+	-472,	-456,	-504,	-488,	-408,	-392,	-440,	-424,
+	-88,	-72,	-120,	-104,	-24,	-8,	-56,	-40,
+	-216,	-200,	-248,	-232,	-152,	-136,	-184,	-168,
+	-1376,	-1312,	-1504,	-1440,	-1120,	-1056,	-1248,	-1184,
+	-1888,	-1824,	-2016,	-1952,	-1632,	-1568,	-1760,	-1696,
+	-688,	-656,	-752,	-720,	-560,	-528,	-624,	-592,
+	-944,	-912,	-1008,	-976,	-816,	-784,	-880,	-848,
+	5504,	5248,	6016,	5760,	4480,	4224,	4992,	4736,
+	7552,	7296,	8064,	7808,	6528,	6272,	7040,	6784,
+	2752,	2624,	3008,	2880,	2240,	2112,	2496,	2368,
+	3776,	3648,	4032,	3904,	3264,	3136,	3520,	3392,
+	22016,	20992,	24064,	23040,	17920,	16896,	19968,	18944,
+	30208,	29184,	32256,	31232,	26112,	25088,	28160,	27136,
+	11008,	10496,	12032,	11520,	8960,	8448,	9984,	9472,
+	15104,	14592,	16128,	15616,	13056,	12544,	14080,	13568,
+	344,	328,	376,	360,	280,	264,	312,	296,
+	472,	456,	504,	488,	408,	392,	440,	424,
+	88,	72,	120,	104,	24,	8,	56,	40,
+	216,	200,	248,	232,	152,	136,	184,	168,
+	1376,	1312,	1504,	1440,	1120,	1056,	1248,	1184,
+	1888,	1824,	2016,	1952,	1632,	1568,	1760,	1696,
+	688,	656,	752,	720,	560,	528,	624,	592,
+	944,	912,	1008,	976,	816,	784,	880,	848,
+};
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/sound/opl3sa2.c linux/drivers/sound/opl3sa2.c
--- linux.orig/drivers/sound/opl3sa2.c	Thu Oct 11 16:43:30 2001
+++ linux/drivers/sound/opl3sa2.c	Mon Jan  7 14:33:07 2002
@@ -55,6 +55,7 @@
  *                         sb_card.c and awe_wave.c. (Dec 12, 2000)
  * Scott Murray            Some small cleanups to the init code output.
  *                         (Jan 7, 2001)
+ * Zwane Mwaikambo	   Added PM support. (Dec 4 2001)
  *
  */
 
@@ -62,13 +63,16 @@
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/isapnp.h>
-
+#include <linux/pm.h>
 #include "sound_config.h"
 
 #include "ad1848.h"
 #include "mpu401.h"
 
+#define OPL3SA2_MODULE_NAME	"opl3sa2"
+
 /* Useful control port indexes: */
+#define OPL3SA2_PM	     0x01
 #define OPL3SA2_SYS_CTRL     0x02
 #define OPL3SA2_IRQ_CONFIG   0x03
 #define OPL3SA2_DMA_CONFIG   0x06
@@ -86,6 +90,11 @@
 #define DEFAULT_MIC    50
 #define DEFAULT_TIMBRE 0
 
+/* Power saving modes */
+#define OPL3SA2_PM_MODE1	0x05
+#define OPL3SA2_PM_MODE2	0x04
+#define OPL3SA2_PM_MODE3	0x03
+
 /* For checking against what the card returns: */
 #define VERSION_UNKNOWN 0
 #define VERSION_YMF711  1
@@ -121,6 +130,10 @@
 typedef struct opl3sa2_mixerdata_tag {
 	unsigned short cfg_port;
 	unsigned short padding;
+	unsigned char  pm_reg;
+	unsigned int   in_suspend;
+	struct pm_dev  *pmdev;
+	unsigned int   card;
 	unsigned int   volume_l;
 	unsigned int   volume_r;
 	unsigned int   mic;
@@ -328,6 +341,20 @@
 }
 
 
+static void opl3sa2_mixer_restore(opl3sa2_mixerdata* devc, int card)
+{
+	if (devc) {
+		opl3sa2_set_volume(devc, devc->volume_l, devc->volume_r);
+		opl3sa2_set_mic(devc, devc->mic);
+
+		if (chipset[card] == CHIPSET_OPL3SA3) {
+			opl3sa3_set_bass(devc, devc->bass_l, devc->bass_r);
+			opl3sa3_set_treble(devc, devc->treble_l, devc->treble_r);
+		}
+	}
+}
+
+
 static inline void arg_to_vol_mono(unsigned int vol, int* value)
 {
 	int left;
@@ -608,9 +635,9 @@
 	char tag;
 
 	/*
-	 * Verify that the I/O port range is free.
+	 * Try and allocate our I/O port range.
 	 */
-	if(check_region(hw_config->io_base, 2)) {
+	if(!request_region(hw_config->io_base, 2, OPL3SA2_MODULE_NAME)) {
 		printk(KERN_ERR "opl3sa2: Control I/O port %#x not free\n",
 		       hw_config->io_base);
 		return 0;
@@ -699,7 +726,6 @@
 
 static void __init attach_opl3sa2(struct address_info* hw_config, int card)
 {
-   	request_region(hw_config->io_base, 2, chipset_name[card]);
 
 	/* Initialize IRQ configuration to IRQ-B: -, IRQ-A: WSS+MPU+OPL3 */
 	opl3sa2_write(hw_config->io_base, OPL3SA2_IRQ_CONFIG, 0x0d);
@@ -892,6 +918,81 @@
 
 /* End of component functions */
 
+/* Power Management support functions */
+static int opl3sa2_suspend(struct pm_dev *pdev, unsigned char pm_mode)
+{
+	unsigned long flags;
+	opl3sa2_mixerdata *p;
+
+	if (!pdev)
+		return -EINVAL;
+
+	save_flags(flags);
+	cli();
+
+	p = (opl3sa2_mixerdata *) pdev->data;
+
+	switch (pm_mode) {
+	case 1:
+		pm_mode = OPL3SA2_PM_MODE1;
+		break;
+	case 2:
+		pm_mode = OPL3SA2_PM_MODE2;
+		break;
+	case 3:
+		pm_mode = OPL3SA2_PM_MODE3;
+		break;
+	default:
+		/* we don't know howto handle this... */
+		restore_flags(flags);
+		return -EBUSY;
+	}
+
+	p->in_suspend = 1;
+	/* its supposed to automute before suspending, so we wont bother */
+	opl3sa2_read(p->cfg_port, OPL3SA2_PM, &p->pm_reg);
+	opl3sa2_write(p->cfg_port, OPL3SA2_PM, p->pm_reg | pm_mode);
+	/* wait a while for the clock oscillator to stabilise */
+	mdelay(10);
+
+	restore_flags(flags);
+	return 0;
+}
+
+static int opl3sa2_resume(struct pm_dev *pdev)
+{
+	unsigned long flags;
+	opl3sa2_mixerdata *p;
+
+	if (!pdev)
+		return -EINVAL;
+
+	p = (opl3sa2_mixerdata *) pdev->data;
+	save_flags(flags);
+	cli();
+
+	/* I don't think this is necessary */
+	opl3sa2_write(p->cfg_port, OPL3SA2_PM, p->pm_reg);
+	opl3sa2_mixer_restore(p, p->card);
+	p->in_suspend = 0;
+
+	restore_flags(flags);
+	return 0;
+}
+
+static int opl3sa2_pm_callback(struct pm_dev *pdev, pm_request_t rqst, void *data)
+{
+	unsigned char mode = (unsigned  char)data;
+
+	switch (rqst) {
+		case PM_SUSPEND:
+			return opl3sa2_suspend(pdev, mode);
+
+		case PM_RESUME:
+			return opl3sa2_resume(pdev);
+	}
+	return 0;
+}
 
 /*
  * Install OPL3-SA2 based card(s).
@@ -989,6 +1090,12 @@
 		attach_opl3sa2_mss(&cfg_mss[card]);
 		attach_opl3sa2_mixer(&cfg[card], card);
 
+		opl3sa2_data[card].card = card;
+		/* register our power management capabilities */
+		opl3sa2_data[card].pmdev = pm_register(PM_ISA_DEV, card, opl3sa2_pm_callback);
+		if (opl3sa2_data[card].pmdev)
+			opl3sa2_data[card].pmdev->data = &opl3sa2_data[card];
+
 		/*
 		 * Set the Yamaha 3D enhancement mode (aka Ymersion) if asked to and
 		 * it's supported.
@@ -1033,6 +1140,9 @@
 	int card;
 
 	for(card = 0; card < opl3sa2_cards_num; card++) {
+		if (opl3sa2_data[card].pmdev)
+			pm_unregister(opl3sa2_data[card].pmdev);
+
 	        if(cfg_mpu[card].slots[1] != -1) {
 			unload_opl3sa2_mpu(&cfg_mpu[card]);
 		}
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/usb/Config.in linux/drivers/usb/Config.in
--- linux.orig/drivers/usb/Config.in	Mon Jan  7 23:41:09 2002
+++ linux/drivers/usb/Config.in	Wed Dec 26 15:09:47 2001
@@ -72,6 +72,8 @@
    dep_tristate '  USB OV511 Camera support' CONFIG_USB_OV511 $CONFIG_USB $CONFIG_VIDEO_DEV
    dep_tristate '  USB Philips Cameras' CONFIG_USB_PWC $CONFIG_USB $CONFIG_VIDEO_DEV
    dep_tristate '  USB SE401 Camera support' CONFIG_USB_SE401 $CONFIG_USB $CONFIG_VIDEO_DEV
+   dep_tristate '  USB STV680 (Pencam) Camera support' CONFIG_USB_STV680 $CONFIG_USB $CONFIG_VIDEO_DEV
+   dep_tristate '  USB 3com HomeConnect (aka vicam) support (EXPERIMENTAL)' CONFIG_USB_VICAM $CONFIG_USB $CONFIG_VIDEO_DEV $CONFIG_EXPERIMENTAL
    dep_tristate '  D-Link USB FM radio support (EXPERIMENTAL)' CONFIG_USB_DSBR $CONFIG_USB $CONFIG_VIDEO_DEV $CONFIG_EXPERIMENTAL
    dep_tristate '  DABUSB driver' CONFIG_USB_DABUSB $CONFIG_USB
 fi
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/usb/Makefile linux/drivers/usb/Makefile
--- linux.orig/drivers/usb/Makefile	Mon Jan  7 23:41:09 2002
+++ linux/drivers/usb/Makefile	Wed Dec 26 15:09:47 2001
@@ -65,8 +65,10 @@
 obj-$(CONFIG_USB_MDC800)	+= mdc800.o
 obj-$(CONFIG_USB_USS720)	+= uss720.o
 obj-$(CONFIG_USB_DABUSB)	+= dabusb.o
+obj-$(CONFIG_USB_VICAM)		+= vicam.o
 obj-$(CONFIG_USB_OV511)		+= ov511.o
 obj-$(CONFIG_USB_SE401)		+= se401.o
+obj-$(CONFIG_USB_STV680)	+= stv680.o
 obj-$(CONFIG_USB_PEGASUS)	+= pegasus.o
 obj-$(CONFIG_USB_CATC)		+= catc.o
 obj-$(CONFIG_USB_KAWETH)        += kaweth.o
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/usb/audio.c linux/drivers/usb/audio.c
--- linux.orig/drivers/usb/audio.c	Mon Jan  7 23:41:09 2002
+++ linux/drivers/usb/audio.c	Wed Dec 26 14:28:35 2001
@@ -3378,10 +3378,10 @@
 	if (state->nrchannels > 2)
 		printk(KERN_WARNING "usbaudio: feature unit %u: OSS mixer interface does not support more than 2 channels\n", ftr[3]);
 
-		nr_logical_channels=(ftr[0]-7)/ftr[5]-1;
+	nr_logical_channels=(ftr[0]-7)/ftr[5]-1;
 
-		if (nr_logical_channels != state->nrchannels) {
-			printk(KERN_WARNING "usbaudio: warning: found %d of %d logical channels.\n", state->nrchannels,nr_logical_channels);
+	if (nr_logical_channels != state->nrchannels) {
+		printk(KERN_WARNING "usbaudio: warning: found %d of %d logical channels.\n", state->nrchannels,nr_logical_channels);
 
 		if (state->nrchannels == 1 && nr_logical_channels==0) {
 			printk(KERN_INFO "usbaudio: assuming the channel found is the master channel (got a Philips camera?). Should be fine.\n");
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/usb/mdc800.c linux/drivers/usb/mdc800.c
--- linux.orig/drivers/usb/mdc800.c	Mon Jan  7 23:41:09 2002
+++ linux/drivers/usb/mdc800.c	Wed Dec 26 14:28:36 2001
@@ -30,8 +30,14 @@
  *
  * The driver supports only one camera.
  * 
- * (08/04/2001) gb
+ * Fix: mdc800 used sleep_on and slept with io_lock held.
+ * Converted sleep_on to waitqueues with schedule_timeout and made io_lock
+ * a semaphore from a spinlock.
+ * by Oliver Neukum <520047054719-0001@t-online.de>
+ * (02/12/2001)
+ * 
  * Identify version on module load.
+ * (08/04/2001) gb
  *
  * version 0.7.5
  * Fixed potential SMP races with Spinlocks.
@@ -136,6 +142,7 @@
 
 	purb_t			irq_urb;
 	wait_queue_head_t	irq_wait;
+	int                     irq_woken;
 	char*			irq_urb_buffer;
 
 	int			camera_busy;          // is camera busy ?
@@ -145,11 +152,13 @@
 	purb_t   		write_urb;
 	char*			write_urb_buffer;
 	wait_queue_head_t	write_wait;
+	int                     written;
 
 
 	purb_t	   		download_urb;
 	char*			download_urb_buffer;
 	wait_queue_head_t	download_wait;
+	int                     downloaded;
 	int			download_left;		// Bytes left to download ?
 
 
@@ -159,7 +168,7 @@
 	int			out_count;	// Bytes in the buffer
 
 	int			open;		// Camera device open ?
-	spinlock_t		io_lock;	// IO -lock	
+	struct semaphore	io_lock;	// IO -lock
 
 	char 			in [8];		// Command Input Buffer
 	int  			in_count;
@@ -284,6 +293,7 @@
 	if (wake_up)
 	{
 		mdc800->camera_request_ready=0;
+		mdc800->irq_woken=1;
 		wake_up_interruptible (&mdc800->irq_wait);
 	}
 }
@@ -300,9 +310,19 @@
  */
 static int mdc800_usb_waitForIRQ (int mode, int msec)
 {
+        DECLARE_WAITQUEUE(wait, current);
+
 	mdc800->camera_request_ready=1+mode;
 
-	interruptible_sleep_on_timeout (&mdc800->irq_wait, msec*HZ/1000);
+	add_wait_queue(&mdc800->irq_wait, &wait);
+	set_current_state(TASK_INTERRUPTIBLE);
+	if (!mdc800->irq_woken)
+	{
+	        schedule_timeout (msec*HZ/1000);
+	}
+        remove_wait_queue(&mdc800->irq_wait, &wait);
+	set_current_state(TASK_RUNNING);
+	mdc800->irq_woken = 0;
 
 	if (mdc800->camera_request_ready>0)
 	{
@@ -337,6 +357,7 @@
 	{	
 		mdc800->state=READY;
 	}
+	mdc800->written = 1;
 	wake_up_interruptible (&mdc800->write_wait);
 }
 
@@ -364,6 +385,7 @@
 	{
 		err ("request bytes fails (status:%i)", urb->status);
 	}
+	mdc800->downloaded = 1;
 	wake_up_interruptible (&mdc800->download_wait);
 }
 
@@ -445,7 +467,7 @@
 
 	info ("Found Mustek MDC800 on USB.");
 
-	spin_lock (&mdc800->io_lock);
+	down (&mdc800->io_lock);
 
 	mdc800->dev=dev;
 	mdc800->open=0;
@@ -484,7 +506,7 @@
 
 	mdc800->state=READY;
 
-	spin_unlock (&mdc800->io_lock);
+	up (&mdc800->io_lock);
 	
 	return mdc800;
 }
@@ -558,7 +580,7 @@
 	int retval=0;
 	int errn=0;
 
-	spin_lock (&mdc800->io_lock);
+	down (&mdc800->io_lock);
 	
 	if (mdc800->state == NOT_CONNECTED)
 	{
@@ -594,7 +616,7 @@
 	dbg ("Mustek MDC800 device opened.");
 
 error_out:
-	spin_unlock (&mdc800->io_lock);
+	up (&mdc800->io_lock);
 	return errn;
 }
 
@@ -607,10 +629,9 @@
 	int retval=0;
 	dbg ("Mustek MDC800 device closed.");
 
-	spin_lock (&mdc800->io_lock);
+	down (&mdc800->io_lock);
 	if (mdc800->open && (mdc800->state != NOT_CONNECTED))
 	{
-		spin_unlock(&mdc800->io_lock);
 		usb_unlink_urb (mdc800->irq_urb);
 		usb_unlink_urb (mdc800->write_urb);
 		usb_unlink_urb (mdc800->download_urb);
@@ -618,11 +639,10 @@
 	}
 	else
 	{
-		spin_unlock (&mdc800->io_lock);
 		retval=-EIO;
 	}
 
-
+        up(&mdc800->io_lock);
 	return retval;
 }
 
@@ -634,22 +654,23 @@
 {
 	int   left=len, sts=len; /* single transfer size */
 	char* ptr=buf;
+	DECLARE_WAITQUEUE(wait, current);
 
-	spin_lock (&mdc800->io_lock);
+	down (&mdc800->io_lock);
 	if (mdc800->state == NOT_CONNECTED)
 	{
-		spin_unlock (&mdc800->io_lock);
+		up (&mdc800->io_lock);
 		return -EBUSY;
 	}
 	if (mdc800->state == WORKING)
 	{
 		warn ("Illegal State \"working\" reached during read ?!");
-		spin_unlock (&mdc800->io_lock);
+		up (&mdc800->io_lock);
 		return -EBUSY;
 	}
 	if (!mdc800->open)
 	{
-		spin_unlock (&mdc800->io_lock);
+		up (&mdc800->io_lock);
 		return -EBUSY;
 	}
 
@@ -657,7 +678,7 @@
 	{
 		if (signal_pending (current)) 
 		{
-			spin_unlock (&mdc800->io_lock);
+			up (&mdc800->io_lock);
 			return -EINTR;
 		}
 
@@ -676,21 +697,29 @@
 				if (usb_submit_urb (mdc800->download_urb))
 				{
 					err ("Can't submit download urb (status=%i)",mdc800->download_urb->status);
-					spin_unlock (&mdc800->io_lock);
+					up (&mdc800->io_lock);
 					return len-left;
 				}
-				interruptible_sleep_on_timeout (&mdc800->download_wait, TO_DOWNLOAD_GET_READY*HZ/1000);
+				add_wait_queue(&mdc800->download_wait, &wait);
+				set_current_state(TASK_INTERRUPTIBLE);
+				if (!mdc800->downloaded)
+				{
+				        schedule_timeout (TO_DOWNLOAD_GET_READY*HZ/1000);
+				}
+                                set_current_state(TASK_RUNNING);
+				remove_wait_queue(&mdc800->download_wait, &wait);
+				mdc800->downloaded = 0;
 				if (mdc800->download_urb->status != 0)
 				{
 					err ("request download-bytes fails (status=%i)",mdc800->download_urb->status);
-					spin_unlock (&mdc800->io_lock);
+					up (&mdc800->io_lock);
 					return len-left;
 				}
 			}
 			else
 			{
 				/* No more bytes -> that's an error*/
-				spin_unlock (&mdc800->io_lock);
+				up (&mdc800->io_lock);
 				return -EIO;
 			}
 		}
@@ -704,7 +733,7 @@
 		}
 	}
 
-	spin_unlock (&mdc800->io_lock);
+	up (&mdc800->io_lock);
 	return len-left;
 }
 
@@ -718,16 +747,17 @@
 static ssize_t mdc800_device_write (struct file *file, const char *buf, size_t len, loff_t *pos)
 {
 	int i=0;
+	DECLARE_WAITQUEUE(wait, current);
 
-	spin_lock (&mdc800->io_lock);
+	down (&mdc800->io_lock);
 	if (mdc800->state != READY)
 	{
-		spin_unlock (&mdc800->io_lock);
+		up (&mdc800->io_lock);
 		return -EBUSY;
 	}
 	if (!mdc800->open )
 	{
-		spin_unlock (&mdc800->io_lock);
+		up (&mdc800->io_lock);
 		return -EBUSY;
 	}
 
@@ -735,7 +765,7 @@
 	{
 		if (signal_pending (current)) 
 		{
-			spin_unlock (&mdc800->io_lock);
+			up (&mdc800->io_lock);
 			return -EINTR;
 		}
 
@@ -757,7 +787,7 @@
 		else
 		{
 			err ("Command is to long !\n");
-			spin_unlock (&mdc800->io_lock);
+			up (&mdc800->io_lock);
 			return -EIO;
 		}
 
@@ -769,7 +799,7 @@
 			if (mdc800_usb_waitForIRQ (0,TO_GET_READY))
 			{
 				err ("Camera didn't get ready.\n");
-				spin_unlock (&mdc800->io_lock);
+				up (&mdc800->io_lock);
 				return -EIO;
 			}
 
@@ -781,14 +811,22 @@
 			if (usb_submit_urb (mdc800->write_urb))
 			{
 				err ("submitting write urb fails (status=%i)", mdc800->write_urb->status);
-				spin_unlock (&mdc800->io_lock);
+				up (&mdc800->io_lock);
 				return -EIO;
 			}
-			interruptible_sleep_on_timeout (&mdc800->write_wait, TO_WRITE_GET_READY*HZ/1000);
+			add_wait_queue(&mdc800->write_wait, &wait);
+			set_current_state(TASK_INTERRUPTIBLE);
+			if (!mdc800->written)
+			{
+			        schedule_timeout (TO_WRITE_GET_READY*HZ/1000);
+			}
+                        set_current_state(TASK_RUNNING);
+			remove_wait_queue(&mdc800->write_wait, &wait);
+			mdc800->written = 0;
 			if (mdc800->state == WORKING)
 			{
 				usb_unlink_urb (mdc800->write_urb);
-				spin_unlock (&mdc800->io_lock);
+				up (&mdc800->io_lock);
 				return -EIO;
 			}
 
@@ -800,7 +838,7 @@
 					{
 						err ("call 0x07 before 0x05,0x3e");
 						mdc800->state=READY;
-						spin_unlock (&mdc800->io_lock);
+						up (&mdc800->io_lock);
 						return -EIO;
 					}
 					mdc800->pic_len=-1;
@@ -819,7 +857,7 @@
 						if (mdc800_usb_waitForIRQ (1,TO_READ_FROM_IRQ))
 						{
 							err ("requesting answer from irq fails");
-							spin_unlock (&mdc800->io_lock);
+							up (&mdc800->io_lock);
 							return -EIO;
 						}
 
@@ -847,7 +885,7 @@
 						if (mdc800_usb_waitForIRQ (0,TO_DEFAULT_COMMAND))
 						{
 							err ("Command Timeout.");
-							spin_unlock (&mdc800->io_lock);
+							up (&mdc800->io_lock);
 							return -EIO;
 						}
 					}
@@ -857,7 +895,7 @@
 		}
 		i++;
 	}
-	spin_unlock (&mdc800->io_lock);
+	up (&mdc800->io_lock);
 	return i;
 }
 
@@ -916,11 +954,15 @@
 	mdc800->dev=0;
 	mdc800->open=0;
 	mdc800->state=NOT_CONNECTED;
-	spin_lock_init (&mdc800->io_lock);
+	init_MUTEX (&mdc800->io_lock);
 
 	init_waitqueue_head (&mdc800->irq_wait);
 	init_waitqueue_head (&mdc800->write_wait);
 	init_waitqueue_head (&mdc800->download_wait);
+
+	mdc800->irq_woken = 0;
+	mdc800->downloaded = 0;
+	mdc800->written = 0;
 
 	try (mdc800->irq_urb_buffer=kmalloc (8, GFP_KERNEL));
 	try (mdc800->write_urb_buffer=kmalloc (8, GFP_KERNEL));
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/usb/serial/Config.in linux/drivers/usb/serial/Config.in
--- linux.orig/drivers/usb/serial/Config.in	Mon Jan  7 23:41:09 2002
+++ linux/drivers/usb/serial/Config.in	Wed Dec 26 15:09:47 2001
@@ -15,6 +15,7 @@
 dep_tristate '  USB Empeg empeg-car Mark I/II Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_EMPEG $CONFIG_USB_SERIAL $CONFIG_EXPERIMENTAL
 dep_tristate '  USB FTDI Single Port Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_FTDI_SIO $CONFIG_USB_SERIAL $CONFIG_EXPERIMENTAL
 dep_tristate '  USB Handspring Visor / Palm m50x / Sony Clie Driver' CONFIG_USB_SERIAL_VISOR $CONFIG_USB_SERIAL
+dep_tristate '  USB Compaq iPAQ Driver' CONFIG_USB_SERIAL_IPAQ $CONFIG_USB_SERIAL
 dep_tristate '  USB IR Dongle Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_IR $CONFIG_USB_SERIAL $CONFIG_EXPERIMENTAL
 dep_tristate '  USB Inside Out Edgeport Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_EDGEPORT $CONFIG_USB_SERIAL $CONFIG_EXPERIMENTAL
 dep_tristate '  USB Keyspan PDA Single Port Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_KEYSPAN_PDA $CONFIG_USB_SERIAL $CONFIG_EXPERIMENTAL
@@ -28,6 +29,7 @@
    dep_mbool '    USB Keyspan USA-19W Firmware' CONFIG_USB_SERIAL_KEYSPAN_USA19W $CONFIG_USB_SERIAL_KEYSPAN
    dep_mbool '    USB Keyspan USA-49W Firmware' CONFIG_USB_SERIAL_KEYSPAN_USA49W $CONFIG_USB_SERIAL_KEYSPAN
 dep_tristate '  USB MCT Single Port Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_MCT_U232 $CONFIG_USB_SERIAL $CONFIG_EXPERIMENTAL
+dep_tristate '  USB KL5KUSB105 (Palmconnect) Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_KLSI $CONFIG_USB_SERIAL $CONFIG_EXPERIMENTAL
 dep_tristate '  USB Prolific 2303 Single Port Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_PL2303 $CONFIG_USB_SERIAL $CONFIG_EXPERIMENTAL
 dep_tristate '  USB REINER SCT cyberJack pinpad/e-com chipcard reader (EXPERIMENTAL)' CONFIG_USB_SERIAL_CYBERJACK $CONFIG_USB_SERIAL $CONFIG_EXPERIMENTAL
 dep_tristate '  USB Xircom / Entregra Single Port Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_XIRCOM $CONFIG_USB_SERIAL $CONFIG_EXPERIMENTAL
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/usb/serial/Makefile linux/drivers/usb/serial/Makefile
--- linux.orig/drivers/usb/serial/Makefile	Mon Jan  7 23:41:09 2002
+++ linux/drivers/usb/serial/Makefile	Wed Dec 26 15:09:47 2001
@@ -8,6 +8,7 @@
 
 obj-$(CONFIG_USB_SERIAL)                        += usbserial.o
 obj-$(CONFIG_USB_SERIAL_VISOR)			+= visor.o
+obj-$(CONFIG_USB_SERIAL_IPAQ)			+= ipaq.o
 obj-$(CONFIG_USB_SERIAL_WHITEHEAT)		+= whiteheat.o
 obj-$(CONFIG_USB_SERIAL_FTDI_SIO)		+= ftdi_sio.o
 obj-$(CONFIG_USB_SERIAL_KEYSPAN_PDA)		+= keyspan_pda.o
@@ -22,7 +23,8 @@
 obj-$(CONFIG_USB_SERIAL_PL2303)			+= pl2303.o
 obj-$(CONFIG_USB_SERIAL_CYBERJACK)		+= cyberjack.o
 obj-$(CONFIG_USB_SERIAL_IR)			+= ir-usb.o
- 
+obj-$(CONFIG_USB_SERIAL_KLSI)			+= kl5kusb105.o
+
 # Objects that export symbols.
 export-objs	:= usbserial.o
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/usb/serial/ipaq.c linux/drivers/usb/serial/ipaq.c
--- linux.orig/drivers/usb/serial/ipaq.c	Thu Jan  1 00:00:00 1970
+++ linux/drivers/usb/serial/ipaq.c	Wed Dec 26 14:28:36 2001
@@ -0,0 +1,532 @@
+/*
+ * USB Compaq iPAQ driver
+ *
+ *	Copyright (C) 2001
+ *	    Ganesh Varadarajan <ganesh@veritas.com>
+ *
+ *	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/kernel.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/fcntl.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/usb.h>
+
+#ifdef CONFIG_USB_SERIAL_DEBUG
+	static int debug = 1;
+#else
+	static int debug = 0;
+#endif
+
+#include "usb-serial.h"
+#include "ipaq.h"
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v0.1"
+#define DRIVER_AUTHOR "Ganesh Varadarajan <ganesh@veritas.com>"
+#define DRIVER_DESC "USB Compaq iPAQ driver"
+
+/* Function prototypes for an ipaq */
+static int  ipaq_open (struct usb_serial_port *port, struct file *filp);
+static void ipaq_close (struct usb_serial_port *port, struct file *filp);
+static int  ipaq_startup (struct usb_serial *serial);
+static void ipaq_shutdown (struct usb_serial *serial);
+static int ipaq_write(struct usb_serial_port *port, int from_user, const unsigned char *buf,
+		       int count);
+static int ipaq_write_bulk(struct usb_serial_port *port, int from_user, const unsigned char *buf,
+			   int count);
+static int ipaq_write_flush(struct usb_serial_port *port);
+static void ipaq_read_bulk_callback (struct urb *urb);
+static void ipaq_write_bulk_callback(struct urb *urb);
+static int ipaq_write_room(struct usb_serial_port *port);
+static int ipaq_chars_in_buffer(struct usb_serial_port *port);
+static void ipaq_destroy_lists(struct usb_serial_port *port);
+
+
+static __devinitdata struct usb_device_id ipaq_id_table [] = {
+	{ USB_DEVICE(IPAQ_VENDOR_ID, IPAQ_PRODUCT_ID) },
+	{ }					/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE (usb, ipaq_id_table);
+
+/* All of the device info needed for the Compaq iPAQ */
+struct usb_serial_device_type ipaq_device = {
+	name:			"Compaq iPAQ",
+	id_table:		ipaq_id_table,
+	needs_interrupt_in:	MUST_HAVE_NOT,
+	needs_bulk_in:		MUST_HAVE,
+	needs_bulk_out:		MUST_HAVE,
+	num_interrupt_in:	0,
+	num_bulk_in:		1,
+	num_bulk_out:		1,
+	num_ports:		1,
+	open:			ipaq_open,
+	close:			ipaq_close,
+	startup:		ipaq_startup,
+	shutdown:		ipaq_shutdown,
+	write:			ipaq_write,
+	write_room:		ipaq_write_room,
+	chars_in_buffer:	ipaq_chars_in_buffer,
+	read_bulk_callback:	ipaq_read_bulk_callback,
+	write_bulk_callback:	ipaq_write_bulk_callback,
+};
+
+static spinlock_t	write_list_lock;
+static int		bytes_in;
+static int		bytes_out;
+
+static int ipaq_open(struct usb_serial_port *port, struct file *filp)
+{
+	struct usb_serial	*serial = port->serial;
+	struct ipaq_private	*priv;
+	struct ipaq_packet	*pkt;
+	int			i, result = 0;
+
+	if (port_paranoia_check(port, __FUNCTION__)) {
+		return -ENODEV;
+	}
+	
+	dbg(__FUNCTION__ " - port %d", port->number);
+
+	down(&port->sem);
+	
+	++port->open_count;
+	MOD_INC_USE_COUNT;
+	
+	if (!port->active) {
+		port->active = 1;
+		bytes_in = 0;
+		bytes_out = 0;
+		priv = (struct ipaq_private *)kmalloc(sizeof(struct ipaq_private), GFP_KERNEL);
+		if (priv == NULL) {
+			err(__FUNCTION__ " - Out of memory");
+			return -ENOMEM;
+		}
+		port->private = (void *)priv;
+		priv->active = 0;
+		priv->queue_len = 0;
+		INIT_LIST_HEAD(&priv->queue);
+		INIT_LIST_HEAD(&priv->freelist);
+
+		for (i = 0; i < URBDATA_QUEUE_MAX / PACKET_SIZE; i++) {
+			pkt = kmalloc(sizeof(struct ipaq_packet), GFP_KERNEL);
+			if (pkt == NULL) {
+				goto enomem;
+			}
+			pkt->data = kmalloc(PACKET_SIZE, GFP_KERNEL);
+			if (pkt->data == NULL) {
+				kfree(pkt);
+				goto enomem;
+			}
+			pkt->len = 0;
+			pkt->written = 0;
+			INIT_LIST_HEAD(&pkt->list);
+			list_add(&pkt->list, &priv->freelist);
+			priv->free_len += PACKET_SIZE;
+		}
+
+		/*
+		 * Force low latency on. This will immediately push data to the line
+		 * discipline instead of queueing.
+		 */
+
+		port->tty->low_latency = 1;
+
+		/*
+		 * Lose the small buffers usbserial provides. Make larger ones.
+		 */
+
+		kfree(port->bulk_in_buffer);
+		kfree(port->bulk_out_buffer);
+		port->bulk_in_buffer = kmalloc(URBDATA_SIZE, GFP_KERNEL);
+		if (port->bulk_in_buffer == NULL) {
+			goto enomem;
+		}
+		port->bulk_out_buffer = kmalloc(URBDATA_SIZE, GFP_KERNEL);
+		if (port->bulk_out_buffer == NULL) {
+			kfree(port->bulk_in_buffer);
+			goto enomem;
+		}
+		port->read_urb->transfer_buffer = port->bulk_in_buffer;
+		port->write_urb->transfer_buffer = port->bulk_out_buffer;
+		port->read_urb->transfer_buffer_length = URBDATA_SIZE;
+		port->bulk_out_size = port->write_urb->transfer_buffer_length = URBDATA_SIZE;
+		
+		/* Start reading from the device */
+		FILL_BULK_URB(port->read_urb, serial->dev, 
+			      usb_rcvbulkpipe(serial->dev, port->bulk_in_endpointAddress),
+			      port->read_urb->transfer_buffer, port->read_urb->transfer_buffer_length,
+			      ipaq_read_bulk_callback, port);
+		result = usb_submit_urb(port->read_urb);
+		if (result) {
+			err(__FUNCTION__ " - failed submitting read urb, error %d", result);
+		}
+
+		/*
+		 * Send out two control messages observed in win98 sniffs. Not sure what
+		 * they do.
+		 */
+
+		result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), 0x22, 0x21,
+				0x1, 0, NULL, 0, 5 * HZ);
+		if (result < 0) {
+			err(__FUNCTION__ " - failed doing control urb, error %d", result);
+		}
+		result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), 0x22, 0x21,
+				0x1, 0, NULL, 0, 5 * HZ);
+		if (result < 0) {
+			err(__FUNCTION__ " - failed doing control urb, error %d", result);
+		}
+	}
+	
+	up(&port->sem);
+	
+	return result;
+
+enomem:
+	ipaq_destroy_lists(port);
+	kfree(priv);
+	err(__FUNCTION__ " - Out of memory");
+	return -ENOMEM;
+}
+
+
+static void ipaq_close(struct usb_serial_port *port, struct file *filp)
+{
+	struct usb_serial	*serial;
+	struct ipaq_private	*priv = port->private;
+
+	if (port_paranoia_check(port, __FUNCTION__)) {
+		return; 
+	}
+	
+	dbg(__FUNCTION__ " - port %d", port->number);
+			 
+	serial = get_usb_serial(port, __FUNCTION__);
+	if (!serial)
+		return;
+	
+	down (&port->sem);
+
+	--port->open_count;
+
+	if (port->open_count <= 0) {
+
+		/*
+		 * shut down bulk read and write
+		 */
+
+		usb_unlink_urb(port->write_urb);
+		usb_unlink_urb(port->read_urb);
+		ipaq_destroy_lists(port);
+		kfree(priv);
+		port->private = NULL;
+		port->active = 0;
+		port->open_count = 0;
+
+	}
+	up (&port->sem);
+
+	/* Uncomment the following line if you want to see some statistics in your syslog */
+	/* info ("Bytes In = %d  Bytes Out = %d", bytes_in, bytes_out); */
+
+	MOD_DEC_USE_COUNT;
+}
+
+static void ipaq_read_bulk_callback(struct urb *urb)
+{
+	struct usb_serial_port	*port = (struct usb_serial_port *)urb->context;
+	struct usb_serial	*serial = get_usb_serial (port, __FUNCTION__);
+	struct tty_struct	*tty;
+	unsigned char		*data = urb->transfer_buffer;
+	int			i, result;
+
+	if (port_paranoia_check(port, __FUNCTION__))
+		return;
+
+	dbg(__FUNCTION__ " - port %d", port->number);
+
+	if (!serial) {
+		dbg(__FUNCTION__ " - bad serial pointer, exiting");
+		return;
+	}
+
+	if (urb->status) {
+		dbg(__FUNCTION__ " - nonzero read bulk status received: %d", urb->status);
+		return;
+	}
+
+	usb_serial_debug_data (__FILE__, __FUNCTION__, urb->actual_length, data);
+
+	tty = port->tty;
+	if (urb->actual_length) {
+		for (i = 0; i < urb->actual_length ; ++i) {
+			/* if we insert more than TTY_FLIPBUF_SIZE characters, we drop them. */
+			if(tty->flip.count >= TTY_FLIPBUF_SIZE) {
+				tty_flip_buffer_push(tty);
+			}
+			/* this doesn't actually push the data through unless tty->low_latency is set */
+			tty_insert_flip_char(tty, data[i], 0);
+		}
+		tty_flip_buffer_push(tty);
+		bytes_in += urb->actual_length;
+	}
+
+	/* Continue trying to always read  */
+	FILL_BULK_URB(port->read_urb, serial->dev, 
+		      usb_rcvbulkpipe(serial->dev, port->bulk_in_endpointAddress),
+		      port->read_urb->transfer_buffer, port->read_urb->transfer_buffer_length,
+		      ipaq_read_bulk_callback, port);
+	result = usb_submit_urb(port->read_urb);
+	if (result)
+		err(__FUNCTION__ " - failed resubmitting read urb, error %d", result);
+	return;
+}
+
+static int ipaq_write(struct usb_serial_port *port, int from_user, const unsigned char *buf,
+		       int count)
+{
+	const unsigned char	*current_position = buf;
+	int			bytes_sent = 0;
+	int			transfer_size;
+
+	dbg(__FUNCTION__ " - port %d", port->number);
+
+	usb_serial_debug_data(__FILE__, __FUNCTION__, count, buf);
+	
+	while (count > 0) {
+		transfer_size = min(count, PACKET_SIZE);
+		if (ipaq_write_bulk(port, from_user, current_position, transfer_size)) {
+			break;
+		}
+		current_position += transfer_size;
+		bytes_sent += transfer_size;
+		count -= transfer_size;
+		bytes_out += transfer_size;
+	}
+
+	return bytes_sent;
+} 
+
+static int ipaq_write_bulk(struct usb_serial_port *port, int from_user, const unsigned char *buf,
+			   int count)
+{
+	struct ipaq_private	*priv = port->private;
+	struct ipaq_packet	*pkt = NULL;
+	int			result = 0;
+	unsigned long		flags;
+
+	if (priv->free_len <= 0) {
+		dbg(__FUNCTION__ " - we're stuffed");
+		return -EAGAIN;
+	}
+
+	spin_lock_irqsave(&write_list_lock, flags);
+	if (!list_empty(&priv->freelist)) {
+		pkt = list_entry(priv->freelist.next, struct ipaq_packet, list);
+		list_del(&pkt->list);
+		priv->free_len -= PACKET_SIZE;
+	}
+	spin_unlock_irqrestore(&write_list_lock, flags);
+	if (pkt == NULL) {
+		dbg(__FUNCTION__ " - we're stuffed");
+		return -EAGAIN;
+	}
+
+	if (from_user) {
+		copy_from_user(pkt->data, buf, count);
+	} else {
+		memcpy(pkt->data, buf, count);
+	}
+	usb_serial_debug_data(__FILE__, __FUNCTION__, count, pkt->data);
+
+	pkt->len = count;
+	pkt->written = 0;
+	spin_lock_irqsave(&write_list_lock, flags);
+	list_add_tail(&pkt->list, &priv->queue);
+	priv->queue_len += count;
+	if (priv->active == 0) {
+		priv->active = 1;
+		result = ipaq_write_flush(port);
+	}
+	spin_unlock_irqrestore(&write_list_lock, flags);
+	return result;
+}
+
+static int ipaq_write_flush(struct usb_serial_port *port)
+{
+	struct ipaq_private	*priv = (struct ipaq_private *)port->private;
+	struct usb_serial	*serial = port->serial;
+	int			count, room, result;
+	struct ipaq_packet	*pkt;
+	struct urb		*urb = port->write_urb;
+	struct list_head	*tmp;
+
+	if (urb->status == -EINPROGRESS) {
+		/* Should never happen */
+		err(__FUNCTION__ " - flushing while urb is active !");
+		return -EAGAIN;
+	}
+	room = URBDATA_SIZE;
+	for (tmp = priv->queue.next; tmp != &priv->queue;) {
+		pkt = list_entry(tmp, struct ipaq_packet, list);
+		tmp = tmp->next;
+		count = min(room, (int)(pkt->len - pkt->written));
+		memcpy(urb->transfer_buffer + (URBDATA_SIZE - room),
+		       pkt->data + pkt->written, count);
+		room -= count;
+		pkt->written += count;
+		priv->queue_len -= count;
+		if (pkt->written == pkt->len) {
+			list_del(&pkt->list);
+			list_add(&pkt->list, &priv->freelist);
+			priv->free_len += PACKET_SIZE;
+		}
+		if (room == 0) {
+			break;
+		}
+	}
+
+	count = URBDATA_SIZE - room;
+	FILL_BULK_URB(port->write_urb, serial->dev, 
+		      usb_sndbulkpipe(serial->dev, port->bulk_out_endpointAddress),
+		      port->write_urb->transfer_buffer, count, ipaq_write_bulk_callback,
+		      port);
+	result = usb_submit_urb(urb);
+	if (result) {
+		err(__FUNCTION__ " - failed submitting write urb, error %d", result);
+	}
+	return result;
+}
+
+static void ipaq_write_bulk_callback(struct urb *urb)
+{
+	struct usb_serial_port	*port = (struct usb_serial_port *)urb->context;
+	struct ipaq_private	*priv = (struct ipaq_private *)port->private;
+	unsigned long		flags;
+
+	if (port_paranoia_check (port, __FUNCTION__)) {
+		return;
+	}
+	
+	dbg(__FUNCTION__ " - port %d", port->number);
+	
+	if (urb->status) {
+		dbg(__FUNCTION__ " - nonzero write bulk status received: %d", urb->status);
+	}
+
+	spin_lock_irqsave(&write_list_lock, flags);
+	if (!list_empty(&priv->queue)) {
+		ipaq_write_flush(port);
+	} else {
+		priv->active = 0;
+	}
+	spin_unlock_irqrestore(&write_list_lock, flags);
+	queue_task(&port->tqueue, &tq_immediate);
+	mark_bh(IMMEDIATE_BH);
+	
+	return;
+}
+
+static int ipaq_write_room(struct usb_serial_port *port)
+{
+	struct ipaq_private	*priv = (struct ipaq_private *)port->private;
+
+	dbg(__FUNCTION__ " - freelen %d", priv->free_len);
+	return priv->free_len;
+}
+
+static int ipaq_chars_in_buffer(struct usb_serial_port *port)
+{
+	struct ipaq_private	*priv = (struct ipaq_private *)port->private;
+
+	dbg(__FUNCTION__ " - queuelen %d", priv->queue_len);
+	return priv->queue_len;
+}
+
+static void ipaq_destroy_lists(struct usb_serial_port *port)
+{
+	struct ipaq_private	*priv = (struct ipaq_private *)port->private;
+	struct list_head	*tmp;
+	struct ipaq_packet	*pkt;
+
+	for (tmp = priv->queue.next; tmp != &priv->queue;) {
+		pkt = list_entry(tmp, struct ipaq_packet, list);
+		tmp = tmp->next;
+		kfree(pkt->data);
+		kfree(pkt);
+	}
+	for (tmp = priv->freelist.next; tmp != &priv->freelist;) {
+		pkt = list_entry(tmp, struct ipaq_packet, list);
+		tmp = tmp->next;
+		kfree(pkt->data);
+		kfree(pkt);
+	}
+	return;
+}
+
+
+static int ipaq_startup(struct usb_serial *serial)
+{
+	dbg(__FUNCTION__);
+	usb_set_configuration(serial->dev, 1);
+	return 0;
+}
+
+static void ipaq_shutdown(struct usb_serial *serial)
+{
+	int i;
+
+	dbg (__FUNCTION__);
+
+	/* stop reads and writes on all ports */
+	for (i=0; i < serial->num_ports; ++i) {
+		while (serial->port[i].open_count > 0) {
+			ipaq_close(&serial->port[i], NULL);
+		}
+	}
+}
+
+static int __init ipaq_init(void)
+{
+	usb_serial_register(&ipaq_device);
+	info(DRIVER_DESC " " DRIVER_VERSION);
+
+	return 0;
+}
+
+
+static void __exit ipaq_exit(void)
+{
+	usb_serial_deregister(&ipaq_device);
+}
+
+
+module_init(ipaq_init);
+module_exit(ipaq_exit);
+
+MODULE_AUTHOR( DRIVER_AUTHOR );
+MODULE_DESCRIPTION( DRIVER_DESC );
+MODULE_LICENSE("GPL");
+
+MODULE_PARM(debug, "i");
+MODULE_PARM_DESC(debug, "Debug enabled or not");
+
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/usb/serial/ipaq.h linux/drivers/usb/serial/ipaq.h
--- linux.orig/drivers/usb/serial/ipaq.h	Thu Jan  1 00:00:00 1970
+++ linux/drivers/usb/serial/ipaq.h	Wed Dec 26 14:28:36 2001
@@ -0,0 +1,60 @@
+/*
+ * USB Compaq iPAQ driver
+ *
+ *	Copyright (C) 2001
+ *	    Ganesh Varadarajan <ganesh@veritas.com>
+ *
+ *	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.
+ *
+ * 
+ */
+
+#ifndef __LINUX_USB_SERIAL_IPAQ_H
+#define __LINUX_USB_SERIAL_IPAQ_H
+
+
+#define IPAQ_VENDOR_ID			0x049f
+#define IPAQ_PRODUCT_ID			0x0003
+
+/*
+ * Since we can't queue our bulk write urbs (don't know why - it just
+ * doesn't work), we can send down only one write urb at a time. The simplistic
+ * approach taken by the generic usbserial driver will work, but it's not good
+ * for performance. Therefore, we buffer upto URBDATA_QUEUE_MAX bytes of write
+ * requests coming from the line discipline. This is done by chaining them
+ * in lists of struct ipaq_packet, each packet holding a maximum of
+ * PACKET_SIZE bytes.
+ *
+ * ipaq_write() can be called from bottom half context; hence we can't
+ * allocate memory for packets there. So we initialize a pool of packets at
+ * the first open and maintain a freelist.
+ *
+ * The value of PACKET_SIZE was empirically determined by
+ * checking the maximum write sizes sent down by the ppp ldisc.
+ * URBDATA_QUEUE_MAX is set to 64K, which is the maximum TCP window size
+ * supported by the iPAQ.
+ */
+
+struct ipaq_packet {
+	char			*data;
+	size_t			len;
+	size_t			written;
+	struct list_head	list;
+};
+
+struct ipaq_private {
+	int			active;
+	int			queue_len;
+	int			free_len;
+	struct list_head	queue;
+	struct list_head	freelist;
+};
+
+#define URBDATA_SIZE		4096
+#define URBDATA_QUEUE_MAX	(64 * 1024)
+#define PACKET_SIZE		256
+
+#endif
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/usb/serial/kl5kusb105.c linux/drivers/usb/serial/kl5kusb105.c
--- linux.orig/drivers/usb/serial/kl5kusb105.c	Thu Jan  1 00:00:00 1970
+++ linux/drivers/usb/serial/kl5kusb105.c	Wed Dec 26 15:09:47 2001
@@ -0,0 +1,1132 @@
+/*
+ * KLSI KL5KUSB105 chip RS232 converter driver
+ *
+ *   Copyright (C) 2001 Utz-Uwe Haus <haus@uuhaus.de>
+ *
+ *   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.
+ *
+ * All information about the device was acquired using SniffUSB ans snoopUSB
+ * on Windows98.
+ * It was written out of frustration with the PalmConnect USB Serial adapter
+ * sold by Palm Inc.
+ * Neither Palm, nor their contractor (MCCI) or their supplier (KLSI) provided
+ * information that was not already available.
+ *
+ * It seems that KLSI bought some silicon-design information from ScanLogic, 
+ * whose SL11R processor is at the core of the KL5KUSB chipset from KLSI.
+ * KLSI has firmware available for their devices; it is probable that the
+ * firmware differs from that used by KLSI in their products. If you have an
+ * original KLSI device and can provide some information on it, I would be 
+ * most interested in adding support for it here. If you have any information 
+ * on the protocol used (or find errors in my reverse-engineered stuff), please
+ * let me know.
+ *
+ * The code was only tested with a PalmConnect USB adapter; if you
+ * are adventurous, try it with any KLSI-based device and let me know how it
+ * breaks so that I can fix it!
+ */
+
+/* TODO:
+ *	check modem line signals
+ *	implement handshaking or decide that we do not support it
+ */
+
+/* History:
+ *   0.3a - implemented pools of write URBs
+ *   0.3  - alpha version for public testing
+ *   0.2  - TIOCMGET works, so autopilot(1) can be used!
+ *   0.1  - can be used to to pilot-xfer -p /dev/ttyUSB0 -l
+ *
+ *   The driver skeleton is mainly based on mct_u232.c and various other 
+ *   pieces of code shamelessly copied from the drivers/usb/serial/ directory.
+ */
+
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+/*#include <linux/fcntl.h>*/
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/module.h>
+/*#include <linux/spinlock.h>*/
+#include <linux/usb.h>
+
+#ifdef CONFIG_USB_SERIAL_DEBUG
+ 	static int debug = 1;
+#else
+ 	static int debug;
+#endif
+
+#include "usb-serial.h"
+#include "kl5kusb105.h"
+
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v0.3a"
+#define DRIVER_AUTHOR "Utz-Uwe Haus <haus@uuhaus.de>"
+#define DRIVER_DESC "KLSI KL5KUSB105 chipset USB->Serial Converter driver"
+
+
+/*
+ * Function prototypes
+ */
+static int  klsi_105_startup	         (struct usb_serial *serial);
+static void klsi_105_shutdown	         (struct usb_serial *serial);
+static int  klsi_105_open	         (struct usb_serial_port *port,
+					  struct file *filp);
+static void klsi_105_close	         (struct usb_serial_port *port,
+					  struct file *filp);
+static int  klsi_105_write	         (struct usb_serial_port *port,
+					  int from_user,
+					  const unsigned char *buf,
+					  int count);
+static void klsi_105_write_bulk_callback (struct urb *urb);
+static int  klsi_105_chars_in_buffer     (struct usb_serial_port *port);
+static int  klsi_105_write_room          (struct usb_serial_port *port);
+
+static void klsi_105_read_bulk_callback  (struct urb *urb);
+static void klsi_105_set_termios         (struct usb_serial_port *port,
+					  struct termios * old);
+static int  klsi_105_ioctl	         (struct usb_serial_port *port,
+					  struct file * file,
+					  unsigned int cmd,
+					  unsigned long arg);
+static void klsi_105_throttle		 (struct usb_serial_port *port);
+static void klsi_105_unthrottle		 (struct usb_serial_port *port);
+/*
+static void klsi_105_break_ctl	         (struct usb_serial_port *port,
+					  int break_state );
+ */
+
+/*
+ * All of the device info needed for the MCT USB-RS232 converter.
+ */
+static __devinitdata struct usb_device_id id_table_combined [] = {
+	{ USB_DEVICE(PALMCONNECT_VID, PALMCONNECT_PID) },
+	{ USB_DEVICE(KLSI_VID, KLSI_KL5KUSB105D_PID) },
+	{ }		/* Terminating entry */
+};
+
+static __devinitdata struct usb_device_id palmconnect_table [] = {
+        { USB_DEVICE(PALMCONNECT_VID, PALMCONNECT_PID) },
+        { }                        /* Terminating entry */
+};
+
+static __devinitdata struct usb_device_id kl5kusb105d_table [] = {
+        { USB_DEVICE(KLSI_VID, KLSI_KL5KUSB105D_PID) },
+        { }                        /* Terminating entry */
+};
+
+
+MODULE_DEVICE_TABLE (usb, id_table_combined);
+
+
+static struct usb_serial_device_type palmconnect_device = {
+	name:		     "PalmConnect USB Serial",
+	id_table:	     palmconnect_table,
+	needs_interrupt_in:  MUST_HAVE,	  /* 1 interrupt-in endpoints */
+	needs_bulk_in:	     MUST_HAVE,   /* 1 bulk-in endpoint */
+	needs_bulk_out:	     MUST_HAVE,	  /* 1 bulk-out endpoint */
+	num_interrupt_in:    1,
+	num_bulk_in:	     1,
+	num_bulk_out:	     1,
+	num_ports:	     1,
+	open:		     klsi_105_open,
+	close:		     klsi_105_close,
+	write:		     klsi_105_write,
+	write_bulk_callback: klsi_105_write_bulk_callback,
+	chars_in_buffer:     klsi_105_chars_in_buffer,
+	write_room:          klsi_105_write_room,
+	read_bulk_callback:  klsi_105_read_bulk_callback,
+	ioctl:		     klsi_105_ioctl,
+	set_termios:	     klsi_105_set_termios,
+	/*break_ctl:	     klsi_105_break_ctl,*/
+	startup:	     klsi_105_startup,
+	shutdown:	     klsi_105_shutdown,
+	throttle:	     klsi_105_throttle,
+	unthrottle:	     klsi_105_unthrottle,
+};
+
+static struct usb_serial_device_type kl5kusb105d_device = {
+	name:		     "generic KL5KUSB105D USB->Serial",
+	id_table:	     kl5kusb105d_table,
+	needs_interrupt_in:  MUST_HAVE,	 /* 1 interrupt-in endpoints */
+	needs_bulk_in:	     MUST_HAVE,  /* 1 bulk-in endpoint */
+	needs_bulk_out:	     MUST_HAVE,	 /* 1 bulk-out endpoint */
+	num_interrupt_in:    1,
+	num_bulk_in:	     1,
+	num_bulk_out:	     1,
+	num_ports:	     1,
+	open:		     klsi_105_open,
+	close:		     klsi_105_close,
+	write:		     klsi_105_write,
+	write_bulk_callback: klsi_105_write_bulk_callback,
+	chars_in_buffer:     klsi_105_chars_in_buffer,
+	write_room:          klsi_105_write_room,
+	read_bulk_callback:  klsi_105_read_bulk_callback,
+	ioctl:		     klsi_105_ioctl,
+	set_termios:	     klsi_105_set_termios,
+	/*break_ctl:	     klsi_105_break_ctl,*/
+	startup:	     klsi_105_startup,
+	shutdown:	     klsi_105_shutdown,
+	throttle:	     klsi_105_throttle,
+	unthrottle:	     klsi_105_unthrottle,
+};
+
+
+struct klsi_105_port_settings {
+	__u8	pktlen;		/* always 5, it seems */
+	__u8	baudrate;
+	__u8	databits;
+	__u8	unknown1;
+	__u8	unknown2;
+} __attribute__ ((packed));
+
+/* we implement a pool of NUM_URBS urbs per usb_serial */
+#define NUM_URBS			1
+#define URB_TRANSFER_BUFFER_SIZE	64
+struct klsi_105_private {
+	struct klsi_105_port_settings	cfg;
+	struct termios			termios;
+	unsigned long			line_state; /* modem line settings */
+	/* write pool */
+	struct urb *			write_urb_pool[NUM_URBS];
+	spinlock_t			write_urb_pool_lock;
+	unsigned long			bytes_in;
+	unsigned long			bytes_out;
+};
+
+
+/*
+ * Handle vendor specific USB requests
+ */
+
+
+#define KLSI_TIMEOUT	 (HZ * 5 ) /* default urb timeout */
+
+static int klsi_105_chg_port_settings(struct usb_serial *serial,
+				      struct klsi_105_port_settings *settings)
+{
+	int rc;
+
+        rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+			     KL5KUSB105A_SIO_SET_DATA,
+                             USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_INTERFACE,
+			     0, /* value */
+			     0, /* index */
+			     settings,
+			     sizeof(struct klsi_105_port_settings),
+			     KLSI_TIMEOUT);
+	if (rc < 0)
+		err("Change port settings failed (error = %d)", rc);
+	info(__FUNCTION__ " - %d byte block, baudrate %x, databits %d, u1 %d, u2 %d",
+	    settings->pktlen,
+	    settings->baudrate, settings->databits,
+	    settings->unknown1, settings->unknown2);
+        return rc;
+} /* klsi_105_chg_port_settings */
+
+/* translate a 16-bit status value from the device to linux's TIO bits */
+static unsigned long klsi_105_status2linestate(const __u16 status)
+{
+	unsigned long res = 0;
+
+	res =   ((status & KL5KUSB105A_DSR) ? TIOCM_DSR : 0)
+	      | ((status & KL5KUSB105A_CTS) ? TIOCM_CTS : 0)
+	      ;
+
+	return res;
+}
+/* 
+ * Read line control via vendor command and return result through
+ * *line_state_p 
+ */
+/* It seems that the status buffer has always only 2 bytes length */
+#define KLSI_STATUSBUF_LEN	2
+static int klsi_105_get_line_state(struct usb_serial *serial,
+				   unsigned long *line_state_p)
+{
+	int rc;
+	__u8 status_buf[KLSI_STATUSBUF_LEN] = { -1,-1};
+	__u16 status;
+
+	info(__FUNCTION__ " - sending SIO Poll request");
+        rc = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
+			     KL5KUSB105A_SIO_POLL,
+                             USB_TYPE_VENDOR | USB_DIR_IN,
+			     0, /* value */
+			     0, /* index */
+			     status_buf, KLSI_STATUSBUF_LEN,
+			     10*HZ
+			     );
+	if (rc < 0)
+		err("Reading line status failed (error = %d)", rc);
+	else {
+		status = status_buf[0] + (status_buf[1]<<8);
+
+		info(__FUNCTION__ " - read status %x %x",
+		     status_buf[0], status_buf[1]);
+
+		*line_state_p = klsi_105_status2linestate(status);
+	}
+
+        return rc;
+}
+
+
+/*
+ * Driver's tty interface functions
+ */
+
+static int klsi_105_startup (struct usb_serial *serial)
+{
+	struct klsi_105_private *priv;
+	int i;
+
+	/* check if we support the product id (see keyspan.c)
+	 * FIXME
+	 */
+
+	/* allocate the private data structure */
+	for (i=0; i<serial->num_ports; i++) {
+		serial->port[i].private = kmalloc(sizeof(struct klsi_105_private),
+						   GFP_KERNEL);
+		if (!serial->port[i].private) {
+			dbg(__FUNCTION__ "kmalloc for klsi_105_private failed.");
+			return (-1); /* error */
+		}
+		priv = (struct klsi_105_private *)serial->port[i].private;
+		/* set initial values for control structures */
+		priv->cfg.pktlen    = 5;
+		priv->cfg.baudrate  = kl5kusb105a_sio_b9600;
+		priv->cfg.databits  = kl5kusb105a_dtb_8;
+		priv->cfg.unknown1  = 0;
+		priv->cfg.unknown2  = 1;
+
+		priv->line_state    = 0;
+
+		priv->bytes_in	    = 0;
+		priv->bytes_out	    = 0;
+
+		spin_lock_init (&priv->write_urb_pool_lock);
+		for (i=0; i<NUM_URBS; i++) {
+			struct urb* urb = usb_alloc_urb(0);
+
+			priv->write_urb_pool[i] = urb;
+			if (urb == NULL) {
+				err("No more urbs???");
+				continue;
+			}
+
+			urb->transfer_buffer = NULL;
+			urb->transfer_buffer = kmalloc (URB_TRANSFER_BUFFER_SIZE,
+							GFP_KERNEL);
+			if (!urb->transfer_buffer) {
+				err (__FUNCTION__ 
+				     " - out of memory for urb buffers.");
+				continue;
+			}
+		}
+
+		/* priv->termios is left uninitalized until port opening */
+		init_waitqueue_head(&serial->port[i].write_wait);
+	}
+	
+	return (0);
+} /* klsi_105_startup */
+
+
+static void klsi_105_shutdown (struct usb_serial *serial)
+{
+	int i;
+	
+	dbg (__FUNCTION__);
+
+	/* stop reads and writes on all ports */
+	for (i=0; i < serial->num_ports; ++i) {
+		struct klsi_105_private *priv = 
+			(struct klsi_105_private*) serial->port[i].private;
+		unsigned long flags;
+		while (serial->port[i].open_count > 0) {
+			klsi_105_close (&serial->port[i], NULL);
+		}
+
+		if (priv) {
+			/* kill our write urb pool */
+			int j;
+			struct urb **write_urbs = priv->write_urb_pool;
+			spin_lock_irqsave(&priv->write_urb_pool_lock,flags);
+
+			for (j = 0; j < NUM_URBS; j++) {
+				if (write_urbs[j]) {
+					/* FIXME - uncomment the following
+					 * usb_unlink_urb call when the host
+					 * controllers get fixed to set
+					 * urb->dev = NULL after the urb is
+					 * finished.  Otherwise this call
+					 * oopses. */
+					/* usb_unlink_urb(write_urbs[j]); */
+					if (write_urbs[j]->transfer_buffer)
+						    kfree(write_urbs[j]->transfer_buffer);
+					usb_free_urb (write_urbs[j]);
+				}
+			}
+
+			spin_unlock_irqrestore (&priv->write_urb_pool_lock,
+					       	flags);
+
+			kfree(serial->port[i].private);
+		}
+	}
+} /* klsi_105_shutdown */
+
+static int  klsi_105_open (struct usb_serial_port *port, struct file *filp)
+{
+	struct usb_serial *serial = port->serial;
+	struct klsi_105_private *priv = (struct klsi_105_private *)port->private;
+	int retval = 0;
+
+	dbg(__FUNCTION__" port %d", port->number);
+
+	down (&port->sem);
+	
+	++port->open_count;
+	MOD_INC_USE_COUNT;
+
+	if (!port->active) {
+		int rc;
+		int i;
+		unsigned long line_state;
+		port->active = 1;
+
+		/* force low_latency on so that our tty_push actually forces
+		 * the data through
+		 * port->tty->low_latency = 1; */
+
+		/* Do a defined restart:
+		 * Set up sane default baud rate and send the 'READ_ON'
+		 * vendor command. 
+		 * FIXME: set modem line control (how?)
+		 * Then read the modem line control and store values in
+		 * priv->line_state.
+		 */
+		priv->cfg.pktlen   = 5;
+		priv->cfg.baudrate = kl5kusb105a_sio_b9600;
+		priv->cfg.databits = kl5kusb105a_dtb_8;
+		priv->cfg.unknown1 = 0;
+		priv->cfg.unknown2 = 1;
+		klsi_105_chg_port_settings(serial, &(priv->cfg));
+		
+		/* set up termios structure */
+		priv->termios.c_iflag = port->tty->termios->c_iflag;
+		priv->termios.c_oflag = port->tty->termios->c_oflag;
+		priv->termios.c_cflag = port->tty->termios->c_cflag;
+		priv->termios.c_lflag = port->tty->termios->c_lflag;
+		for (i=0; i<NCCS; i++)
+			priv->termios.c_cc[i] = port->tty->termios->c_cc[i];
+
+
+		/* READ_ON and urb submission */
+		FILL_BULK_URB(port->read_urb, serial->dev, 
+			      usb_rcvbulkpipe(serial->dev,
+					      port->bulk_in_endpointAddress),
+			      port->read_urb->transfer_buffer,
+			      port->read_urb->transfer_buffer_length,
+			      klsi_105_read_bulk_callback,
+			      port);
+		port->read_urb->transfer_flags |= USB_QUEUE_BULK;
+
+		rc = usb_submit_urb(port->read_urb);
+		if (rc) {
+			err(__FUNCTION__ 
+			    " - failed submitting read urb, error %d", rc);
+			retval = rc;
+			goto exit;
+		}
+
+		rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev,0),
+				     KL5KUSB105A_SIO_CONFIGURE,
+				     USB_TYPE_VENDOR|USB_DIR_OUT|USB_RECIP_INTERFACE,
+				     KL5KUSB105A_SIO_CONFIGURE_READ_ON,
+				     0, /* index */
+				     NULL,
+				     0,
+				     KLSI_TIMEOUT);
+		if (rc < 0) {
+			err("Enabling read failed (error = %d)", rc);
+			retval = rc;
+		} else 
+			dbg(__FUNCTION__ " - enabled reading");
+
+		rc = klsi_105_get_line_state(serial, &line_state);
+		if (rc >= 0) {
+			priv->line_state = line_state;
+			dbg(__FUNCTION__ 
+			    " - read line state 0x%lx", line_state);
+			retval = 0;
+		} else
+			retval = rc;
+	}
+
+exit:
+	up (&port->sem);
+	
+	return retval;
+} /* klsi_105_open */
+
+
+static void klsi_105_close (struct usb_serial_port *port, struct file *filp)
+{
+	struct usb_serial *serial;
+	struct klsi_105_private *priv 
+		= (struct klsi_105_private *)port->private;
+	dbg(__FUNCTION__" port %d", port->number);
+
+	serial = get_usb_serial (port, __FUNCTION__);
+
+	if(!serial)
+		return;
+
+	down (&port->sem);
+
+	--port->open_count;
+
+	if (port->open_count <= 0) {
+		/* send READ_OFF */
+		int rc = usb_control_msg(serial->dev,
+					 usb_sndctrlpipe(serial->dev, 0),
+					 KL5KUSB105A_SIO_CONFIGURE,
+					 USB_TYPE_VENDOR | USB_DIR_OUT,
+					 KL5KUSB105A_SIO_CONFIGURE_READ_OFF,
+					 0, /* index */
+					 NULL, 0,
+					 KLSI_TIMEOUT);
+		if (rc < 0)
+			    err("Disabling read failed (error = %d)", rc);
+
+		/* shutdown our bulk reads and writes */
+		usb_unlink_urb (port->write_urb);
+		usb_unlink_urb (port->read_urb);
+		/* unlink our write pool */
+		/* FIXME */
+		/* wgg - do I need this? I think so. */
+		usb_unlink_urb (port->interrupt_in_urb);
+		port->active = 0;
+		info("kl5kusb105 port stats: %ld bytes in, %ld bytes out", priv->bytes_in, priv->bytes_out);
+	}
+	
+	up (&port->sem);
+	MOD_DEC_USE_COUNT;
+} /* klsi_105_close */
+
+
+/* We need to write a complete 64-byte data block and encode the
+ * number actually sent in the first double-byte, LSB-order. That 
+ * leaves at most 62 bytes of payload.
+ */
+#define KLSI_105_DATA_OFFSET	2   /* in the bulk urb data block */
+
+static int klsi_105_write (struct usb_serial_port *port, int from_user,
+			   const unsigned char *buf, int count)
+{
+	struct usb_serial *serial = port->serial;
+	struct klsi_105_private *priv = 
+		(struct klsi_105_private*) port->private;
+	int result, size;
+	int bytes_sent=0;
+
+	dbg(__FUNCTION__ " - port %d", port->number);
+
+	down (&port->sem);	/* to lock against someone else trying to
+				   take an URB we just selected from the pool */
+
+	while (count > 0) {
+		/* try to find a free urb (write 0 bytes if none) */
+		struct urb *urb = NULL;
+		unsigned long flags;
+		int i;
+		/* since the pool is per-port we might not need the spin lock !? */
+		spin_lock_irqsave (&priv->write_urb_pool_lock, flags);
+		for (i=0; i<NUM_URBS; i++) {
+			if (priv->write_urb_pool[i]->status != -EINPROGRESS) {
+				urb = priv->write_urb_pool[i];
+				dbg(__FUNCTION__ " - using pool URB %d", i);
+				break;
+			}
+		}
+		spin_unlock_irqrestore (&priv->write_urb_pool_lock, flags);
+
+		if (urb==NULL) {
+			dbg (__FUNCTION__ " - no more free urbs");
+			goto exit;
+		}
+
+		if (urb->transfer_buffer == NULL) {
+			urb->transfer_buffer = kmalloc (URB_TRANSFER_BUFFER_SIZE, GFP_KERNEL);
+			if (urb->transfer_buffer == NULL) {
+				err(__FUNCTION__ " - no more kernel memory...");
+				goto exit;
+			}
+		}
+
+		size = min (count, port->bulk_out_size - KLSI_105_DATA_OFFSET);
+		size = min (size, URB_TRANSFER_BUFFER_SIZE - KLSI_105_DATA_OFFSET);
+
+		if (from_user) {
+			if (copy_from_user(urb->transfer_buffer
+					   + KLSI_105_DATA_OFFSET, buf, size)) {
+				up (&port->sem);
+				return -EFAULT;
+			}
+		} else {
+			memcpy (urb->transfer_buffer + KLSI_105_DATA_OFFSET,
+			       	buf, size);
+		}
+
+		/* write payload size into transfer buffer */
+		((__u8 *)urb->transfer_buffer)[0] = (__u8) (size & 0xFF);
+		((__u8 *)urb->transfer_buffer)[1] = (__u8) ((size & 0xFF00)>>8);
+
+		/* set up our urb */
+		FILL_BULK_URB(urb, serial->dev,
+			      usb_sndbulkpipe(serial->dev,
+					      port->bulk_out_endpointAddress),
+			      urb->transfer_buffer,
+			      URB_TRANSFER_BUFFER_SIZE,
+			      klsi_105_write_bulk_callback,
+			      port);
+		urb->transfer_flags |= USB_QUEUE_BULK;
+
+
+		/* send the data out the bulk port */
+		result = usb_submit_urb(urb);
+		if (result) {
+			err(__FUNCTION__
+			    " - failed submitting write urb, error %d", result);
+			goto exit;
+		}
+		buf += size;
+		bytes_sent += size;
+		count -= size;
+	}
+exit:
+	up (&port->sem);
+	priv->bytes_out+=bytes_sent;
+
+	return bytes_sent;	/* that's how much we wrote */
+} /* klsi_105_write */
+
+static void klsi_105_write_bulk_callback ( struct urb *urb)
+{
+	struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
+	struct usb_serial *serial = port->serial;
+
+	dbg(__FUNCTION__ " - port %d", port->number);
+	
+	if (!serial) {
+		dbg(__FUNCTION__ " - bad serial pointer, exiting");
+		return;
+	}
+
+	if (urb->status) {
+		dbg(__FUNCTION__ " - nonzero write bulk status received: %d",
+		    urb->status);
+		return;
+	}
+
+	/* from generic_write_bulk_callback */
+	queue_task(&port->tqueue, &tq_immediate);
+	mark_bh(IMMEDIATE_BH);
+
+	return;
+} /* klsi_105_write_bulk_completion_callback */
+
+
+/* return number of characters currently in the writing process */
+static int klsi_105_chars_in_buffer (struct usb_serial_port *port)
+{
+	int chars = 0;
+	int i;
+	unsigned long flags;
+	struct klsi_105_private *priv = 
+		(struct klsi_105_private*) port->private;
+
+	spin_lock_irqsave (&priv->write_urb_pool_lock, flags);
+
+	for (i = 0; i < NUM_URBS; ++i) {
+		if (priv->write_urb_pool[i]->status == -EINPROGRESS) {
+			chars += URB_TRANSFER_BUFFER_SIZE;
+		}
+	}
+
+	spin_unlock_irqrestore (&priv->write_urb_pool_lock, flags);
+
+	dbg (__FUNCTION__ " - returns %d", chars);
+	return (chars);
+}
+
+static int klsi_105_write_room (struct usb_serial_port *port)
+{
+	unsigned long flags;
+	int i;
+	int room = 0;
+	struct klsi_105_private *priv = 
+		(struct klsi_105_private*) port->private;
+
+	spin_lock_irqsave (&priv->write_urb_pool_lock, flags);
+	for (i = 0; i < NUM_URBS; ++i) {
+		if (priv->write_urb_pool[i]->status != -EINPROGRESS) {
+			room += URB_TRANSFER_BUFFER_SIZE;
+		}
+	}
+
+	spin_unlock_irqrestore (&priv->write_urb_pool_lock, flags);
+
+	dbg(__FUNCTION__ " - returns %d", room);
+	return (room);
+}
+
+
+
+static void klsi_105_read_bulk_callback (struct urb *urb)
+{
+	struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
+	struct usb_serial *serial = port->serial;
+	struct klsi_105_private *priv = 
+		(struct klsi_105_private*) port->private;
+	struct tty_struct *tty;
+	unsigned char *data = urb->transfer_buffer;
+	int rc;
+
+        dbg(__FUNCTION__ " - port %d", port->number);
+
+	/* The urb might have been killed. */
+        if (urb->status) {
+                dbg(__FUNCTION__ " - nonzero read bulk status received: %d",
+		    urb->status);
+                return;
+        }
+	if (!serial) {
+		dbg(__FUNCTION__ " - bad serial pointer, exiting");
+		return;
+	}
+	
+	/* The data received is again preceded by a length double-byte in LSB-
+	 * first order (see klsi_105_write() )
+	 */
+	if (urb->actual_length == 0) {
+		/* empty urbs seem to happen, we ignore them */
+		/* dbg(__FUNCTION__ " - emtpy URB"); */
+	       ;
+	} else if (urb->actual_length <= 2) {
+		dbg(__FUNCTION__ " - size %d URB not understood",
+		    urb->actual_length);
+		usb_serial_debug_data (__FILE__, __FUNCTION__, urb->actual_length, data);
+	} else {
+		int i;
+		int bytes_sent = ((__u8 *) data)[0] +
+				 ((unsigned int) ((__u8 *) data)[1] << 8);
+		tty = port->tty;
+		/* we should immediately resubmit the URB, before attempting
+		 * to pass the data on to the tty layer. But that needs locking
+		 * against re-entry an then mixed-up data because of
+		 * intermixed tty_flip_buffer_push()s
+		 * FIXME
+		 */ 
+		usb_serial_debug_data (__FILE__, __FUNCTION__,
+				       urb->actual_length, data);
+
+		if (bytes_sent + 2 > urb->actual_length) {
+			dbg(__FUNCTION__ 
+			    " - trying to read more data than available"
+			    " (%d vs. %d)",
+			    bytes_sent+2, urb->actual_length);
+			/* cap at implied limit */
+			bytes_sent = urb->actual_length - 2;
+		}
+
+		for (i = 2; i < 2+bytes_sent; i++) {
+			/* if we insert more than TTY_FLIPBUF_SIZE characters,
+			 * we drop them. */
+			if(tty->flip.count >= TTY_FLIPBUF_SIZE) {
+				tty_flip_buffer_push(tty);
+			}
+			/* this doesn't actually push the data through unless 
+			 * tty->low_latency is set */
+			tty_insert_flip_char(tty, ((__u8*) data)[i], 0);
+		}
+		tty_flip_buffer_push(tty);
+		priv->bytes_in += bytes_sent;
+	}
+	/* Continue trying to always read  */
+	FILL_BULK_URB(port->read_urb, serial->dev, 
+		      usb_rcvbulkpipe(serial->dev,
+				      port->bulk_in_endpointAddress),
+		      port->read_urb->transfer_buffer,
+		      port->read_urb->transfer_buffer_length,
+		      klsi_105_read_bulk_callback,
+		      port);
+	rc = usb_submit_urb(port->read_urb);
+	if (rc)
+		err(__FUNCTION__ 
+		    " - failed resubmitting read urb, error %d", rc);
+} /* klsi_105_read_bulk_callback */
+
+
+static void klsi_105_set_termios (struct usb_serial_port *port,
+				  struct termios *old_termios)
+{
+	struct usb_serial *serial = port->serial;
+	struct klsi_105_private *priv = (struct klsi_105_private *)port->private;
+	unsigned int iflag = port->tty->termios->c_iflag;
+	unsigned int old_iflag = old_termios->c_iflag;
+	unsigned int cflag = port->tty->termios->c_cflag;
+	unsigned int old_cflag = old_termios->c_cflag;
+	
+	/*
+	 * Update baud rate
+	 */
+	if( (cflag & CBAUD) != (old_cflag & CBAUD) ) {
+	        /* reassert DTR and (maybe) RTS on transition from B0 */
+		if( (old_cflag & CBAUD) == B0 ) {
+			dbg(__FUNCTION__ ": baud was B0");
+#if 0
+			priv->control_state |= TIOCM_DTR;
+			/* don't set RTS if using hardware flow control */
+			if (!(old_cflag & CRTSCTS)) {
+				priv->control_state |= TIOCM_RTS;
+			}
+			mct_u232_set_modem_ctrl(serial, priv->control_state);
+#endif
+		}
+		
+		switch(cflag & CBAUD) {
+		case B0: /* handled below */
+			break;
+		case B1200: priv->cfg.baudrate = kl5kusb105a_sio_b1200;
+			break;
+		case B2400: priv->cfg.baudrate = kl5kusb105a_sio_b2400;
+			break;
+		case B4800: priv->cfg.baudrate = kl5kusb105a_sio_b4800;
+			break;
+		case B9600: priv->cfg.baudrate = kl5kusb105a_sio_b9600;
+			break;
+		case B19200: priv->cfg.baudrate = kl5kusb105a_sio_b19200;
+			break;
+		case B38400: priv->cfg.baudrate = kl5kusb105a_sio_b38400;
+			break;
+		case B57600: priv->cfg.baudrate = kl5kusb105a_sio_b57600;
+			break;
+		case B115200: priv->cfg.baudrate = kl5kusb105a_sio_b115200;
+			break;
+		default:
+			err("KLSI USB->Serial converter:"
+			    " unsupported baudrate request, using default"
+			    " of 9600");
+			priv->cfg.baudrate = kl5kusb105a_sio_b9600;
+			break;
+		}
+		if ((cflag & CBAUD) == B0 ) {
+			dbg(__FUNCTION__ ": baud is B0");
+			/* Drop RTS and DTR */
+			/* maybe this should be simulated by sending read
+			 * disable and read enable messages?
+			 */
+			;
+#if 0
+			priv->control_state &= ~(TIOCM_DTR | TIOCM_RTS);
+        		mct_u232_set_modem_ctrl(serial, priv->control_state);
+#endif
+		}
+	}
+
+	if ((cflag & CSIZE) != (old_cflag & CSIZE)) {
+		/* set the number of data bits */
+		switch (cflag & CSIZE) {
+		case CS5:
+			dbg(__FUNCTION__ " - 5 bits/byte not supported");
+			return ;
+		case CS6:
+			dbg(__FUNCTION__ " - 6 bits/byte not supported");
+			return ;
+		case CS7:
+			priv->cfg.databits = kl5kusb105a_dtb_7;
+			break;
+		case CS8:
+			priv->cfg.databits = kl5kusb105a_dtb_8;
+			break;
+		default:
+			err("CSIZE was not CS5-CS8, using default of 8");
+			priv->cfg.databits = kl5kusb105a_dtb_8;
+			break;
+		}
+	}
+
+	/*
+	 * Update line control register (LCR)
+	 */
+	if ((cflag & (PARENB|PARODD)) != (old_cflag & (PARENB|PARODD))
+	    || (cflag & CSTOPB) != (old_cflag & CSTOPB) ) {
+		
+#if 0
+		priv->last_lcr = 0;
+
+		/* set the parity */
+		if (cflag & PARENB)
+			priv->last_lcr |= (cflag & PARODD) ?
+				MCT_U232_PARITY_ODD : MCT_U232_PARITY_EVEN;
+		else
+			priv->last_lcr |= MCT_U232_PARITY_NONE;
+
+		/* set the number of stop bits */
+		priv->last_lcr |= (cflag & CSTOPB) ?
+			MCT_U232_STOP_BITS_2 : MCT_U232_STOP_BITS_1;
+
+		mct_u232_set_line_ctrl(serial, priv->last_lcr);
+#endif
+		;
+	}
+	
+	/*
+	 * Set flow control: well, I do not really now how to handle DTR/RTS.
+	 * Just do what we have seen with SniffUSB on Win98.
+	 */
+	if( (iflag & IXOFF) != (old_iflag & IXOFF)
+	    || (iflag & IXON) != (old_iflag & IXON)
+	    ||  (cflag & CRTSCTS) != (old_cflag & CRTSCTS) ) {
+		
+		/* Drop DTR/RTS if no flow control otherwise assert */
+#if 0
+		if ((iflag & IXOFF) || (iflag & IXON) || (cflag & CRTSCTS) )
+			priv->control_state |= TIOCM_DTR | TIOCM_RTS;
+		else
+			priv->control_state &= ~(TIOCM_DTR | TIOCM_RTS);
+		mct_u232_set_modem_ctrl(serial, priv->control_state);
+#endif
+		;
+	}
+
+	/* now commit changes to device */
+	klsi_105_chg_port_settings(serial, &(priv->cfg));
+} /* klsi_105_set_termios */
+
+
+#if 0
+static void mct_u232_break_ctl( struct usb_serial_port *port, int break_state )
+{
+	struct usb_serial *serial = port->serial;
+	struct mct_u232_private *priv = (struct mct_u232_private *)port->private;
+	unsigned char lcr = priv->last_lcr;
+
+	dbg (__FUNCTION__ "state=%d", break_state);
+
+	if (break_state)
+		lcr |= MCT_U232_SET_BREAK;
+
+	mct_u232_set_line_ctrl(serial, lcr);
+} /* mct_u232_break_ctl */
+#endif
+
+static int klsi_105_ioctl (struct usb_serial_port *port, struct file * file,
+			   unsigned int cmd, unsigned long arg)
+{
+	struct usb_serial *serial = port->serial;
+	struct klsi_105_private *priv = (struct klsi_105_private *)port->private;
+	int mask;
+	
+	dbg (__FUNCTION__ "cmd=0x%x", cmd);
+
+	/* Based on code from acm.c and others */
+	switch (cmd) {
+	case TIOCMGET: {
+		int rc;
+		unsigned long line_state;
+		dbg (__FUNCTION__ " - TIOCMGET request, just guessing");
+
+		rc = klsi_105_get_line_state(serial, &line_state);
+		if (rc < 0) {
+			err("Reading line control failed (error = %d)", rc);
+			/* better return value? EAGAIN? */
+			return -ENOIOCTLCMD;
+		} else {
+			priv->line_state = line_state;
+			dbg(__FUNCTION__ " - read line state 0x%lx", line_state);
+		}
+		return put_user(priv->line_state, (unsigned long *) arg); 
+	       };
+
+	case TIOCMSET: /* Turns on and off the lines as specified by the mask */
+	case TIOCMBIS: /* turns on (Sets) the lines as specified by the mask */
+	case TIOCMBIC: /* turns off (Clears) the lines as specified by the mask */
+		if (get_user(mask, (unsigned long *) arg))
+			return -EFAULT;
+
+		if ((cmd == TIOCMSET) || (mask & TIOCM_RTS)) {
+			/* RTS needs set */
+			if( ((cmd == TIOCMSET) && (mask & TIOCM_RTS)) ||
+			    (cmd == TIOCMBIS) )
+				dbg (__FUNCTION__ " - set RTS not handled");
+				/* priv->control_state |=  TIOCM_RTS; */
+			else
+				dbg (__FUNCTION__ " - clear RTS not handled");
+				/* priv->control_state &= ~TIOCM_RTS; */
+		}
+
+		if ((cmd == TIOCMSET) || (mask & TIOCM_DTR)) {
+			/* DTR needs set */
+			if( ((cmd == TIOCMSET) && (mask & TIOCM_DTR)) ||
+			    (cmd == TIOCMBIS) )
+				dbg (__FUNCTION__ " - set DTR not handled");
+			/*	priv->control_state |=  TIOCM_DTR; */
+			else
+				dbg (__FUNCTION__ " - clear DTR not handled");
+				/* priv->control_state &= ~TIOCM_DTR; */
+		}
+		/*
+		mct_u232_set_modem_ctrl(serial, priv->control_state);
+		*/
+		break;
+					
+	case TIOCMIWAIT:
+		/* wait for any of the 4 modem inputs (DCD,RI,DSR,CTS)*/
+		/* TODO */
+		dbg (__FUNCTION__ " - TIOCMIWAIT not handled");
+		return -ENOIOCTLCMD;
+
+	case TIOCGICOUNT:
+		/* return count of modemline transitions */
+		/* TODO */
+		dbg (__FUNCTION__ " - TIOCGICOUNT not handled");
+		return -ENOIOCTLCMD;
+	case TCGETS: {
+	     /* return current info to caller */
+	     int retval;
+
+	     dbg (__FUNCTION__ " - TCGETS data faked/incomplete");
+
+	     retval = verify_area(VERIFY_WRITE, (void *)arg,
+				  sizeof(struct termios));
+
+	     if (retval)
+			 return(retval);
+
+	     kernel_termios_to_user_termios((struct termios *)arg,  
+					    &priv->termios);
+	     return(0);
+	     }
+	case TCSETS: {
+		/* set port termios to the one given by the user */
+		int retval;
+
+		dbg (__FUNCTION__ " - TCSETS not handled");
+
+		retval = verify_area(VERIFY_READ, (void *)arg,
+				     sizeof(struct termios));
+
+		if (retval)
+			    return(retval);
+
+		user_termios_to_kernel_termios(&priv->termios,
+					       (struct termios *)arg);
+		klsi_105_set_termios(port, &priv->termios);
+		return(0);
+	     }
+	case TCSETSW: {
+		/* set port termios and try to wait for completion of last
+		 * write operation */
+		/* We guess here. If there are not too many write urbs
+		 * outstanding, we lie. */
+		/* what is the right way to wait here? schedule() ? */
+	        /*
+		while (klsi_105_chars_in_buffer(port) > (NUM_URBS / 4 ) * URB_TRANSFER_BUFFER_SIZE)
+			    schedule();
+		 */
+		return -ENOIOCTLCMD;
+		      }
+	default:
+		dbg(__FUNCTION__ ": arg not supported - 0x%04x",cmd);
+		return(-ENOIOCTLCMD);
+		break;
+	}
+	return 0;
+} /* klsi_105_ioctl */
+
+static void klsi_105_throttle (struct usb_serial_port *port)
+{
+
+	dbg(__FUNCTION__ " - port %d", port->number);
+
+	down (&port->sem);
+
+	usb_unlink_urb (port->read_urb);
+
+	up (&port->sem);
+
+	return;
+}
+static void klsi_105_unthrottle (struct usb_serial_port *port)
+{
+	int result;
+
+	dbg(__FUNCTION__ " - port %d", port->number);
+
+	down (&port->sem);
+
+	port->read_urb->dev = port->serial->dev;
+	result = usb_submit_urb(port->read_urb);
+	if (result)
+		err(__FUNCTION__ " - failed submitting read urb, error %d",
+		    result);
+
+	up (&port->sem);
+
+	return;
+}
+
+
+
+static int __init klsi_105_init (void)
+{
+	usb_serial_register (&palmconnect_device);
+	usb_serial_register (&kl5kusb105d_device);
+
+	info(DRIVER_DESC " " DRIVER_VERSION);
+	return 0;
+}
+
+
+static void __exit klsi_105_exit (void)
+{
+	usb_serial_deregister (&palmconnect_device);
+	usb_serial_deregister (&kl5kusb105d_device);
+}
+
+
+module_init (klsi_105_init);
+module_exit (klsi_105_exit);
+
+MODULE_AUTHOR( DRIVER_AUTHOR );
+MODULE_DESCRIPTION( DRIVER_DESC );
+MODULE_LICENSE("GPL"); 
+
+
+MODULE_PARM(debug, "i");
+MODULE_PARM_DESC(debug, "enable extensive debugging messages");
+/* FIXME: implement
+MODULE_PARM(num_urbs, "i");
+MODULE_PARM_DESC(num_urbs, "number of URBs to use in write pool");
+*/
+
+/* vim: set sts=8 ts=8 sw=8: */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/usb/serial/kl5kusb105.h linux/drivers/usb/serial/kl5kusb105.h
--- linux.orig/drivers/usb/serial/kl5kusb105.h	Thu Jan  1 00:00:00 1970
+++ linux/drivers/usb/serial/kl5kusb105.h	Wed Dec 26 15:09:47 2001
@@ -0,0 +1,69 @@
+/*
+ * Definitions for the KLSI KL5KUSB105 serial port adapter
+ */
+
+/* vendor/product pairs that are known to contain this chipset */
+#define PALMCONNECT_VID		0x0830
+#define PALMCONNECT_PID		0x0080
+
+#define KLSI_VID		0x05e9
+#define KLSI_KL5KUSB105D_PID	0x00c0
+
+/* Vendor commands: */
+
+
+/* port table -- the chip supports up to 4 channels */
+
+/* baud rates */
+
+typedef enum {
+  kl5kusb105a_sio_b115200 = 0,
+  kl5kusb105a_sio_b57600  = 1,
+  kl5kusb105a_sio_b38400  = 2,
+  kl5kusb105a_sio_b19200  = 4,
+  kl5kusb105a_sio_b14400  = 5,
+  kl5kusb105a_sio_b9600   = 6,
+  kl5kusb105a_sio_b4800   = 8,   /* unchecked */
+  kl5kusb105a_sio_b2400   = 9,   /* unchecked */
+  kl5kusb105a_sio_b1200   = 0xa,  /* unchecked */
+  kl5kusb105a_sio_b600    = 0xb   /* unchecked */
+} KL5KUSB105A_SIO_baudrate_t;
+
+/* data bits */
+#define kl5kusb105a_dtb_7   7
+#define kl5kusb105a_dtb_8   8
+
+
+
+/* requests: */
+#define KL5KUSB105A_SIO_SET_DATA  1
+#define KL5KUSB105A_SIO_POLL      2
+#define KL5KUSB105A_SIO_CONFIGURE      3
+/* values used for request KL5KUSB105A_SIO_CONFIGURE */
+#define KL5KUSB105A_SIO_CONFIGURE_READ_ON      3
+#define KL5KUSB105A_SIO_CONFIGURE_READ_OFF     2
+
+/* Interpretation of modem status lines */
+/* These need sorting out by individually connecting pins and checking
+ * results. FIXME!
+ * When data is being sent we see 0x30 in the lower byte; this must
+ * contain DSR and CTS ...
+ */
+#define KL5KUSB105A_DSR			((1<<4) | (1<<5))
+#define KL5KUSB105A_CTS			((1<<5) | (1<<4))
+
+#define KL5KUSB105A_WANTS_TO_SEND	0x30
+//#define KL5KUSB105A_DTR			/* Data Terminal Ready */
+//#define KL5KUSB105A_CTS			/* Clear To Send */
+//#define KL5KUSB105A_CD			/* Carrier Detect */
+//#define KL5KUSB105A_DSR			/* Data Set Ready */
+//#define KL5KUSB105A_RxD			/* Receive pin */
+
+//#define KL5KUSB105A_LE
+//#define KL5KUSB105A_RTS		
+//#define KL5KUSB105A_ST		
+//#define KL5KUSB105A_SR		
+//#define KL5KUSB105A_RI			/* Ring Indicator */
+
+/* vim: set ts=8 sts=8: */
+
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/usb/serial/visor.c linux/drivers/usb/serial/visor.c
--- linux.orig/drivers/usb/serial/visor.c	Mon Jan  7 23:41:09 2002
+++ linux/drivers/usb/serial/visor.c	Wed Dec 26 14:28:36 2001
@@ -12,6 +12,10 @@
  *
  * See Documentation/usb/usb-serial.txt for more information on using this driver
  * 
+ * (12/18/2001) gkh
+ *	Added better Clie support for 3.5 devices.  Thanks to Geoffrey Levand
+ *	for the patch.
+ *
  * (11/11/2001) gkh
  *	Added support for the m125 devices, and added check to prevent oopses
  *	for Clié devices that lie about the number of ports they have.
@@ -145,6 +149,7 @@
 static void visor_set_termios	(struct usb_serial_port *port, struct termios *old_termios);
 static void visor_write_bulk_callback	(struct urb *urb);
 static void visor_read_bulk_callback	(struct urb *urb);
+static int  clie_3_5_startup	(struct usb_serial *serial);
 
 
 static __devinitdata struct usb_device_id visor_id_table [] = {
@@ -251,6 +256,7 @@
 	close:			visor_close,
 	throttle:		visor_throttle,
 	unthrottle:		visor_unthrottle,
+	startup:		clie_3_5_startup,
 	ioctl:			visor_ioctl,
 	set_termios:		visor_set_termios,
 	write:			visor_write,
@@ -705,6 +711,46 @@
 	return 0;
 }
 
+static int clie_3_5_startup (struct usb_serial *serial)
+{
+	int result;
+	u8 data;
+
+	dbg(__FUNCTION__);
+
+	/*
+	 * Note that PEG-300 series devices expect the following two calls.
+	 */
+
+	/* get the config number */
+	result = usb_control_msg (serial->dev, usb_rcvctrlpipe(serial->dev, 0),
+				  USB_REQ_GET_CONFIGURATION, USB_DIR_IN,
+				  0, 0, &data, 1, HZ * 3);
+	if (result < 0) {
+		err(__FUNCTION__ ": get config number failed: %d", result);
+		return result;
+	}
+	if (result != 1) {
+		err(__FUNCTION__ ": get config number bad return length: %d", result);
+		return -EIO;
+	}
+
+	/* get the interface number */
+	result = usb_control_msg (serial->dev, usb_rcvctrlpipe(serial->dev, 0),
+				  USB_REQ_GET_INTERFACE, 
+				  USB_DIR_IN | USB_DT_DEVICE,
+				  0, 0, &data, 1, HZ * 3);
+	if (result < 0) {
+		err(__FUNCTION__ ": get interface number failed: %d", result);
+		return result;
+	}
+	if (result != 1) {
+		err(__FUNCTION__ ": get interface number bad return length: %d", result);
+		return -EIO;
+	}
+
+	return 0;
+}
 
 static void visor_shutdown (struct usb_serial *serial)
 {
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/usb/stv680.c linux/drivers/usb/stv680.c
--- linux.orig/drivers/usb/stv680.c	Thu Jan  1 00:00:00 1970
+++ linux/drivers/usb/stv680.c	Wed Dec 26 15:09:47 2001
@@ -0,0 +1,1633 @@
+/*
+ *  STV0680 USB Camera Driver, by Kevin Sisson (kjsisson@bellsouth.net)
+ *  
+ * Thanks to STMicroelectronics for information on the usb commands, and 
+ * to Steve Miller at STM for his help and encouragement while I was 
+ * writing this driver.
+ *
+ * This driver is based heavily on the 
+ * Endpoints (formerly known as AOX) se401 USB Camera Driver
+ * Copyright (c) 2000 Jeroen B. Vreeken (pe1rxq@amsat.org)
+ *
+ * Still somewhat based on the Linux ov511 driver.
+ * 
+ * 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.
+ *
+ * History: 
+ * ver 0.1 October, 2001. Initial attempt. 
+ *
+ * ver 0.2 November, 2001. Fixed asbility to resize, added brightness
+ *                         function, made more stable (?)
+ *
+ * ver 0.21 Nov, 2001.     Added gamma correction and white balance, 
+ *                         due to Alexander Schwartz. Still trying to 
+ *                         improve stablility. Moved stuff into stv680.h
+ *
+ * ver 0.22 Nov, 2001.	   Added sharpen function (by Michael Sweet, 
+ *                         mike@easysw.com) from GIMP, also used in pencam. 
+ *                         Simple, fast, good integer math routine.
+ *
+ * ver 0.23 Dec, 2001 (gkh)
+ * 			   Took out sharpen function, ran code through
+ * 			   Lindent, and did other minor tweaks to get
+ * 			   things to work properly with 2.5.1
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+#include <linux/proc_fs.h>
+#include <linux/pagemap.h>
+#include <linux/wrapper.h>
+#include <linux/smp_lock.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/videodev.h>
+#include <linux/usb.h>
+
+#include "stv680.h"
+
+static int video_nr = -1;
+static int swapRGB = 0;
+
+static unsigned int debug = 0;
+
+#define PDEBUG(level, fmt, args...) \
+	do { \
+	if (debug >= level)	\
+		info("[" __PRETTY_FUNCTION__ ":%d] " fmt, __LINE__ , ## args);	\
+	} while (0)
+
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v0.23"
+#define DRIVER_AUTHOR "Kevin Sisson <kjsisson@bellsouth.net>"
+#define DRIVER_DESC "STV0680 USB Camera Driver"
+
+MODULE_AUTHOR (DRIVER_AUTHOR);
+MODULE_DESCRIPTION (DRIVER_DESC);
+MODULE_LICENSE ("GPL");
+MODULE_PARM (debug, "i");
+MODULE_PARM_DESC (debug, "Debug enabled or not");
+MODULE_PARM (swapRGB, "i");
+MODULE_PARM_DESC (swapRGB, "Swap red and blue, e.g., for xawtv");
+MODULE_PARM (video_nr, "i");
+EXPORT_NO_SYMBOLS;
+
+/********************************************************************
+ *
+ * Memory management
+ *
+ * This is a shameless copy from the USB-cpia driver (linux kernel
+ * version 2.3.29 or so, I have no idea what this code actually does ;).
+ * Actually it seems to be a copy of a shameless copy of the bttv-driver.
+ * Or that is a copy of a shameless copy of ... (To the powers: is there
+ * no generic kernel-function to do this sort of stuff?)
+ *
+ * Yes, it was a shameless copy from the bttv-driver. IIRC, Alan says
+ * there will be one, but apparentely not yet -jerdfelt
+ *
+ * So I copied it again for the ov511 driver -claudio
+ *
+ * Same for the se401 driver -Jeroen
+ *
+ * And the STV0680 driver - Kevin
+ ********************************************************************/
+
+/* Given PGD from the address space's page table, return the kernel
+ * virtual mapping of the physical memory mapped at ADR.
+ */
+static inline unsigned long uvirt_to_kva (pgd_t * pgd, unsigned long adr)
+{
+	unsigned long ret = 0UL;
+	pmd_t *pmd;
+	pte_t *ptep, pte;
+
+	if (!pgd_none (*pgd)) {
+		pmd = pmd_offset (pgd, adr);
+		if (!pmd_none (*pmd)) {
+			ptep = pte_offset (pmd, adr);
+			pte = *ptep;
+			if (pte_present (pte)) {
+				ret = (unsigned long) page_address (pte_page (pte));
+				ret |= (adr & (PAGE_SIZE - 1));
+			}
+		}
+	}
+	return ret;
+}
+
+/* Here we want the physical address of the memory. This is used when 
+ * initializing the contents of the area and marking the pages as reserved.
+ */
+static inline unsigned long kvirt_to_pa (unsigned long adr)
+{
+	unsigned long va, kva, ret;
+
+	va = VMALLOC_VMADDR (adr);
+	kva = uvirt_to_kva (pgd_offset_k (va), va);
+	ret = __pa (kva);
+	return ret;
+}
+
+static void *rvmalloc (unsigned long size)
+{
+	void *mem;
+	unsigned long adr, page;
+
+	/* Round it off to PAGE_SIZE */
+	size += (PAGE_SIZE - 1);
+	size &= ~(PAGE_SIZE - 1);
+
+	mem = vmalloc_32 (size);
+	if (!mem)
+		return NULL;
+
+	memset (mem, 0, size);	/* Clear the ram out, no junk to the user */
+	adr = (unsigned long) mem;
+	while (size > 0) {
+		page = kvirt_to_pa (adr);
+		mem_map_reserve (virt_to_page (__va (page)));
+		adr += PAGE_SIZE;
+		if (size > PAGE_SIZE)
+			size -= PAGE_SIZE;
+		else
+			size = 0;
+	}
+	return mem;
+}
+
+static void rvfree (void *mem, unsigned long size)
+{
+	unsigned long adr, page;
+
+	if (!mem)
+		return;
+
+	size += (PAGE_SIZE - 1);
+	size &= ~(PAGE_SIZE - 1);
+
+	adr = (unsigned long) mem;
+	while (size > 0) {
+		page = kvirt_to_pa (adr);
+		mem_map_unreserve (virt_to_page (__va (page)));
+		adr += PAGE_SIZE;
+		if (size > PAGE_SIZE)
+			size -= PAGE_SIZE;
+		else
+			size = 0;
+	}
+	vfree (mem);
+}
+
+
+/*********************************************************************
+ * pencam read/write functions
+ ********************************************************************/
+
+static int stv_sndctrl (int set, struct usb_stv *stv680, unsigned short req, unsigned short value, unsigned char *buffer, int size)
+{
+	int ret = -1;
+
+	switch (set) {
+	case 0:		/*  0xc1  */
+		ret = usb_control_msg (stv680->udev,
+				       usb_rcvctrlpipe (stv680->udev, 0),
+				       req,
+				       (USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT),
+				       value, 0, buffer, size, PENCAM_TIMEOUT);
+		break;
+
+	case 1:		/*  0x41  */
+		ret = usb_control_msg (stv680->udev,
+				       usb_sndctrlpipe (stv680->udev, 0),
+				       req,
+				       (USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT),
+				       value, 0, buffer, size, PENCAM_TIMEOUT);
+		break;
+
+	case 2:		/*  0x80  */
+		ret = usb_control_msg (stv680->udev,
+				       usb_rcvctrlpipe (stv680->udev, 0),
+				       req,
+				       (USB_DIR_IN | USB_RECIP_DEVICE),
+				       value, 0, buffer, size, PENCAM_TIMEOUT);
+		break;
+
+	case 3:		/*  0x40  */
+		ret = usb_control_msg (stv680->udev,
+				       usb_sndctrlpipe (stv680->udev, 0),
+				       req,
+				       (USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE),
+				       value, 0, buffer, size, PENCAM_TIMEOUT);
+		break;
+
+	}
+	if ((ret < 0) && (req != 0x0a)) {
+		PDEBUG (1, "STV(e): usb_control_msg error %i, request = 0x%x, error = %i", set, req, ret);
+	}
+	return ret;
+}
+
+static int stv_set_config (struct usb_stv *dev, int configuration, int interface, int alternate)
+{
+
+	if (usb_set_configuration (dev->udev, configuration) < 0) {
+		PDEBUG (1, "STV(e): FAILED to set configuration %i", configuration);
+		return -1;
+	}
+	if (usb_set_interface (dev->udev, interface, alternate) < 0) {
+		PDEBUG (1, "STV(e): FAILED to set alternate interface %i", alternate);
+		return -1;
+	}
+	return 0;
+}
+
+static int stv_stop_video (struct usb_stv *dev)
+{
+	int i;
+	unsigned char *buf;
+
+	buf = kmalloc (40, GFP_KERNEL);
+	if (buf == NULL) {
+		PDEBUG (0, "STV(e): Out of (small buf) memory");
+		return -1;
+	}
+
+	/* this is a high priority command; it stops all lower order commands */
+	if ((i = stv_sndctrl (1, dev, 0x04, 0x0000, buf, 0x0)) < 0) {
+		i = stv_sndctrl (0, dev, 0x80, 0, buf, 0x02);	/* Get Last Error; 2 = busy */
+		PDEBUG (1, "STV(i): last error: %i,  command = 0x%x", buf[0], buf[1]);
+	} else {
+		PDEBUG (1, "STV(i): Camera reset to idle mode.");
+	}
+
+	if ((i = stv_set_config (dev, 1, 0, 0)) < 0)
+		PDEBUG (1, "STV(e): Reset config during exit failed");
+
+	/*  get current mode  */
+	buf[0] = 0xf0;
+	if ((i = stv_sndctrl (0, dev, 0x87, 0, buf, 0x08)) != 0x08)	/* get mode */
+		PDEBUG (0, "STV(e): Stop_video: problem setting original mode");
+	if (dev->origMode != buf[0]) {
+		memset (buf, 0, 8);
+		buf[0] = (unsigned char) dev->origMode;
+		if ((i = stv_sndctrl (3, dev, 0x07, 0x0100, buf, 0x08)) != 0x08) {
+			PDEBUG (0, "STV(e): Stop_video: Set_Camera_Mode failed");
+			i = -1;
+		}
+		buf[0] = 0xf0;
+		i = stv_sndctrl (0, dev, 0x87, 0, buf, 0x08);
+		if ((i != 0x08) || (buf[0] != dev->origMode)) {
+			PDEBUG (0, "STV(e): camera NOT set to original resolution.");
+			i = -1;
+		} else
+			PDEBUG (0, "STV(i): Camera set to original resolution");
+	}
+	/* origMode */
+	kfree (buf);
+	return i;
+}
+
+static int stv_set_video_mode (struct usb_stv *dev)
+{
+	int i, stop_video = 1;
+	unsigned char *buf;
+
+	buf = kmalloc (40, GFP_KERNEL);
+	if (buf == NULL) {
+		PDEBUG (0, "STV(e): Out of (small buf) memory");
+		return -1;
+	}
+
+	if ((i = stv_set_config (dev, 1, 0, 0)) < 0) {
+		kfree (buf);
+		return i;
+	}
+
+	i = stv_sndctrl (2, dev, 0x06, 0x0100, buf, 0x12);
+	if (!(i > 0) && (buf[8] == 0x53) && (buf[9] == 0x05)) {
+		PDEBUG (1, "STV(e): Could not get descriptor 0100.");
+		goto error;
+	}
+
+	/*  set alternate interface 1 */
+	if ((i = stv_set_config (dev, 1, 0, 1)) < 0)
+		goto error;
+
+	if ((i = stv_sndctrl (0, dev, 0x85, 0, buf, 0x10)) != 0x10)
+		goto error;
+	PDEBUG (1, "STV(i): Setting video mode.");
+	/*  Switch to Video mode: 0x0100 = VGA (640x480), 0x0000 = CIF (352x288) 0x0300 = QVGA (320x240)  */
+	if ((i = stv_sndctrl (1, dev, 0x09, dev->VideoMode, buf, 0x0)) < 0) {
+		stop_video = 0;
+		goto error;
+	}
+	goto exit;
+
+error:
+	kfree (buf);
+	if (stop_video == 1)
+		stv_stop_video (dev);
+	return -1;
+
+exit:
+	kfree (buf);
+	return 0;
+}
+
+static int stv_init (struct usb_stv *stv680)
+{
+	int i = 0;
+	unsigned char *buffer;
+	unsigned long int bufsize;
+
+	buffer = kmalloc (40, GFP_KERNEL);
+	if (buffer == NULL) {
+		PDEBUG (0, "STV(e): Out of (small buf) memory");
+		return -1;
+	}
+	memset (buffer, 0, 40);
+	udelay (100);
+
+	/* set config 1, interface 0, alternate 0 */
+	if ((i = stv_set_config (stv680, 1, 0, 0)) < 0) {
+		kfree (buffer);
+		PDEBUG (0, "STV(e): set config 1,0,0 failed");
+		return -1;
+	}
+	/* ping camera to be sure STV0680 is present */
+	if ((i = stv_sndctrl (0, stv680, 0x88, 0x5678, buffer, 0x02)) != 0x02)
+		goto error;
+	if ((buffer[0] != 0x56) || (buffer[1] != 0x78)) {
+		PDEBUG (1, "STV(e): camera ping failed!!");
+		goto error;
+	}
+
+	/* get camera descriptor */
+	if ((i = stv_sndctrl (2, stv680, 0x06, 0x0200, buffer, 0x09)) != 0x09)
+		goto error;
+	i = stv_sndctrl (2, stv680, 0x06, 0x0200, buffer, 0x22);
+	if (!(i >= 0) && (buffer[7] == 0xa0) && (buffer[8] == 0x23)) {
+		PDEBUG (1, "STV(e): Could not get descriptor 0200.");
+		goto error;
+	}
+	if ((i = stv_sndctrl (0, stv680, 0x8a, 0, buffer, 0x02)) != 0x02)
+		goto error;
+	if ((i = stv_sndctrl (0, stv680, 0x8b, 0, buffer, 0x24)) != 0x24)
+		goto error;
+	if ((i = stv_sndctrl (0, stv680, 0x85, 0, buffer, 0x10)) != 0x10)
+		goto error;
+
+	stv680->SupportedModes = buffer[7];
+	i = stv680->SupportedModes;
+	stv680->CIF = 0;
+	stv680->VGA = 0;
+	stv680->QVGA = 0;
+	if (i & 1)
+		stv680->CIF = 1;
+	if (i & 2)
+		stv680->VGA = 1;
+	if (i & 8)
+		stv680->QVGA = 1;
+	if (stv680->SupportedModes == 0) {
+		PDEBUG (0, "STV(e): There are NO supported STV680 modes!!");
+		i = -1;
+		goto error;
+	} else {
+		if (stv680->CIF)
+			PDEBUG (0, "STV(i): CIF is supported");
+		if (stv680->QVGA)
+			PDEBUG (0, "STV(i): QVGA is supported");
+	}
+	/* FW rev, ASIC rev, sensor ID  */
+	PDEBUG (1, "STV(i): Firmware rev is %i.%i", buffer[0], buffer[1]);
+	PDEBUG (1, "STV(i): ASIC rev is %i.%i", buffer[2], buffer[3]);
+	PDEBUG (1, "STV(i): Sensor ID is %i", (buffer[4]*16) + (buffer[5]>>4));
+
+	/*  set alternate interface 1 */
+	if ((i = stv_set_config (stv680, 1, 0, 1)) < 0)
+		goto error;
+
+	if ((i = stv_sndctrl (0, stv680, 0x85, 0, buffer, 0x10)) != 0x10)
+		goto error;
+	if ((i = stv_sndctrl (0, stv680, 0x8d, 0, buffer, 0x08)) != 0x08)
+		goto error;
+	i = buffer[3];
+	PDEBUG (0, "STV(i): Camera has %i pictures.", i);
+
+	/*  get current mode */
+	if ((i = stv_sndctrl (0, stv680, 0x87, 0, buffer, 0x08)) != 0x08)
+		goto error;
+	stv680->origMode = buffer[0];	/* 01 = VGA, 03 = QVGA, 00 = CIF */
+
+	/* This will attemp CIF mode, if supported. If not, set to QVGA  */
+	memset (buffer, 0, 8);
+	if (stv680->CIF)
+		buffer[0] = 0x00;
+	else if (stv680->QVGA)
+		buffer[0] = 0x03;
+	if ((i = stv_sndctrl (3, stv680, 0x07, 0x0100, buffer, 0x08)) != 0x08) {
+		PDEBUG (0, "STV(i): Set_Camera_Mode failed");
+		i = -1;
+		goto error;
+	}
+	buffer[0] = 0xf0;
+	stv_sndctrl (0, stv680, 0x87, 0, buffer, 0x08);
+	if (((stv680->CIF == 1) && (buffer[0] != 0x00)) || ((stv680->QVGA == 1) && (buffer[0] != 0x03))) {
+		PDEBUG (0, "STV(e): Error setting camera video mode!");
+		i = -1;
+		goto error;
+	} else {
+		if (buffer[0] == 0) {
+			stv680->VideoMode = 0x0000;
+			PDEBUG (0, "STV(i): Video Mode set to CIF");
+		}
+		if (buffer[0] == 0x03) {
+			stv680->VideoMode = 0x0300;
+			PDEBUG (0, "STV(i): Video Mode set to QVGA");
+		}
+	}
+	if ((i = stv_sndctrl (0, stv680, 0x8f, 0, buffer, 0x10)) != 0x10)
+		goto error;
+	bufsize = (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | (buffer[3]);
+	stv680->cwidth = (buffer[4] << 8) | (buffer[5]);	/* ->camera = 322, 356, 644  */
+	stv680->cheight = (buffer[6] << 8) | (buffer[7]);	/* ->camera = 242, 292, 484  */
+	stv680->origGain = buffer[12];
+
+	goto exit;
+
+error:
+	i = stv_sndctrl (0, stv680, 0x80, 0, buffer, 0x02);	/* Get Last Error */
+	PDEBUG (1, "STV(i): last error: %i,  command = 0x%x", buffer[0], buffer[1]);
+	kfree (buffer);
+	return -1;
+
+exit:
+	kfree (buffer);
+
+	/* video = 320x240, 352x288 */
+	if (stv680->CIF == 1) {
+		stv680->maxwidth = 352;
+		stv680->maxheight = 288;
+		stv680->vwidth = 352;
+		stv680->vheight = 288;
+	}
+	if (stv680->QVGA == 1) {
+		stv680->maxwidth = 320;
+		stv680->maxheight = 240;
+		stv680->vwidth = 320;
+		stv680->vheight = 240;
+	}
+
+	stv680->rawbufsize = bufsize;	/* must be ./. by 8 */
+	stv680->maxframesize = bufsize * 3;	/* RGB size */
+	PDEBUG (2, "STV(i): cwidth = %i, cheight = %i", stv680->cwidth, stv680->cheight);
+	PDEBUG (1, "STV(i): width = %i, height = %i, rawbufsize = %li", stv680->vwidth, stv680->vheight, stv680->rawbufsize);
+
+	/* some default values */
+	stv680->bulk_in_endpointAddr = 0x82;
+	stv680->dropped = 0;
+	stv680->error = 0;
+	stv680->framecount = 0;
+	stv680->readcount = 0;
+	stv680->streaming = 0;
+	/* bright, white, colour, hue, contrast are set by software, not in stv0680 */
+	stv680->brightness = 32767;
+	stv680->chgbright = 0;
+	stv680->whiteness = 0;	/* only for greyscale */
+	stv680->colour = 32767;
+	stv680->contrast = 32767;
+	stv680->hue = 32767;
+	stv680->palette = STV_VIDEO_PALETTE;
+	stv680->depth = 24;	/* rgb24 bits */
+	swapRGB = 0;
+	PDEBUG (1, "STV(i): swapRGB is OFF");
+
+	if (stv_set_video_mode (stv680) < 0) {
+		PDEBUG (0, "STV(e): Could not set video mode in stv_init");
+		return -1;
+	}
+
+	return 0;
+}
+
+/***************** last of pencam  routines  *******************/
+
+/********************************************************************
+ * /proc interface
+ *******************************************************************/
+
+#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
+
+static struct proc_dir_entry *stv680_proc_entry = NULL;
+extern struct proc_dir_entry *video_proc_entry;
+
+#define YES_NO(x) ((x) ? "yes" : "no")
+
+static int stv680_read_proc (char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+	char *out = page;
+	int len;
+	struct usb_stv *stv680 = data;
+
+	/* Stay under PAGE_SIZE or else bla bla bla.... */
+
+	out += sprintf (out, "driver_version  : %s\n", DRIVER_VERSION);
+	out += sprintf (out, "model           : %s\n", stv680->camera_name);
+	out += sprintf (out, "in use          : %s\n", YES_NO (stv680->user));
+	out += sprintf (out, "streaming       : %s\n", YES_NO (stv680->streaming));
+	out += sprintf (out, "num_frames      : %d\n", STV680_NUMFRAMES);
+
+	out += sprintf (out, "Current size    : %ix%i\n", stv680->vwidth, stv680->vheight);
+	out += sprintf (out, "swapRGB         : %s\n", YES_NO (swapRGB));
+	out += sprintf (out, "Palette         : %i", stv680->palette);
+
+	out += sprintf (out, "\n");
+
+	out += sprintf (out, "Frames total    : %d\n", stv680->readcount);
+	out += sprintf (out, "Frames read     : %d\n", stv680->framecount);
+	out += sprintf (out, "Packets dropped : %d\n", stv680->dropped);
+	out += sprintf (out, "Decoding Errors : %d\n", stv680->error);
+
+	len = out - page;
+	len -= off;
+	if (len < count) {
+		*eof = 1;
+		if (len <= 0)
+			return 0;
+	} else
+		len = count;
+
+	*start = page + off;
+	return len;
+}
+
+static int create_proc_stv680_cam (struct usb_stv *stv680)
+{
+	char name[9];
+	struct proc_dir_entry *ent;
+
+	if (!stv680_proc_entry || !stv680)
+		return -1;
+
+	sprintf (name, "video%d", stv680->vdev.minor);
+
+	ent = create_proc_entry (name, S_IFREG | S_IRUGO | S_IWUSR, stv680_proc_entry);
+	if (!ent)
+		return -1;
+
+	ent->data = stv680;
+	ent->read_proc = stv680_read_proc;
+	stv680->proc_entry = ent;
+	return 0;
+}
+
+static void destroy_proc_stv680_cam (struct usb_stv *stv680)
+{
+	/* One to much, just to be sure :) */
+	char name[9];
+
+	if (!stv680 || !stv680->proc_entry)
+		return;
+
+	sprintf (name, "video%d", stv680->vdev.minor);
+	remove_proc_entry (name, stv680_proc_entry);
+	stv680->proc_entry = NULL;
+}
+
+static int proc_stv680_create (void)
+{
+	if (video_proc_entry == NULL) {
+		PDEBUG (0, "STV(e): /proc/video/ doesn't exist!");
+		return -1;
+	}
+	stv680_proc_entry = create_proc_entry ("stv680", S_IFDIR, video_proc_entry);
+
+	if (stv680_proc_entry) {
+		stv680_proc_entry->owner = THIS_MODULE;
+	} else {
+		PDEBUG (0, "STV(e): Unable to initialize /proc/video/stv680");
+		return -1;
+	}
+	return 0;
+}
+
+static void proc_stv680_destroy (void)
+{
+	if (stv680_proc_entry == NULL)
+		return;
+
+	remove_proc_entry ("stv", video_proc_entry);
+}
+#endif				/* CONFIG_PROC_FS && CONFIG_VIDEO_PROC_FS */
+
+/********************************************************************
+ * Camera control
+ *******************************************************************/
+
+static int stv680_get_pict (struct usb_stv *stv680, struct video_picture *p)
+{
+	/* This sets values for v4l interface. max/min = 65535/0  */
+
+	p->brightness = stv680->brightness;
+	p->whiteness = stv680->whiteness;	/* greyscale */
+	p->colour = stv680->colour;
+	p->contrast = stv680->contrast;
+	p->hue = stv680->hue;
+	p->palette = stv680->palette;
+	p->depth = stv680->depth;
+	return 0;
+}
+
+static int stv680_set_pict (struct usb_stv *stv680, struct video_picture *p)
+{
+	/* See above stv680_get_pict  */
+
+	if (p->palette != STV_VIDEO_PALETTE) {
+		PDEBUG (2, "STV(e): Palette set error in _set_pic");
+		return 1;
+	}
+
+	if (stv680->brightness != p->brightness) {
+		stv680->chgbright = 1;
+		stv680->brightness = p->brightness;
+	} else {
+		stv680->chgbright = 0;
+	}
+
+	stv680->whiteness = p->whiteness;	/* greyscale */
+	stv680->colour = p->colour;
+	stv680->contrast = p->contrast;
+	stv680->hue = p->hue;
+	stv680->palette = p->palette;
+	stv680->depth = p->depth;
+
+	return 0;
+}
+
+static void stv680_video_irq (struct urb *urb)
+{
+	struct usb_stv *stv680 = urb->context;
+	int length = urb->actual_length;
+
+	if (length < stv680->rawbufsize)
+		PDEBUG (2, "STV(i): Lost data in transfer: exp %li, got %i", stv680->rawbufsize, length);
+
+	/* ohoh... */
+	if (!stv680->streaming)
+		return;
+
+	if (!stv680->udev) {
+		PDEBUG (0, "STV(e): device vapourished in video_irq");
+		return;
+	}
+
+	/* 0 sized packets happen if we are to fast, but sometimes the camera
+	   keeps sending them forever...
+	 */
+	if (length && !urb->status) {
+		stv680->nullpackets = 0;
+		switch (stv680->scratch[stv680->scratch_next].state) {
+		case BUFFER_READY:
+		case BUFFER_BUSY:
+			stv680->dropped++;
+			break;
+
+		case BUFFER_UNUSED:
+			memcpy (stv680->scratch[stv680->scratch_next].data,
+			        (unsigned char *) urb->transfer_buffer, length);
+			stv680->scratch[stv680->scratch_next].state = BUFFER_READY;
+			stv680->scratch[stv680->scratch_next].length = length;
+			if (waitqueue_active (&stv680->wq)) {
+				wake_up_interruptible (&stv680->wq);
+			}
+			stv680->scratch_overflow = 0;
+			stv680->scratch_next++;
+			if (stv680->scratch_next >= STV680_NUMSCRATCH)
+				stv680->scratch_next = 0;;
+			break;
+		}		/* switch  */
+	} else {
+		stv680->nullpackets++;
+		if (stv680->nullpackets > STV680_MAX_NULLPACKETS) {
+			if (waitqueue_active (&stv680->wq)) {
+				wake_up_interruptible (&stv680->wq);
+			}
+		}
+	}			/*  if - else */
+
+	/* Resubmit urb for new data */
+	urb->status = 0;
+	urb->dev = stv680->udev;
+	if (usb_submit_urb (urb))
+		PDEBUG (0, "STV(e): urb burned down in video irq");
+	return;
+}				/*  _video_irq  */
+
+static int stv680_start_stream (struct usb_stv *stv680)
+{
+	urb_t *urb;
+	int err = 0, i;
+
+	stv680->streaming = 1;
+
+	/* Do some memory allocation */
+	for (i = 0; i < STV680_NUMFRAMES; i++) {
+		stv680->frame[i].data = stv680->fbuf + i * stv680->maxframesize;
+		stv680->frame[i].curpix = 0;
+	}
+	/* packet size = 4096  */
+	for (i = 0; i < STV680_NUMSBUF; i++) {
+		stv680->sbuf[i].data = kmalloc (stv680->rawbufsize, GFP_KERNEL);
+		if (stv680->sbuf[i].data == NULL) {
+			PDEBUG (0, "STV(e): Could not kmalloc raw data buffer %i", i);
+			return -1;
+		}
+	}
+
+	stv680->scratch_next = 0;
+	stv680->scratch_use = 0;
+	stv680->scratch_overflow = 0;
+	for (i = 0; i < STV680_NUMSCRATCH; i++) {
+		stv680->scratch[i].data = kmalloc (stv680->rawbufsize, GFP_KERNEL);
+		if (stv680->scratch[i].data == NULL) {
+			PDEBUG (0, "STV(e): Could not kmalloc raw scratch buffer %i", i);
+			return -1;
+		}
+		stv680->scratch[i].state = BUFFER_UNUSED;
+	}
+
+	for (i = 0; i < STV680_NUMSBUF; i++) {
+		urb = usb_alloc_urb (0);
+		if (!urb)
+			return ENOMEM;
+
+		/* sbuf is urb->transfer_buffer, later gets memcpyed to scratch */
+		usb_fill_bulk_urb (urb, stv680->udev,
+				   usb_rcvbulkpipe (stv680->udev, stv680->bulk_in_endpointAddr),
+				   stv680->sbuf[i].data, stv680->rawbufsize,
+				   stv680_video_irq, stv680);
+		urb->timeout = PENCAM_TIMEOUT * 2;
+		urb->transfer_flags |= USB_QUEUE_BULK;
+		stv680->urb[i] = urb;
+		err = usb_submit_urb (stv680->urb[i]);
+		if (err)
+			PDEBUG (0, "STV(e): urb burned down in start stream");
+	}			/* i STV680_NUMSBUF */
+
+	stv680->framecount = 0;
+	return 0;
+}
+
+static int stv680_stop_stream (struct usb_stv *stv680)
+{
+	int i;
+
+	if (!stv680->streaming || !stv680->udev)
+		return 1;
+
+	stv680->streaming = 0;
+
+	for (i = 0; i < STV680_NUMSBUF; i++)
+		if (stv680->urb[i]) {
+			stv680->urb[i]->next = NULL;
+			usb_unlink_urb (stv680->urb[i]);
+			usb_free_urb (stv680->urb[i]);
+			stv680->urb[i] = NULL;
+			kfree (stv680->sbuf[i].data);
+		}
+	for (i = 0; i < STV680_NUMSCRATCH; i++) {
+		kfree (stv680->scratch[i].data);
+		stv680->scratch[i].data = NULL;
+	}
+
+	return 0;
+}
+
+static int stv680_set_size (struct usb_stv *stv680, int width, int height)
+{
+	int wasstreaming = stv680->streaming;
+
+	/* Check to see if we need to change */
+	if ((stv680->vwidth == width) && (stv680->vheight == height))
+		return 0;
+
+	PDEBUG (1, "STV(i): size request for %i x %i", width, height);
+	/* Check for a valid mode */
+	if ((!width || !height) || ((width & 1) || (height & 1))) {
+		PDEBUG (1, "STV(e): set_size error: request: v.width = %i, v.height = %i  actual: stv.width = %i, stv.height = %i", width, height, stv680->vwidth, stv680->vheight);
+		return 1;
+	}
+
+	if ((width < (stv680->maxwidth / 2)) || (height < (stv680->maxheight / 2))) {
+		width = stv680->maxwidth / 2;
+		height = stv680->maxheight / 2;
+	} else if ((width >= 158) && (width <= 166)) {
+		width = 160;
+		height = 120;
+	} else if ((width >= 172) && (width <= 180)) {
+		width = 176;
+		height = 144;
+	} else if ((width >= 318) && (width <= 350)) {
+		width = 320;
+		height = 240;
+	} else if ((width >= 350) && (width <= 358)) {
+		width = 352;
+		height = 288;
+	}
+
+	/* Stop a current stream and start it again at the new size */
+	if (wasstreaming)
+		stv680_stop_stream (stv680);
+	stv680->vwidth = width;
+	stv680->vheight = height;
+	PDEBUG (1, "STV(i): size set to %i x %i", stv680->vwidth, stv680->vheight);
+	if (wasstreaming)
+		stv680_start_stream (stv680);
+
+	return 0;
+}
+
+/**********************************************************************
+ * Video Decoding
+ **********************************************************************/
+
+/*******  routines from the pencam program; hey, they work!  ********/
+
+/*
+ * STV0680 Vision Camera Chipset Driver
+ * Copyright (C) 2000 Adam Harrison <adam@antispin.org> 
+*/
+
+#define RED 0
+#define GREEN 1
+#define BLUE 2
+#define AD(x, y, w) (((y)*(w)+(x))*3)
+
+static void bayer_unshuffle (struct usb_stv *stv680, struct stv680_scratch *buffer)
+{
+	int x, y, i;
+	int w = stv680->cwidth;
+	int vw = stv680->cwidth, vh = stv680->cheight, vstep = 1;
+	unsigned int p = 0;
+	int colour = 0, bayer = 0;
+	unsigned char *raw = buffer->data;
+	struct stv680_frame *frame = &stv680->frame[stv680->curframe];
+	unsigned char *output = frame->data;
+	unsigned char *temp = frame->data;
+	int offset = buffer->offset;
+
+	if (frame->curpix == 0) {
+		if (frame->grabstate == FRAME_READY) {
+			frame->grabstate = FRAME_GRABBING;
+		}
+	}
+	if (offset != frame->curpix) {	/* Regard frame as lost :( */
+		frame->curpix = 0;
+		stv680->error++;
+		return;
+	}
+
+	if ((stv680->vwidth == 322) || (stv680->vwidth == 320)) {
+		vw = 320;
+		vh = 240;
+		vstep = 1;
+	}
+	if ((stv680->vwidth == 352)) {
+		vw = 352;
+		vh = 288;
+		vstep = 1;
+	}
+	if ((stv680->vwidth == 160)) {
+		vw = 160;
+		vh = 120;
+		vstep = 2;
+	}
+	if ((stv680->vwidth == 176)) {
+		vw = 176;
+		vh = 144;
+		vstep = 2;
+	}
+	memset (output, 0, 3 * vw * vh);	/* clear output matrix. Maybe not necessary. */
+
+	for (y = 0; y < vh; y++) {
+		for (x = 0; x < vw; x++) {
+
+			switch (vstep) {
+			case 1:
+				if (x & 1)
+					p = *(raw + y * w + (x >> 1));
+				else
+					p = *(raw + y * w + (x >> 1) + (w >> 1));
+				break;
+
+			case 2:
+				if (x & 1)
+					p = *(raw + ((y * w) << 1) + x);
+				else
+					p = *(raw + ((y * w) << 1) + x + (w >> 1));
+				break;
+			}
+
+			if (y & 1)
+				bayer = 2;
+			else
+				bayer = 0;
+			if (x & 1)
+				bayer++;
+
+			switch (bayer) {
+			case 0:
+			case 3:
+				colour = 1;
+				break;
+			case 1:
+				colour = 0;
+				break;
+			case 2:
+				colour = 2;
+				break;
+			}
+			i = (y * vw + x) * 3;	/* output already zeroed out with memset */
+			*(output + i + colour) = (unsigned char) p;
+		}		/* for x */
+	}			/* for y */
+
+	/****** gamma correction plus hardcoded white balance */
+	/* Thanks to Alexander Schwartx <alexander.schwartx@gmx.net> for this code.
+	   Correction values red[], green[], blue[], are generated by 
+	   (pow(i/256.0, GAMMA)*255.0)*white balanceRGB where GAMMA=0.55, 1<i<255. 
+	   White balance (RGB)= 1.0, 1.17, 1.48. Values are calculated as double float and 
+	   converted to unsigned char. Values are in stv680.h  */
+	for (y = 0; y < vh; y++) {
+		for (x = 0; x < vw; x++) {
+			i = (y * vw + x) * 3;
+			*(output + i) = red[*(output + i)];
+			*(output + i + 1) = green[*(output + i + 1)];
+			*(output + i + 2) = blue[*(output + i + 2)];
+		}
+	}
+
+	/******  bayer demosaic  ******/
+	for (y = 1; y < (vh - 1); y++) {
+		for (x = 1; x < (vw - 1); x++) {	/* work out pixel type */
+			if (y & 1)
+				bayer = 0;
+			else
+				bayer = 2;
+			if (!(x & 1))
+				bayer++;
+
+			switch (bayer) {
+			case 0:	/* green. blue lr, red tb */
+				*(output + AD (x, y, vw) + BLUE) = ((int) *(output + AD (x - 1, y, vw) + BLUE) + (int) *(output + AD (x + 1, y, vw) + BLUE)) >> 1;
+				*(output + AD (x, y, vw) + RED) = ((int) *(output + AD (x, y - 1, vw) + RED) + (int) *(output + AD (x, y + 1, vw) + RED)) >> 1;
+				break;
+
+			case 1:	/* blue. green lrtb, red diagonals */
+				*(output + AD (x, y, vw) + GREEN) = ((int) *(output + AD (x - 1, y, vw) + GREEN) + (int) *(output + AD (x + 1, y, vw) + GREEN) + (int) *(output + AD (x, y - 1, vw) + GREEN) + (int) *(output + AD (x, y + 1, vw) + GREEN)) >> 2;
+				*(output + AD (x, y, vw) + RED) = ((int) *(output + AD (x - 1, y - 1, vw) + RED) + (int) *(output + AD (x - 1, y + 1, vw) + RED) + (int) *(output + AD (x + 1, y - 1, vw) + RED) + (int) *(output + AD (x + 1, y + 1, vw) + RED)) >> 2;
+				break;
+
+			case 2:	/* red. green lrtb, blue diagonals */
+				*(output + AD (x, y, vw) + GREEN) = ((int) *(output + AD (x - 1, y, vw) + GREEN) + (int) *(output + AD (x + 1, y, vw) + GREEN) + (int) *(output + AD (x, y - 1, vw) + GREEN) + (int) *(output + AD (x, y + 1, vw) + GREEN)) >> 2;
+				*(output + AD (x, y, vw) + BLUE) = ((int) *(output + AD (x - 1, y - 1, vw) + BLUE) + (int) *(output + AD (x + 1, y - 1, vw) + BLUE) + (int) *(output + AD (x - 1, y + 1, vw) + BLUE) + (int) *(output + AD (x + 1, y + 1, vw) + BLUE)) >> 2;
+				break;
+
+			case 3:	/* green. red lr, blue tb */
+				*(output + AD (x, y, vw) + RED) = ((int) *(output + AD (x - 1, y, vw) + RED) + (int) *(output + AD (x + 1, y, vw) + RED)) >> 1;
+				*(output + AD (x, y, vw) + BLUE) = ((int) *(output + AD (x, y - 1, vw) + BLUE) + (int) *(output + AD (x, y + 1, vw) + BLUE)) >> 1;
+				break;
+			}	/* switch */
+		}		/* for x */
+	}			/* for y  - end demosaic  */
+
+	/* output is RGB; some programs want BGR  */
+	if (swapRGB == 1) {
+		for (y = 0; y < vh; y++) {
+			for (x = 0; x < vw; x++) {
+				i = (y * vw + x) * 3;
+				*(temp) = *(output + i);
+				*(output + i) = *(output + i + 2);
+				*(output + i + 2) = *(temp);
+			}
+		}
+	}
+	/* brightness */
+	if (stv680->chgbright == 1) {
+		if (stv680->brightness >= 32767) {
+			p = (stv680->brightness - 32767) / 256;
+			for (x = 0; x < (vw * vh * 3); x++) {
+				if ((*(output + x) + (unsigned char) p) > 255)
+					*(output + x) = 255;
+				else
+					*(output + x) += (unsigned char) p;
+			}	/* for */
+		} else {
+			p = (32767 - stv680->brightness) / 256;
+			for (x = 0; x < (vw * vh * 3); x++) {
+				if ((unsigned char) p > *(output + x))
+					*(output + x) = 0;
+				else
+					*(output + x) -= (unsigned char) p;
+			}	/* for */
+		}		/* else */
+	}
+	/* if */
+	frame->curpix = 0;
+	frame->curlinepix = 0;
+	frame->grabstate = FRAME_DONE;
+	stv680->framecount++;
+	stv680->readcount++;
+	if (stv680->frame[(stv680->curframe + 1) & (STV680_NUMFRAMES - 1)].grabstate == FRAME_READY) {
+		stv680->curframe = (stv680->curframe + 1) & (STV680_NUMFRAMES - 1);
+	}
+
+}				/* bayer_unshuffle */
+
+/*******  end routines from the pencam program  *********/
+
+static int stv680_newframe (struct usb_stv *stv680, int framenr)
+{
+	int errors = 0;
+
+	while (stv680->streaming && (stv680->frame[framenr].grabstate == FRAME_READY || stv680->frame[framenr].grabstate == FRAME_GRABBING)) {
+		if (!stv680->frame[framenr].curpix) {
+			errors++;
+		}
+		wait_event_interruptible (stv680->wq, (stv680->scratch[stv680->scratch_use].state == BUFFER_READY));
+
+		if (stv680->nullpackets > STV680_MAX_NULLPACKETS) {
+			stv680->nullpackets = 0;
+			PDEBUG (2, "STV(i): too many null length packets, restarting capture");
+			stv680_stop_stream (stv680);
+			stv680_start_stream (stv680);
+		} else {
+			if (stv680->scratch[stv680->scratch_use].state != BUFFER_READY) {
+				stv680->frame[framenr].grabstate = FRAME_ERROR;
+				PDEBUG (2, "STV(e): FRAME_ERROR in _newframe");
+				return -EIO;
+			}
+			stv680->scratch[stv680->scratch_use].state = BUFFER_BUSY;
+
+			bayer_unshuffle (stv680, &stv680->scratch[stv680->scratch_use]);
+
+			stv680->scratch[stv680->scratch_use].state = BUFFER_UNUSED;
+			stv680->scratch_use++;
+			if (stv680->scratch_use >= STV680_NUMSCRATCH)
+				stv680->scratch_use = 0;
+			if (errors > STV680_MAX_ERRORS) {
+				errors = 0;
+				PDEBUG (2, "STV(i): too many errors, restarting capture");
+				stv680_stop_stream (stv680);
+				stv680_start_stream (stv680);
+			}
+		}		/* else */
+	}			/* while */
+	return 0;
+}
+
+/*********************************************************************
+ * Video4Linux
+ *********************************************************************/
+
+static int stv_open (struct video_device *dev, int flags)
+{
+	struct usb_stv *stv680 = (struct usb_stv *) dev;
+	int err = 0;
+
+	/* we are called with the BKL held */
+	MOD_INC_USE_COUNT;
+	stv680->user = 1;
+	err = stv_init (stv680);	/* main initialization routine for camera */
+
+	if (err >= 0) {
+		stv680->fbuf = rvmalloc (stv680->maxframesize * STV680_NUMFRAMES);
+		if (!stv680->fbuf) {
+			PDEBUG (0, "STV(e): Could not rvmalloc frame bufer");
+			err = -ENOMEM;
+		}
+	}
+	if (err) {
+		MOD_DEC_USE_COUNT;
+		stv680->user = 0;
+	}
+
+	return err;
+}
+
+static void stv_close (struct video_device *dev)
+{
+	/* called with BKL held */
+	struct usb_stv *stv680 = (struct usb_stv *) dev;
+	int i;
+
+	for (i = 0; i < STV680_NUMFRAMES; i++)
+		stv680->frame[i].grabstate = FRAME_UNUSED;
+	if (stv680->streaming)
+		stv680_stop_stream (stv680);
+
+	if ((i = stv_stop_video (stv680)) < 0)
+		PDEBUG (1, "STV(e): stop_video failed in stv_close");
+
+	rvfree (stv680->fbuf, stv680->maxframesize * STV680_NUMFRAMES);
+	stv680->user = 0;
+
+	if (stv680->removed) {
+		video_unregister_device (&stv680->vdev);
+		kfree (stv680);
+		stv680 = NULL;
+		PDEBUG (0, "STV(i): device unregistered");
+	}
+	MOD_DEC_USE_COUNT;
+}
+
+static long stv680_write (struct video_device *dev, const char *buf, unsigned long count, int noblock)
+{
+	return -EINVAL;
+}
+
+static int stv680_ioctl (struct video_device *vdev, unsigned int cmd, void *arg)
+{
+	struct usb_stv *stv680 = (struct usb_stv *) vdev;
+
+	if (!stv680->udev)
+		return -EIO;
+
+	switch (cmd) {
+	case VIDIOCGCAP:{
+			struct video_capability b;
+
+			strcpy (b.name, stv680->camera_name);
+			b.type = VID_TYPE_CAPTURE;
+			b.channels = 1;
+			b.audios = 0;
+			b.maxwidth = stv680->maxwidth;
+			b.maxheight = stv680->maxheight;
+			b.minwidth = stv680->maxwidth / 2;
+			b.minheight = stv680->maxheight / 2;
+
+			if (copy_to_user (arg, &b, sizeof (b))) {
+				PDEBUG (2, "STV(e): VIDIOCGGAP failed");
+				return -EFAULT;
+			}
+			return 0;
+		}
+	case VIDIOCGCHAN:{
+			struct video_channel v;
+
+			if (copy_from_user (&v, arg, sizeof (v)))
+				return -EFAULT;
+			if (v.channel != 0)
+				return -EINVAL;
+
+			v.flags = 0;
+			v.tuners = 0;
+			v.type = VIDEO_TYPE_CAMERA;
+			strcpy (v.name, "STV Camera");
+
+			if (copy_to_user (arg, &v, sizeof (v))) {
+				PDEBUG (2, "STV(e): VIDIOCGCHAN failed");
+				return -EFAULT;
+			}
+			return 0;
+		}
+	case VIDIOCSCHAN:{
+			int v;
+
+			if (copy_from_user (&v, arg, sizeof (v))) {
+				PDEBUG (2, "STV(e): VIDIOCSCHAN failed");
+				return -EFAULT;
+			}
+			if (v != 0)
+				return -EINVAL;
+
+			return 0;
+		}
+	case VIDIOCGPICT:{
+			struct video_picture p;
+
+			stv680_get_pict (stv680, &p);
+			if (copy_to_user (arg, &p, sizeof (p))) {
+				PDEBUG (2, "STV(e): VIDIOCGPICT failed");
+				return -EFAULT;
+			}
+			return 0;
+		}
+	case VIDIOCSPICT:{
+			struct video_picture p;
+
+			if (copy_from_user (&p, arg, sizeof (p))) {
+				PDEBUG (2, "STV(e): VIDIOCSPICT failed");
+				return -EFAULT;
+			}
+			copy_from_user (&p, arg, sizeof (p));
+			PDEBUG (2, "STV(i): palette set to RGB in VIDIOSPICT");
+
+			if (stv680_set_pict (stv680, &p))
+				return -EINVAL;
+			return 0;
+		}
+	case VIDIOCSWIN:{
+			struct video_window vw;
+
+			if (copy_from_user (&vw, arg, sizeof (vw)))
+				return -EFAULT;
+			if (vw.flags)
+				return -EINVAL;
+			if (vw.clipcount)
+				return -EINVAL;
+			if (vw.width != stv680->vwidth) {
+				if (stv680_set_size (stv680, vw.width, vw.height)) {
+					PDEBUG (2, "STV(e): failed (from user) set size in VIDIOCSWIN");
+					return -EINVAL;
+				}
+			}
+			return 0;
+		}
+	case VIDIOCGWIN:{
+			struct video_window vw;
+
+			vw.x = 0;	/* FIXME */
+			vw.y = 0;
+			vw.chromakey = 0;
+			vw.flags = 0;
+			vw.clipcount = 0;
+			vw.width = stv680->vwidth;
+			vw.height = stv680->vheight;
+
+			if (copy_to_user (arg, &vw, sizeof (vw))) {
+				PDEBUG (2, "STV(e): VIDIOCGWIN failed");
+				return -EFAULT;
+			}
+			return 0;
+		}
+	case VIDIOCGMBUF:{
+			struct video_mbuf vm;
+			int i;
+
+			memset (&vm, 0, sizeof (vm));
+			vm.size = STV680_NUMFRAMES * stv680->maxframesize;
+			vm.frames = STV680_NUMFRAMES;
+			for (i = 0; i < STV680_NUMFRAMES; i++)
+				vm.offsets[i] = stv680->maxframesize * i;
+
+			if (copy_to_user ((void *) arg, (void *) &vm, sizeof (vm))) {
+				PDEBUG (2, "STV(e): VIDIOCGMBUF failed");
+				return -EFAULT;
+			}
+
+			return 0;
+		}
+	case VIDIOCMCAPTURE:{
+			struct video_mmap vm;
+
+			if (copy_from_user (&vm, arg, sizeof (vm))) {
+				PDEBUG (2, "STV(e): VIDIOCMCAPTURE failed");
+				return -EFAULT;
+			}
+			if (vm.format != STV_VIDEO_PALETTE) {
+				PDEBUG (2, "STV(i): VIDIOCMCAPTURE vm.format (%i) != VIDEO_PALETTE (%i)",
+					vm.format, STV_VIDEO_PALETTE);
+				if (vm.format == 3) {
+					PDEBUG (2, "STV(i): VIDIOCMCAPTURE swapRGB is ON");
+					/* this may fix those apps (e.g., xawtv) that want BGR */
+					swapRGB = 1;
+				}
+				return -EINVAL;
+			}
+			if (vm.frame >= STV680_NUMFRAMES) {
+				PDEBUG (2, "STV(e): VIDIOCMCAPTURE vm.frame > NUMFRAMES");
+				return -EINVAL;
+			}
+			if (stv680->frame[vm.frame].grabstate != FRAME_UNUSED) {
+				PDEBUG (2, "STV(e): VIDIOCMCAPTURE grabstate != FRAME_UNUSED");
+				return -EBUSY;
+			}
+			/* Is this according to the v4l spec??? */
+			if (stv680->vwidth != vm.width) {
+				if (stv680_set_size (stv680, vm.width, vm.height)) {
+					PDEBUG (2, "STV(e): VIDIOCMCAPTURE set_size failed");
+					return -EINVAL;
+				}
+			}
+			stv680->frame[vm.frame].grabstate = FRAME_READY;
+
+			if (!stv680->streaming)
+				stv680_start_stream (stv680);
+
+			return 0;
+		}
+	case VIDIOCSYNC:{
+			int frame, ret = 0;
+
+			if (copy_from_user ((void *) &frame, arg, sizeof (int))) {
+				PDEBUG (2, "STV(e): VIDIOCSYNC failed");
+				return -EFAULT;
+			}
+			if (frame < 0 || frame >= STV680_NUMFRAMES) {
+				PDEBUG (2, "STV(e): Bad frame # in VIDIOCSYNC");
+				return -EINVAL;
+			}
+			ret = stv680_newframe (stv680, frame);
+			stv680->frame[frame].grabstate = FRAME_UNUSED;
+			return ret;
+		}
+	case VIDIOCGFBUF:{
+			struct video_buffer vb;
+
+			memset (&vb, 0, sizeof (vb));
+			vb.base = NULL;	/* frame buffer not supported, not used */
+
+			if (copy_to_user ((void *) arg, (void *) &vb, sizeof (vb))) {
+				PDEBUG (2, "STV(e): VIDIOCSYNC failed");
+				return -EFAULT;
+			}
+			return 0;
+		}
+	case VIDIOCKEY:
+		return 0;
+	case VIDIOCCAPTURE:
+		{
+			PDEBUG (2, "STV(e): VIDIOCCAPTURE failed");
+			return -EINVAL;
+		}
+	case VIDIOCSFBUF:
+		return -EINVAL;
+	case VIDIOCGTUNER:
+	case VIDIOCSTUNER:
+		return -EINVAL;
+	case VIDIOCGFREQ:
+	case VIDIOCSFREQ:
+		return -EINVAL;
+	case VIDIOCGAUDIO:
+	case VIDIOCSAUDIO:
+		return -EINVAL;
+	default:
+		return -ENOIOCTLCMD;
+	}			/* end switch */
+
+	return 0;
+}
+
+static int stv680_mmap (struct video_device *dev, const char *adr, unsigned long size)
+{
+	struct usb_stv *stv680 = (struct usb_stv *) dev;
+	unsigned long start = (unsigned long) adr;
+	unsigned long page, pos;
+
+	down (&stv680->lock);
+
+	if (stv680->udev == NULL) {
+		up (&stv680->lock);
+		return -EIO;
+	}
+	if (size > (((STV680_NUMFRAMES * stv680->maxframesize) + PAGE_SIZE - 1)
+		    & ~(PAGE_SIZE - 1))) {
+		up (&stv680->lock);
+		return -EINVAL;
+	}
+	pos = (unsigned long) stv680->fbuf;
+	while (size > 0) {
+		page = kvirt_to_pa (pos);
+		if (remap_page_range (start, page, PAGE_SIZE, PAGE_SHARED)) {
+			up (&stv680->lock);
+			return -EAGAIN;
+		}
+		start += PAGE_SIZE;
+		pos += PAGE_SIZE;
+		if (size > PAGE_SIZE)
+			size -= PAGE_SIZE;
+		else
+			size = 0;
+	}
+	up (&stv680->lock);
+
+	return 0;
+}
+
+static long stv680_read (struct video_device *dev, char *buf, unsigned long count, int noblock)
+{
+	unsigned long int realcount = count;
+	int ret = 0;
+	struct usb_stv *stv680 = (struct usb_stv *) dev;
+	unsigned long int i;
+
+	if (STV680_NUMFRAMES != 2) {
+		PDEBUG (0, "STV(e): STV680_NUMFRAMES needs to be 2!");
+		return -1;
+	}
+	if (stv680->udev == NULL)
+		return -EIO;
+	if (realcount > (stv680->vwidth * stv680->vheight * 3))
+		realcount = stv680->vwidth * stv680->vheight * 3;
+
+	/* Shouldn't happen: */
+	if (stv680->frame[0].grabstate == FRAME_GRABBING) {
+		PDEBUG (2, "STV(e): FRAME_GRABBING in stv680_read");
+		return -EBUSY;
+	}
+	stv680->frame[0].grabstate = FRAME_READY;
+	stv680->frame[1].grabstate = FRAME_UNUSED;
+	stv680->curframe = 0;
+
+	if (!stv680->streaming)
+		stv680_start_stream (stv680);
+
+	if (!stv680->streaming) {
+		ret = stv680_newframe (stv680, 0);	/* ret should = 0 */
+	}
+
+	ret = stv680_newframe (stv680, 0);
+
+	if (!ret) {
+		if ((i = copy_to_user (buf, stv680->frame[0].data, realcount)) != 0) {
+			PDEBUG (2, "STV(e): copy_to_user frame 0 failed, ret count = %li", i);
+			return -EFAULT;
+		}
+	} else {
+		realcount = ret;
+	}
+	stv680->frame[0].grabstate = FRAME_UNUSED;
+	return realcount;
+}				/* stv680_read */
+
+static int stv_init_done (struct video_device *dev)
+{
+
+#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
+	if (create_proc_stv680_cam ((struct usb_stv *) dev) < 0)
+		return -1;
+#endif
+	return 0;
+}
+
+static struct video_device stv680_template = {
+	owner:		THIS_MODULE,
+	name:		"STV0680 USB camera",
+	type:		VID_TYPE_CAPTURE,
+	hardware:	VID_HARDWARE_SE401,
+	open:		stv_open,
+	close:		stv_close,
+	read:		stv680_read,
+	write:		stv680_write,
+	ioctl:		stv680_ioctl,
+	mmap:		stv680_mmap,
+	initialize:	stv_init_done,
+};
+
+static void *__devinit stv680_probe (struct usb_device *dev, unsigned int ifnum, const struct usb_device_id *id)
+{
+	struct usb_interface_descriptor *interface;
+	struct usb_stv *stv680;
+	char *camera_name = NULL;
+
+	/* We don't handle multi-config cameras */
+	if (dev->descriptor.bNumConfigurations != 1) {
+		PDEBUG (0, "STV(e): Number of Configurations != 1");
+		return NULL;
+	}
+
+	interface = &dev->actconfig->interface[ifnum].altsetting[0];
+	/* Is it a STV680? */
+	if ((dev->descriptor.idVendor == USB_PENCAM_VENDOR_ID) && (dev->descriptor.idProduct == USB_PENCAM_PRODUCT_ID)) {
+		camera_name = "STV0680";
+		PDEBUG (0, "STV(i): STV0680 camera found.");
+	} else {
+		PDEBUG (0, "STV(e): Vendor/Product ID do not match STV0680 values.");
+		PDEBUG (0, "STV(e): Check that the STV0680 camera is connected to the computer.");
+		return NULL;
+	}
+	/* We found one */
+	if ((stv680 = kmalloc (sizeof (*stv680), GFP_KERNEL)) == NULL) {
+		PDEBUG (0, "STV(e): couldn't kmalloc stv680 struct.");
+		return NULL;
+	}
+
+	memset (stv680, 0, sizeof (*stv680));
+
+	stv680->udev = dev;
+	stv680->camera_name = camera_name;
+
+	memcpy (&stv680->vdev, &stv680_template, sizeof (stv680_template));
+	memcpy (stv680->vdev.name, stv680->camera_name, strlen (stv680->camera_name));
+	init_waitqueue_head (&stv680->wq);
+	init_MUTEX (&stv680->lock);
+	wmb ();
+
+	if (video_register_device (&stv680->vdev, VFL_TYPE_GRABBER, video_nr) == -1) {
+		kfree (stv680);
+		PDEBUG (0, "STV(e): video_register_device failed");
+		return NULL;
+	}
+	PDEBUG (0, "STV(i): registered new video device: video%d", stv680->vdev.minor);
+
+	return stv680;
+}
+
+static inline void usb_stv680_remove_disconnected (struct usb_stv *stv680)
+{
+	int i;
+
+	stv680->udev = NULL;
+	stv680->frame[0].grabstate = FRAME_ERROR;
+	stv680->frame[1].grabstate = FRAME_ERROR;
+	stv680->streaming = 0;
+
+	wake_up_interruptible (&stv680->wq);
+
+	for (i = 0; i < STV680_NUMSBUF; i++)
+		if (stv680->urb[i]) {
+			stv680->urb[i]->next = NULL;
+			usb_unlink_urb (stv680->urb[i]);
+			usb_free_urb (stv680->urb[i]);
+			stv680->urb[i] = NULL;
+			kfree (stv680->sbuf[i].data);
+		}
+	for (i = 0; i < STV680_NUMSCRATCH; i++)
+		if (stv680->scratch[i].data) {
+			kfree (stv680->scratch[i].data);
+		}
+	PDEBUG (0, "STV(i): %s disconnected", stv680->camera_name);
+
+#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
+	destroy_proc_stv680_cam (stv680);
+#endif
+	/* Free the memory */
+	kfree (stv680);
+}
+
+static void stv680_disconnect (struct usb_device *dev, void *ptr)
+{
+	struct usb_stv *stv680 = (struct usb_stv *) ptr;
+
+	lock_kernel ();
+	/* We don't want people trying to open up the device */
+	if (!stv680->user) {
+		video_unregister_device (&stv680->vdev);
+		usb_stv680_remove_disconnected (stv680);
+	} else {
+		stv680->removed = 1;
+	}
+	unlock_kernel ();
+}
+
+static struct usb_driver stv680_driver = {
+	name:		"stv680",
+	probe:		stv680_probe,
+	disconnect:	stv680_disconnect,
+	id_table:	device_table
+};
+
+/********************************************************************
+ *  Module routines
+ ********************************************************************/
+
+static int __init usb_stv680_init (void)
+{
+#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
+	if (proc_stv680_create () < 0)
+		return -1;
+#endif
+	if (usb_register (&stv680_driver) < 0) {
+		PDEBUG (0, "STV(e): Could not setup STV0680 driver");
+		return -1;
+	}
+	PDEBUG (0, "STV(i): usb camera driver version %s registering", DRIVER_VERSION);
+
+	info(DRIVER_DESC " " DRIVER_VERSION);
+	return 0;
+}
+
+static void __exit usb_stv680_exit (void)
+{
+	usb_deregister (&stv680_driver);
+	PDEBUG (0, "STV(i): driver deregistered");
+
+#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
+	proc_stv680_destroy ();
+#endif
+}
+
+module_init (usb_stv680_init);
+module_exit (usb_stv680_exit);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/usb/stv680.h linux/drivers/usb/stv680.h
--- linux.orig/drivers/usb/stv680.h	Thu Jan  1 00:00:00 1970
+++ linux/drivers/usb/stv680.h	Wed Dec 26 15:09:47 2001
@@ -0,0 +1,222 @@
+/****************************************************************************
+ *
+ *  Filename: stv680.h
+ *
+ *  Description:
+ *     This is a USB driver for STV0680 based usb video cameras.
+ *
+ *  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.
+ *
+ ****************************************************************************/
+
+/* size of usb transfers */
+#define STV680_PACKETSIZE	4096
+
+/* number of queued bulk transfers to use, may have problems if > 1 */
+#define STV680_NUMSBUF		1
+
+/* number of frames supported by the v4l part */
+#define STV680_NUMFRAMES	2
+
+/* scratch buffers for passing data to the decoders: 2 or 4 are good */
+#define STV680_NUMSCRATCH	2
+
+/* number of nul sized packets to receive before kicking the camera */
+#define STV680_MAX_NULLPACKETS	200
+
+/* number of decoding errors before kicking the camera */
+#define STV680_MAX_ERRORS	100
+
+#define USB_PENCAM_VENDOR_ID	0x0553
+#define USB_PENCAM_PRODUCT_ID	0x0202
+#define PENCAM_TIMEOUT          1000
+/* fmt 4 */
+#define STV_VIDEO_PALETTE    VIDEO_PALETTE_RGB24
+
+static __devinitdata struct usb_device_id device_table[] = {
+	{USB_DEVICE (USB_PENCAM_VENDOR_ID, USB_PENCAM_PRODUCT_ID)},
+	{}
+};
+MODULE_DEVICE_TABLE (usb, device_table);
+
+struct stv680_sbuf {
+	unsigned char *data;
+};
+
+enum {
+	FRAME_UNUSED,		/* Unused (no MCAPTURE) */
+	FRAME_READY,		/* Ready to start grabbing */
+	FRAME_GRABBING,		/* In the process of being grabbed into */
+	FRAME_DONE,		/* Finished grabbing, but not been synced yet */
+	FRAME_ERROR,		/* Something bad happened while processing */
+};
+
+enum {
+	BUFFER_UNUSED,
+	BUFFER_READY,
+	BUFFER_BUSY,
+	BUFFER_DONE,
+};
+
+/* raw camera data <- sbuf (urb transfer buf) */
+struct stv680_scratch {
+	unsigned char *data;
+	volatile int state;
+	int offset;
+	int length;
+};
+
+/* processed data for display ends up here, after bayer */
+struct stv680_frame {
+	unsigned char *data;	/* Frame buffer */
+	volatile int grabstate;	/* State of grabbing */
+	unsigned char *curline;
+	int curlinepix;
+	int curpix;
+};
+
+/* this is almost the video structure uvd_t, with extra parameters for stv */
+struct usb_stv {
+	struct video_device vdev;
+
+	struct usb_device *udev;
+
+	unsigned char bulk_in_endpointAddr;	/* __u8  the address of the bulk in endpoint */
+	char *camera_name;
+
+	unsigned int VideoMode;	/* 0x0100 = VGA, 0x0000 = CIF, 0x0300 = QVGA */
+	int SupportedModes;
+	int CIF;
+	int VGA;
+	int QVGA;
+	int cwidth;		/* camera width */
+	int cheight;		/* camera height */
+	int maxwidth;		/* max video width */
+	int maxheight;		/* max video height */
+	int vwidth;		/* current width for video window */
+	int vheight;		/* current height for video window */
+	unsigned long int rawbufsize;
+	unsigned long int maxframesize;	/* rawbufsize * 3 for RGB */
+
+	int origGain;
+	int origMode;		/* original camera mode */
+
+	struct semaphore lock;	/* to lock the structure */
+	int user;		/* user count for exclusive use */
+	int removed;		/* device disconnected */
+	int streaming;		/* Are we streaming video? */
+	char *fbuf;		/* Videodev buffer area */
+	urb_t *urb[STV680_NUMSBUF];	/* # of queued bulk transfers */
+	int curframe;		/* Current receiving frame */
+	struct stv680_frame frame[STV680_NUMFRAMES];	/* # frames supported by v4l part */
+	int readcount;
+	int framecount;
+	int error;
+	int dropped;
+	int scratch_next;
+	int scratch_use;
+	int scratch_overflow;
+	struct stv680_scratch scratch[STV680_NUMSCRATCH];	/* for decoders */
+	struct stv680_sbuf sbuf[STV680_NUMSBUF];
+
+	unsigned int brightness;
+	unsigned int chgbright;
+	unsigned int whiteness;
+	unsigned int colour;
+	unsigned int contrast;
+	unsigned int hue;
+	unsigned int palette;
+	unsigned int depth;	/* rgb24 in bits */
+
+	wait_queue_head_t wq;	/* Processes waiting */
+
+	struct proc_dir_entry *proc_entry;	/* /proc/stv680/videoX */
+	int nullpackets;
+};
+
+unsigned char red[256] = {
+	0, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
+	21, 21, 21, 21, 21, 21, 21, 28, 34, 39, 43, 47,
+	50, 53, 56, 59, 61, 64, 66, 68, 71, 73, 75, 77,
+	79, 80, 82, 84, 86, 87, 89, 91, 92, 94, 95, 97,
+	98, 100, 101, 102, 104, 105, 106, 108, 109, 110, 111, 113,
+	114, 115, 116, 117, 118, 120, 121, 122, 123, 124, 125, 126,
+	127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138,
+	139, 140, 141, 142, 143, 144, 144, 145, 146, 147, 148, 149,
+	150, 151, 151, 152, 153, 154, 155, 156, 156, 157, 158, 159,
+	160, 160, 161, 162, 163, 164, 164, 165, 166, 167, 167, 168,
+	169, 170, 170, 171, 172, 172, 173, 174, 175, 175, 176, 177,
+	177, 178, 179, 179, 180, 181, 182, 182, 183, 184, 184, 185,
+	186, 186, 187, 187, 188, 189, 189, 190, 191, 191, 192, 193,
+	193, 194, 194, 195, 196, 196, 197, 198, 198, 199, 199, 200,
+	201, 201, 202, 202, 203, 204, 204, 205, 205, 206, 206, 207,
+	208, 208, 209, 209, 210, 210, 211, 212, 212, 213, 213, 214,
+	214, 215, 215, 216, 217, 217, 218, 218, 219, 219, 220, 220,
+	221, 221, 222, 222, 223, 224, 224, 225, 225, 226, 226, 227,
+	227, 228, 228, 229, 229, 230, 230, 231, 231, 232, 232, 233,
+	233, 234, 234, 235, 235, 236, 236, 237, 237, 238, 238, 239,
+	239, 240, 240, 241, 241, 242, 242, 243, 243, 243, 244, 244,
+	245, 245, 246, 246
+};
+
+unsigned char green[256] = {
+	0, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+	24, 24, 24, 24, 24, 24, 24, 32, 39, 45, 50, 54,
+	58, 62, 65, 69, 71, 74, 77, 79, 83, 85, 87, 90,
+	92, 93, 95, 98, 100, 101, 104, 106, 107, 109, 111, 113,
+	114, 116, 118, 119, 121, 122, 124, 126, 127, 128, 129, 132,
+	133, 134, 135, 136, 138, 140, 141, 142, 143, 145, 146, 147,
+	148, 149, 150, 152, 153, 154, 155, 156, 157, 159, 160, 161,
+	162, 163, 164, 166, 167, 168, 168, 169, 170, 171, 173, 174,
+	175, 176, 176, 177, 179, 180, 181, 182, 182, 183, 184, 186,
+	187, 187, 188, 189, 190, 191, 191, 193, 194, 195, 195, 196,
+	197, 198, 198, 200, 201, 201, 202, 203, 204, 204, 205, 207,
+	207, 208, 209, 209, 210, 211, 212, 212, 214, 215, 215, 216,
+	217, 217, 218, 218, 219, 221, 221, 222, 223, 223, 224, 225,
+	225, 226, 226, 228, 229, 229, 230, 231, 231, 232, 232, 233,
+	235, 235, 236, 236, 237, 238, 238, 239, 239, 241, 241, 242,
+	243, 243, 244, 244, 245, 245, 246, 248, 248, 249, 249, 250,
+	250, 251, 251, 252, 253, 253, 255, 255, 255, 255, 255, 255,
+	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+	255, 255, 255, 255
+};
+
+unsigned char blue[256] = {
+	0, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31,
+	31, 31, 31, 31, 31, 31, 31, 41, 50, 57, 63, 69,
+	74, 78, 82, 87, 90, 94, 97, 100, 105, 108, 111, 113,
+	116, 118, 121, 124, 127, 128, 131, 134, 136, 139, 140, 143,
+	145, 148, 149, 150, 153, 155, 156, 159, 161, 162, 164, 167,
+	168, 170, 171, 173, 174, 177, 179, 180, 182, 183, 185, 186,
+	187, 189, 190, 192, 193, 195, 196, 198, 199, 201, 202, 204,
+	205, 207, 208, 210, 211, 213, 213, 214, 216, 217, 219, 220,
+	222, 223, 223, 224, 226, 227, 229, 230, 230, 232, 233, 235,
+	236, 236, 238, 239, 241, 242, 242, 244, 245, 247, 247, 248,
+	250, 251, 251, 253, 254, 254, 255, 255, 255, 255, 255, 255,
+	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+	255, 255, 255, 255
+};
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/usb/usbnet.c linux/drivers/usb/usbnet.c
--- linux.orig/drivers/usb/usbnet.c	Mon Jan  7 23:41:09 2002
+++ linux/drivers/usb/usbnet.c	Wed Dec 26 15:09:47 2001
@@ -1577,7 +1577,7 @@
 	struct nc_trailer	*trailer = 0;
 #endif	/* CONFIG_USB_NET1080 */
 
-	flags = in_interrupt () ? GFP_ATOMIC : GFP_KERNEL;
+	flags = in_interrupt () ? GFP_ATOMIC : GFP_NOIO; /* might be used for nfs */
 
 	// some devices want funky USB-level framing, for
 	// win32 driver (usually) and/or hardware quirks
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/usb/vicam.c linux/drivers/usb/vicam.c
--- linux.orig/drivers/usb/vicam.c	Thu Jan  1 00:00:00 1970
+++ linux/drivers/usb/vicam.c	Wed Dec 26 15:09:47 2001
@@ -0,0 +1,986 @@
+/* -*- linux-c -*-
+ * USB ViCAM driver
+ *
+ * Copyright (c) 2001 Christopher L Cheney (ccheney@cheney.cx)
+ * Copyright (c) 2001 Pavel Machek (pavel@suse.cz) sponsored by SuSE
+ *
+ *      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 driver is for the Vista Imaging ViCAM and 3Com HomeConnect USB
+ *
+ * Thanks to Greg Kroah-Hartman for the USB Skeleton driver
+ *
+ * TODO:
+ *	- find out the ids for the Vista Imaging ViCAM
+ *
+ * History:
+ *
+ * 2001_07_07 - 0.1 - christopher: first version
+ * 2001_08_28 - 0.2 - pavel: messed it up, but for some fun, try 
+ 			while true; do dd if=/dev/video of=/dev/fb0 bs=$[0x1e480] count=1 2> /dev/null; done
+		      yep, moving pictures.
+ * 2001_08_29 - 0.3 - pavel: played a little bit more. Experimental mmap support. For some fun,
+ 			get gqcam-0.9, compile it and run. Better than dd ;-).
+ * 2001_08_29 - 0.4 - pavel: added shutter speed control (not much functional)
+ 			kill update_params if it does not seem to work for you.
+ * 2001_08_30 - 0.5 - pavel: fixed stupid bug with update_params & vicam_bulk
+
+ *
+ * FIXME: It crashes on rmmod with camera plugged.
+ */
+#define DEBUG 1
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/fcntl.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/list.h>
+#include <linux/smp_lock.h>
+#include <linux/devfs_fs_kernel.h>
+#include <linux/usb.h>
+
+#include <asm/io.h>
+#include <linux/wrapper.h>
+#include <linux/vmalloc.h>
+
+#include <linux/videodev.h>
+
+#include "vicam.h"
+#include "vicamurbs.h"
+
+/* Version Information */
+#define DRIVER_VERSION "v0"
+#define DRIVER_AUTHOR "Christopher L Cheney <ccheney@cheney.cx>, Pavel Machek <pavel@suse.cz>"
+#define DRIVER_DESC "USB ViCAM Driver"
+
+/* Define these values to match your device */
+#define USB_VICAM_VENDOR_ID	0x04C1
+#define USB_VICAM_PRODUCT_ID	0x009D
+
+/* table of devices that work with this driver */
+static struct usb_device_id vicam_table [] = {
+	{ USB_DEVICE(USB_VICAM_VENDOR_ID, USB_VICAM_PRODUCT_ID) },
+	{ }					/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE (usb, vicam_table);
+
+static int video_nr = -1; 		/* next avail video device */
+static struct usb_driver vicam_driver;
+
+static char *buf, *buf2;
+static int change_pending = 0; 
+
+static int vicam_parameters(struct usb_vicam *vicam);
+
+/******************************************************************************
+ *
+ *  Memory management functions
+ *
+ *  Taken from bttv-drivers.c 2.4.7-pre3
+ *
+ ******************************************************************************/
+
+/* [DaveM] I've recoded most of this so that:
+ * 1) It's easier to tell what is happening
+ * 2) It's more portable, especially for translating things
+ *    out of vmalloc mapped areas in the kernel.
+ * 3) Less unnecessary translations happen.
+ *
+ * The code used to assume that the kernel vmalloc mappings
+ * existed in the page tables of every process, this is simply
+ * not guarenteed.  We now use pgd_offset_k which is the
+ * defined way to get at the kernel page tables.
+ */
+
+/* Given PGD from the address space's page table, return the kernel
+ * virtual mapping of the physical memory mapped at ADR.
+ */
+static inline unsigned long uvirt_to_kva(pgd_t *pgd, unsigned long adr)
+{
+	unsigned long ret = 0UL;
+	pmd_t *pmd;
+	pte_t *ptep, pte;
+
+	if (!pgd_none(*pgd)) {
+		pmd = pmd_offset(pgd, adr);
+		if (!pmd_none(*pmd)) {
+			ptep = pte_offset(pmd, adr);
+			pte = *ptep;
+			if(pte_present(pte)) {
+				ret  = (unsigned long) page_address(pte_page(pte));
+				ret |= (adr & (PAGE_SIZE - 1));
+
+			}
+		}
+	}
+	return ret;
+}
+
+static inline unsigned long uvirt_to_bus(unsigned long adr)
+{
+	unsigned long kva, ret;
+
+	kva = uvirt_to_kva(pgd_offset(current->mm, adr), adr);
+	ret = virt_to_bus((void *)kva);
+	return ret;
+}
+
+static inline unsigned long kvirt_to_bus(unsigned long adr)
+{
+	unsigned long va, kva, ret;
+
+	va = VMALLOC_VMADDR(adr);
+	kva = uvirt_to_kva(pgd_offset_k(va), va);
+	ret = virt_to_bus((void *)kva);
+	return ret;
+}
+
+/* Here we want the physical address of the memory.
+ * This is used when initializing the contents of the
+ * area and marking the pages as reserved.
+ */
+static inline unsigned long kvirt_to_pa(unsigned long adr)
+{
+	unsigned long va, kva, ret;
+
+	va = VMALLOC_VMADDR(adr);
+	kva = uvirt_to_kva(pgd_offset_k(va), va);
+	ret = __pa(kva);
+	return ret;
+}
+
+static void * rvmalloc(signed long size)
+{
+	void * mem;
+	unsigned long adr, page;
+
+	mem=vmalloc_32(size);
+	if (mem)
+	{
+		memset(mem, 0, size); /* Clear the ram out, no junk to the user */
+		adr=(unsigned long) mem;
+		while (size > 0)
+		{
+			page = kvirt_to_pa(adr);
+			mem_map_reserve(virt_to_page(__va(page)));
+			adr+=PAGE_SIZE;
+			size-=PAGE_SIZE;
+		}
+	}
+	return mem;
+}
+
+static void rvfree(void * mem, signed long size)
+{
+	unsigned long adr, page;
+
+	if (mem)
+	{
+		adr=(unsigned long) mem;
+		while (size > 0)
+		{
+			page = kvirt_to_pa(adr);
+			mem_map_unreserve(virt_to_page(__va(page)));
+			adr+=PAGE_SIZE;
+			size-=PAGE_SIZE;
+		}
+		vfree(mem);
+	}
+}
+
+/******************************************************************************
+ *
+ *  Foo Bar
+ *
+ ******************************************************************************/
+
+/**
+ *	usb_vicam_debug_data
+ */
+static inline void usb_vicam_debug_data (const char *function, int size, const unsigned char *data)
+{
+	int i;
+
+	if (!debug)
+		return;
+
+	printk (KERN_DEBUG __FILE__": %s - length = %d, data = ",
+		function, size);
+	for (i = 0; i < size; ++i) {
+		printk ("%.2x ", data[i]);
+	}
+	printk ("\n");
+}
+
+/*****************************************************************************
+ *
+ *  Send command to vicam
+ *
+ *****************************************************************************/
+
+static int vicam_sndctrl(int set, struct usb_vicam *vicam, unsigned short req,
+	unsigned short value, unsigned char *cp, int size)
+{
+	int ret;
+	unsigned char *transfer_buffer = kmalloc (size, GFP_KERNEL);
+
+	/* Needs to return data I think, works for sending though */
+	memcpy(transfer_buffer, cp, size);
+	
+	ret = usb_control_msg ( vicam->udev, set ? usb_sndctrlpipe(vicam->udev, 0) : usb_rcvctrlpipe(vicam->udev, 0), req, (set ? USB_DIR_OUT : USB_DIR_IN) | USB_TYPE_VENDOR | USB_RECIP_DEVICE, value, 0, transfer_buffer, size, HZ);
+
+	kfree(transfer_buffer);
+	if (ret)
+		printk("vicam: error: %d\n", ret);
+	mdelay(100);
+	return ret;
+}
+
+
+/*****************************************************************************
+ *
+ *  Video4Linux Helpers
+ * 
+ *****************************************************************************/
+
+static int vicam_get_capability(struct usb_vicam *vicam, struct video_capability *b)
+{
+	dbg("vicam_get_capability");
+
+	strcpy(b->name, vicam->camera_name);
+	b->type = VID_TYPE_CAPTURE | VID_TYPE_MONOCHROME;
+	b->channels = 1;
+	b->audios = 0;
+
+	b->maxwidth = vicam->width[vicam->sizes-1];
+	b->maxheight = vicam->height[vicam->sizes-1];
+	b->minwidth = vicam->width[0];
+	b->minheight = vicam->height[0];
+
+	return 0;
+}
+		
+static int vicam_get_channel(struct usb_vicam *vicam, struct video_channel *v)
+{
+	dbg("vicam_get_channel");
+
+	if (v->channel != 0)
+		return -EINVAL;
+ 
+	v->flags = 0;
+	v->tuners = 0;
+	v->type = VIDEO_TYPE_CAMERA;
+	strcpy(v->name, "Camera");
+
+	return 0;
+} 
+		
+static int vicam_set_channel(struct usb_vicam *vicam, struct video_channel *v)
+{
+	dbg("vicam_set_channel");
+
+	if (v->channel != 0)
+		return -EINVAL;
+	
+	return 0;
+}
+		
+static int vicam_get_mmapbuffer(struct usb_vicam *vicam, struct video_mbuf *vm)
+{
+	int i;
+
+	dbg("vicam_get_mmapbuffer");
+
+	memset(vm, 0, sizeof(vm));
+	vm->size = VICAM_NUMFRAMES * vicam->maxframesize;
+	vm->frames = VICAM_NUMFRAMES;
+
+	for (i=0; i<VICAM_NUMFRAMES; i++)
+		vm->offsets[i] = vicam->maxframesize * i;
+
+	return 0;
+}
+
+static int vicam_get_picture(struct usb_vicam *vicam, struct video_picture *p)
+{
+	dbg("vicam_get_picture");
+
+	/* This is probably where that weird 0x56 call goes */
+	p->brightness = vicam->win.brightness;
+	p->hue = vicam->win.hue;
+	p->colour = vicam->win.colour;
+	p->contrast = vicam->win.contrast;
+	p->whiteness = vicam->win.whiteness;
+	p->depth = vicam->win.depth;
+	p->palette = vicam->win.palette;
+
+	return 0;
+}
+
+static void synchronize(struct usb_vicam *vicam)
+{
+	change_pending = 1;
+	interruptible_sleep_on(&vicam->wait);
+	vicam_sndctrl(1, vicam, VICAM_REQ_CAMERA_POWER, 0x00, NULL, 0);
+	mdelay(10);
+	vicam_sndctrl(1, vicam, VICAM_REQ_LED_CONTROL, 0x00, NULL, 0);
+	mdelay(10);
+}
+
+static void params_changed(struct usb_vicam *vicam)
+{
+#if 1
+	synchronize(vicam);
+	mdelay(10);
+	vicam_parameters(vicam);
+	printk("Submiting urb: %d\n", usb_submit_urb(&vicam->readurb));
+#endif
+}
+
+static int vicam_set_picture(struct usb_vicam *vicam, struct video_picture *p)
+{
+	int changed = 0;
+	info("vicam_set_picture (%d)", p->brightness);
+
+
+#define SET(x) \
+	if (vicam->win.x != p->x) \
+		vicam->win.x = p->x, changed = 1;
+	SET(brightness);
+	SET(hue);
+	SET(colour);
+	SET(contrast);
+	SET(whiteness);
+	SET(depth);
+	SET(palette);
+	if (changed)
+		params_changed(vicam);
+
+	return 0;
+	/* Investigate what should be done maybe 0x56 type call */
+	if (p->depth != 8) return 1;
+	if (p->palette != VIDEO_PALETTE_GREY) return 1;
+
+	return 0;
+}
+
+/* FIXME - vicam_sync_frame - important */
+static int vicam_sync_frame(struct usb_vicam *vicam, int frame)
+{
+	dbg("vicam_sync_frame");
+
+	if(frame <0 || frame >= VICAM_NUMFRAMES)
+		return -EINVAL;
+
+	/* Probably need to handle various cases */
+/*	ret=vicam_newframe(vicam, frame);
+	vicam->frame[frame].grabstate=FRAME_UNUSED;
+*/
+	return 0;
+}
+	
+static int vicam_get_window(struct usb_vicam *vicam, struct video_window *vw)
+{
+	dbg("vicam_get_window");
+
+	vw->x = 0;
+	vw->y = 0;
+	vw->chromakey = 0;
+	vw->flags = 0;
+	vw->clipcount = 0;
+	vw->width = vicam->win.width;
+	vw->height = vicam->win.height;
+
+	return 0;
+}
+
+static int vicam_set_window(struct usb_vicam *vicam, struct video_window *vw)
+{
+	info("vicam_set_window");
+		
+	if (vw->flags)
+		return -EINVAL;
+	if (vw->clipcount)
+		return -EINVAL;
+
+	if (vicam->win.width == vw->width && vicam->win.height == vw->height)
+		return 0;
+
+	/* Pick largest mode that is smaller than specified res */
+	/* If specified res is too small reject                 */
+
+	/* Add urb send to device... */
+
+	vicam->win.width = vw->width;
+	vicam->win.height = vw->height;
+	params_changed(vicam);
+
+	return 0;
+}
+
+/* FIXME - vicam_mmap_capture - important */
+static int vicam_mmap_capture(struct usb_vicam *vicam, struct video_mmap *vm)
+{
+	dbg("vicam_mmap_capture");
+
+	/* usbvideo.c looks good for using here */
+
+	/* 
+	if (vm->frame >= VICAM_NUMFRAMES)
+		return -EINVAL;
+	if (vicam->frame[vm->frame].grabstate != FRAME_UNUSED)
+		return -EBUSY;
+	vicam->frame[vm->frame].grabstate=FRAME_READY;
+	*/
+
+	/* No need to vicam_set_window here according to Alan */
+
+	/*
+	if (!vicam->streaming)
+		vicam_start_stream(vicam);
+	*/
+
+	/* set frame as ready */
+
+	return 0;
+}
+
+/*****************************************************************************
+ *
+ *  Video4Linux
+ * 
+ *****************************************************************************/
+
+static int vicam_v4l_open(struct video_device *vdev, int flags)
+{
+	struct usb_vicam *vicam = (struct usb_vicam *)vdev;
+	int err = 0;
+	
+	dbg("vicam_v4l_open");
+
+	MOD_INC_USE_COUNT; 
+	down(&vicam->sem);
+
+	if (vicam->open_count)		/* Maybe not needed? */
+		err = -EBUSY;
+	else {
+		vicam->fbuf = rvmalloc(vicam->maxframesize * VICAM_NUMFRAMES);
+		if (!vicam->fbuf)
+			err=-ENOMEM;
+		else {
+			vicam->open_count = 1;
+		}
+#ifdef BLINKING
+		vicam_sndctrl(1, vicam, VICAM_REQ_CAMERA_POWER, 0x01, NULL, 0);
+		info ("led on");
+		vicam_sndctrl(1, vicam, VICAM_REQ_LED_CONTROL, 0x01, NULL, 0);
+#endif
+	}
+
+	up(&vicam->sem);
+	if (err)
+		MOD_DEC_USE_COUNT;
+	return err;
+}
+
+static void vicam_v4l_close(struct video_device *vdev)
+{
+	struct usb_vicam *vicam = (struct usb_vicam *)vdev;
+
+	dbg("vicam_v4l_close");
+	
+	down(&vicam->sem);
+
+#ifdef BLINKING
+	info ("led off");
+	vicam_sndctrl(1, vicam, VICAM_REQ_LED_CONTROL, 0x00, NULL, 0);
+//	vicam_sndctrl(1, vicam, VICAM_REQ_CAMERA_POWER, 0x00, NULL, 0); Leave it on
+#endif
+
+	rvfree(vicam->fbuf, vicam->maxframesize * VICAM_NUMFRAMES);
+	vicam->fbuf = 0;
+	vicam->open_count=0;
+
+	up(&vicam->sem);
+	/* Why does se401.c have a usbdevice check here? */
+	/* If device is unplugged while open, I guess we only may unregister now */
+	MOD_DEC_USE_COUNT;
+}
+
+static long vicam_v4l_read(struct video_device *vdev, char *user_buf, unsigned long buflen, int noblock)
+{
+	//struct usb_vicam *vicam = (struct usb_vicam *)vdev;
+
+	dbg("vicam_v4l_read(%ld)", buflen);
+
+	if (!vdev || !buf)
+		return -EFAULT;
+
+	if (copy_to_user(user_buf, buf2, buflen))
+		return -EFAULT;
+	return buflen;
+}
+
+static long vicam_v4l_write(struct video_device *dev, const char *buf, unsigned long count, int noblock)
+{
+	info("vicam_v4l_write");
+	return -EINVAL;
+}
+
+static int vicam_v4l_ioctl(struct video_device *vdev, unsigned int cmd, void *arg)
+{
+	struct usb_vicam *vicam = (struct usb_vicam *)vdev;
+	int ret = -EL3RST;
+
+	if (!vicam->udev)
+		return -EIO;
+
+	down(&vicam->sem);
+
+	switch (cmd) {
+	case VIDIOCGCAP:
+	{
+		struct video_capability b;
+		ret = vicam_get_capability(vicam,&b);
+		dbg("name %s",b.name);
+		if (copy_to_user(arg, &b, sizeof(b)))
+			ret = -EFAULT;
+	}
+	case VIDIOCGFBUF:
+	{
+		struct video_buffer vb;
+		info("vicam_v4l_ioctl - VIDIOCGBUF - query frame buffer param");
+		/* frame buffer not supported, not used */
+		memset(&vb, 0, sizeof(vb));
+		vb.base = NULL;
+		
+		/* FIXME - VIDIOCGFBUF - why the void */
+		if (copy_to_user((void *)arg, (void *)&vb, sizeof(vb)))
+			ret = -EFAULT;
+		ret = 0;
+	}
+	case VIDIOCGWIN:
+	{
+		struct video_window vw;
+		ret = vicam_get_window(vicam, &vw);
+		if (copy_to_user(arg, &vw, sizeof(vw)))
+			ret = -EFAULT;
+	}
+	case VIDIOCSWIN:
+	{
+		struct video_window vw;
+		if (copy_from_user(&vw, arg, sizeof(vw)))
+			ret = -EFAULT;
+		else
+			ret = vicam_set_window(vicam, &vw);
+		return ret;
+	}
+	case VIDIOCGCHAN:
+	{
+		struct video_channel v;
+
+		if (copy_from_user(&v, arg, sizeof(v)))
+			ret = -EFAULT;
+		else {
+			ret = vicam_get_channel(vicam,&v);
+			if (copy_to_user(arg, &v, sizeof(v)))
+				ret = -EFAULT;
+		}
+	}
+	case VIDIOCSCHAN:
+	{
+		struct video_channel v;
+		if (copy_from_user(&v, arg, sizeof(v)))
+			ret = -EFAULT;
+		else
+			ret = vicam_set_channel(vicam,&v);
+ 	}
+	case VIDIOCGPICT:
+	{
+		struct video_picture p;
+		ret = vicam_get_picture(vicam, &p);
+		if (copy_to_user(arg, &p, sizeof(p)))
+			ret = -EFAULT;
+	}
+	case VIDIOCSPICT:
+	{
+		struct video_picture p;
+		if (copy_from_user(&p, arg, sizeof(p)))
+			ret = -EFAULT;
+		else
+			ret = vicam_set_picture(vicam, &p);
+	}
+	case VIDIOCGMBUF:
+	{
+		struct video_mbuf vm;
+		ret = vicam_get_mmapbuffer(vicam,&vm);
+		/* FIXME - VIDIOCGMBUF - why the void */
+		if (copy_to_user((void *)arg, (void *)&vm, sizeof(vm)))
+			ret = -EFAULT;
+	}
+	case VIDIOCMCAPTURE:
+	{
+		struct video_mmap vm;
+		ret = vicam_mmap_capture(vicam, &vm);
+		/* FIXME: This is probably not right */
+	}
+	case VIDIOCSYNC:
+	{
+		int frame;
+		/* FIXME - VIDIOCSYNC - why the void */
+		if (copy_from_user((void *)&frame, arg, sizeof(int)))
+			ret = -EFAULT;
+		else
+			ret = vicam_sync_frame(vicam,frame);
+	}
+
+	case VIDIOCKEY:
+		ret = 0;
+ 
+	case VIDIOCCAPTURE:
+	case VIDIOCSFBUF:
+	case VIDIOCGTUNER:
+	case VIDIOCSTUNER:
+	case VIDIOCGFREQ:
+	case VIDIOCSFREQ:
+	case VIDIOCGAUDIO:
+	case VIDIOCSAUDIO:
+	case VIDIOCGUNIT:
+		ret = -EINVAL;
+
+	default:
+	{
+		info("vicam_v4l_ioctl - %ui",cmd);
+		ret = -ENOIOCTLCMD;
+	}
+	} /* end switch */
+
+	up(&vicam->sem);
+        return ret;
+}
+
+static int vicam_v4l_mmap(struct video_device *dev, const char *adr, unsigned long size)
+{
+	struct usb_vicam *vicam = (struct usb_vicam *)dev;
+	unsigned long start = (unsigned long)adr;
+	unsigned long page, pos;
+
+	down(&vicam->sem);
+	
+	if (vicam->udev == NULL) {
+		up(&vicam->sem);
+		return -EIO;
+	}
+#if 0
+	if (size > (((VICAM_NUMFRAMES * vicam->maxframesize) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1))) {
+		up(&vicam->sem);
+		return -EINVAL;
+	}
+#endif
+	pos = (unsigned long)vicam->fbuf;
+	while (size > 0) {
+		page = kvirt_to_pa(pos);
+		if (remap_page_range(start, page, PAGE_SIZE, PAGE_SHARED)) {
+			up(&vicam->sem);
+			return -EAGAIN;
+		}
+		start += PAGE_SIZE;
+		pos += PAGE_SIZE;
+		if (size > PAGE_SIZE)
+			size -= PAGE_SIZE;
+		else
+			size = 0;
+	}
+	up(&vicam->sem);
+
+        return 0;
+}
+
+/* FIXME - vicam_v4l_init */
+static int vicam_v4l_init(struct video_device *dev)
+{
+	/* stick proc fs stuff in here if wanted */
+	dbg("vicam_v4l_init");
+	return 0;
+}
+
+/* FIXME - vicam_template - important */
+static struct video_device vicam_template = {
+	name:		"vicam USB camera",
+	type:		VID_TYPE_CAPTURE,
+	hardware:	VID_HARDWARE_SE401, /* need to ask for own id */
+	open:		vicam_v4l_open,
+	close:		vicam_v4l_close,
+	read:		vicam_v4l_read,
+	write:		vicam_v4l_write,
+	ioctl:		vicam_v4l_ioctl,
+	mmap:		vicam_v4l_mmap,
+	initialize:	vicam_v4l_init,
+};
+
+/******************************************************************************
+ *
+ *  Some Routines
+ *
+ ******************************************************************************/
+
+/*
+Flash the led
+vicam_sndctrl(1, vicam, VICAM_REQ_CAMERA_POWER, 0x01, NULL, 0);
+info ("led on");
+vicam_sndctrl(1, vicam, VICAM_REQ_LED_CONTROL, 0x01, NULL, 0);
+info ("led off");
+vicam_sndctrl(1, vicam, VICAM_REQ_LED_CONTROL, 0x00, NULL, 0);
+vicam_sndctrl(1, vicam, VICAM_REQ_CAMERA_POWER, 0x00, NULL, 0);
+*/
+
+static void vicam_bulk(struct urb *urb)
+{
+	struct usb_vicam *vicam = urb->context;
+
+	/*	if (!vicam || !vicam->dev || !vicam->used)
+		return;
+	*/
+
+	if (urb->status)
+		printk("vicam%d: nonzero read/write bulk status received: %d",
+			0, urb->status);
+
+	urb->actual_length = 0;
+	urb->dev = vicam->udev;
+
+	memcpy(buf2, buf+64, 0x1e480);
+	if (vicam->fbuf)
+		memcpy(vicam->fbuf, buf+64, 0x1e480);
+
+	if (!change_pending) {
+		if (usb_submit_urb(urb))
+			dbg("failed resubmitting read urb");
+	} else {
+		change_pending = 0;
+		wake_up_interruptible(&vicam->wait);
+	}
+}
+
+static int vicam_parameters(struct usb_vicam *vicam)
+{
+	unsigned char req[0x10];
+	unsigned int shutter;
+	shutter = 10;
+
+	switch (vicam->win.width) {
+	case 512:
+	default:
+		memcpy(req, s512x242bw, 0x10);
+		break;
+	case 256:
+		memcpy(req, s256x242bw, 0x10);
+		break;
+	case 128:
+		memcpy(req, s128x122bw, 0x10);
+		break;
+	}
+
+
+	mdelay(10);
+	vicam_sndctrl(1, vicam, VICAM_REQ_CAMERA_POWER, 0x01, NULL, 0);
+	info ("led on");
+	vicam_sndctrl(1, vicam, VICAM_REQ_LED_CONTROL, 0x01, NULL, 0);
+
+	mdelay(10);
+
+	shutter = vicam->win.contrast / 256;
+	if (shutter == 0)
+		shutter = 1;
+	printk("vicam_parameters: brightness %d, shutter %d\n", vicam->win.brightness, shutter );
+	req[0] = vicam->win.brightness /256;
+	shutter = 15600/shutter - 1;
+	req[6] = shutter & 0xff;
+	req[7] = (shutter >> 8) & 0xff;
+	vicam_sndctrl(1, vicam, VICAM_REQ_CAPTURE, 0x80, req, 0x10);
+	mdelay(10);
+	vicam_sndctrl(0, vicam, VICAM_REQ_GET_SOMETHIN, 0, buf, 0x10);
+	mdelay(10);
+
+	return 0;
+}
+
+static int vicam_init(struct usb_vicam *vicam)
+{
+	int width[] = {128, 256, 512};
+	int height[] = {122, 242, 242};
+
+	dbg("vicam_init");
+	buf = kmalloc(0x1e480, GFP_KERNEL);
+	buf2 = kmalloc(0x1e480, GFP_KERNEL);
+	if ((!buf) || (!buf2)) {
+		printk("Not enough memory for vicam!\n");
+		goto error;
+	}
+
+	/* do we do aspect correction in kernel or not? */
+	vicam->sizes = 3;
+	vicam->width = kmalloc(vicam->sizes*sizeof(int), GFP_KERNEL);
+	vicam->height = kmalloc(vicam->sizes*sizeof(int), GFP_KERNEL);
+	memcpy(vicam->width, &width, sizeof(width));
+	memcpy(vicam->height, &height, sizeof(height));
+	vicam->maxframesize = vicam->width[vicam->sizes-1] * vicam->height[vicam->sizes-1];
+
+	/* Download firmware to camera */
+	vicam_sndctrl(1, vicam, VICAM_REQ_VENDOR, 0, firmware1, sizeof(firmware1));
+	vicam_sndctrl(1, vicam, VICAM_REQ_VENDOR, 0, findex1, sizeof(findex1));
+	vicam_sndctrl(1, vicam, VICAM_REQ_VENDOR, 0, fsetup, sizeof(fsetup));
+	vicam_sndctrl(1, vicam, VICAM_REQ_VENDOR, 0, firmware2, sizeof(firmware2));
+	vicam_sndctrl(1, vicam, VICAM_REQ_VENDOR, 0, findex2, sizeof(findex2));
+	vicam_sndctrl(1, vicam, VICAM_REQ_VENDOR, 0, fsetup, sizeof(fsetup));
+
+	vicam_parameters(vicam);
+
+	FILL_BULK_URB(&vicam->readurb, vicam->udev, usb_rcvbulkpipe(vicam->udev, 0x81),
+		      buf, 0x1e480, vicam_bulk, vicam);
+	printk("Submiting urb: %d\n", usb_submit_urb(&vicam->readurb));
+
+	return 0;
+error:
+	if (buf)
+		kfree(buf);
+	if (buf2)
+		kfree(buf2);
+	return 1;
+}
+
+static void * __devinit vicam_probe(struct usb_device *udev, unsigned int ifnum,
+	const struct usb_device_id *id)
+{
+	struct usb_vicam *vicam;
+	char *camera_name=NULL;
+
+	dbg("vicam_probe");
+
+	/* See if the device offered us matches what we can accept */
+	if ((udev->descriptor.idVendor != USB_VICAM_VENDOR_ID) ||
+	    (udev->descriptor.idProduct != USB_VICAM_PRODUCT_ID)) {
+		return NULL;
+	}
+	
+	camera_name="3Com HomeConnect USB";
+	info("ViCAM camera found: %s", camera_name);
+	
+	vicam = kmalloc (sizeof(struct usb_vicam), GFP_KERNEL);
+	if (vicam == NULL) {
+		err ("couldn't kmalloc vicam struct");
+		return NULL;
+	}
+	memset(vicam, 0, sizeof(*vicam));
+	
+	vicam->udev = udev;
+	vicam->camera_name = camera_name;
+	vicam->win.brightness = 128;
+	vicam->win.contrast = 10;
+
+	/* FIXME */
+	if (vicam_init(vicam))
+		return NULL;
+	memcpy(&vicam->vdev, &vicam_template, sizeof(vicam_template));
+	memcpy(vicam->vdev.name, vicam->camera_name, strlen(vicam->camera_name));
+	
+	if (video_register_device(&vicam->vdev, VFL_TYPE_GRABBER, video_nr) == -1) {
+		err("video_register_device");
+		return NULL;
+	}
+
+	info("registered new video device: video%d", vicam->vdev.minor);
+	
+	init_MUTEX (&vicam->sem);
+	init_waitqueue_head(&vicam->wait);
+	
+	return vicam;
+}
+
+
+/* FIXME - vicam_disconnect - important */
+static void vicam_disconnect(struct usb_device *udev, void *ptr)
+{
+	struct usb_vicam *vicam;
+
+	vicam = (struct usb_vicam *) ptr;
+
+	if (!vicam->open_count)
+		video_unregister_device(&vicam->vdev);
+	vicam->udev = NULL;
+/*
+	vicam->frame[0].grabstate = FRAME_ERROR;
+	vicam->frame[1].grabstate = FRAME_ERROR;
+*/
+
+	/* Free buffers and shit */
+
+	info("%s disconnected", vicam->camera_name);
+	synchronize(vicam);
+
+	if (!vicam->open_count) {
+		/* Other random junk */
+		kfree(vicam);
+		vicam = NULL;
+	}
+}
+
+/* usb specific object needed to register this driver with the usb subsystem */
+static struct usb_driver vicam_driver = {
+	name:		"vicam",
+	probe:		vicam_probe,
+	disconnect:	vicam_disconnect,
+	id_table:	vicam_table,
+};
+
+/******************************************************************************
+ *
+ *  Module Routines
+ *
+ ******************************************************************************/
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/* Module paramaters */
+MODULE_PARM(debug, "i");
+MODULE_PARM_DESC(debug, "Debug enabled or not");
+
+static int __init usb_vicam_init(void)
+{
+	int result;
+
+	printk("VICAM: initializing\n");
+	/* register this driver with the USB subsystem */
+	result = usb_register(&vicam_driver);
+	if (result < 0) {
+		err("usb_register failed for the "__FILE__" driver. Error number %d",
+		    result);
+		return -1;
+	}
+
+	info(DRIVER_VERSION " " DRIVER_AUTHOR);
+	info(DRIVER_DESC);
+	return 0;
+}
+
+static void __exit usb_vicam_exit(void)
+{
+	/* deregister this driver with the USB subsystem */
+	usb_deregister(&vicam_driver);
+}
+
+module_init(usb_vicam_init);
+module_exit(usb_vicam_exit);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/usb/vicam.h linux/drivers/usb/vicam.h
--- linux.orig/drivers/usb/vicam.h	Thu Jan  1 00:00:00 1970
+++ linux/drivers/usb/vicam.h	Wed Dec 26 15:09:47 2001
@@ -0,0 +1,81 @@
+/*
+ *
+ * Vista Imaging ViCAM / 3Com HomeConnect Usermode Driver
+ * Christopher L Cheney (C) 2001
+ * 
+ */
+
+#ifndef __LINUX_VICAM_H
+#define __LINUX_VICAM_H
+
+
+#ifdef CONFIG_USB_DEBUG
+	static int debug = 1;
+#else
+	static int debug;
+#endif
+
+/* Use our own dbg macro */
+#undef dbg
+#define dbg(format, arg...) do { if (debug) printk(KERN_DEBUG __FILE__ ": " format "\n" , ## arg); } while (0)
+
+#define VICAM_NUMFRAMES 30
+#define VICAM_NUMSBUF 1
+
+/* USB REQUEST NUMBERS */
+#define VICAM_REQ_VENDOR	0xff
+#define VICAM_REQ_CAMERA_POWER	0x50
+#define VICAM_REQ_CAPTURE	0x51
+#define VICAM_REQ_LED_CONTROL	0x55
+#define VICAM_REQ_GET_SOMETHIN	0x56
+ 
+/* not required but lets you know camera is on */
+/* camera must be on to turn on led */
+/* 0x01 always on  0x03 on when picture taken (flashes) */
+
+struct picture_parm
+{
+	int width;
+	int height;
+	int brightness;
+	int hue;
+	int colour;
+	int contrast;
+	int whiteness;
+	int depth;
+	int palette;
+};
+
+struct vicam_scratch {
+        unsigned char *data;
+        volatile int state;
+        int offset;
+        int length;
+};
+
+/* Structure to hold all of our device specific stuff */
+struct usb_vicam
+{
+	struct video_device vdev;
+	struct usb_device *udev;
+
+	int open_count;	/* number of times this port has been opened */
+	struct semaphore sem;			/* locks this structure */
+	wait_queue_head_t wait;			/* Processes waiting */ 
+
+	int streaming;
+
+	/* v4l stuff */
+	char *camera_name;
+	char *fbuf;
+	urb_t *urb[VICAM_NUMSBUF];
+	int sizes;
+	int *width;
+	int *height;
+	int maxframesize;
+	struct picture_parm win;
+	struct proc_dir_entry *proc_entry;      /* /proc/se401/videoX */
+	struct urb readurb;
+};
+
+#endif
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/usb/vicamurbs.h linux/drivers/usb/vicamurbs.h
--- linux.orig/drivers/usb/vicamurbs.h	Thu Jan  1 00:00:00 1970
+++ linux/drivers/usb/vicamurbs.h	Wed Dec 26 15:09:47 2001
@@ -0,0 +1,330 @@
+/*
+ *
+ * Vista Imaging ViCAM / 3Com HomeConnect Usermode Driver
+ * Christopher L Cheney (C) 2001
+ * 
+ */
+
+
+#ifndef __LINUX_VICAMURBS_H
+#define __LINUX_VICAMURBS_H
+
+/* -------------------------------------------------------------------------- */
+
+/* FIXME - Figure out transfers so that this doesn't need to be here
+ *
+ * Notice: in pieces below, "0" means other code will fill it while "0x00" means this is zero */
+
+/* Request 0x51 Image Setup */
+
+/* 128x98 ? 0x3180 size */
+static unsigned char s128x98bw[] = {
+  0,    0x34, 0xC4, 0x00, 0x00, 0x00, 0,    0,
+  0x18, 0x02, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00
+};
+
+/* 128x122 3D80 size */
+static unsigned char s128x122bw[] = {
+  0,    0x34, 0xF4, 0x00, 0x00, 0x00, 0,    0,
+  0x00, 0x02, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00
+};
+
+/* 256x242 ? 0xF280 size */
+static unsigned char s256x242bw[] = {
+  0,    0x03, 0xC8, 0x03, 0x00, 0x00, 0,    0,
+  0x00, 0x04, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00
+};
+
+/* 512x242 0x1E480 size */
+static unsigned char s512x242bw[] = {
+  0,    0x05, 0x90, 0x07, 0x00, 0x00, 0,    0,
+  0x00, 0x08, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00
+};
+
+/* In s512x242:
+   byte 0: gain -- higher number means brighter image
+   byte 6, 7: shutter speed, little-endian; set this to 15600 * (shutter speed) - 1. (Where shutter speed is something like 1/1000).
+*/
+
+/* -------------------------------------------------------------------------- */
+
+static unsigned char fsetup[] = {
+  0xB6, 0xC3, 0x01, 0x00, 0x06, 0x64,
+
+  0x00, 0x00
+};
+
+static unsigned char firmware1[] = {
+  0xB6, 0xC3, 0x1F, 0x00, 0x02, 0x64,
+
+  0xE7, 0x67, 0xFD, 0xFF, 0x0E, 0xC0, 0xE7, 0x09,
+  0xDE, 0x00, 0x8E, 0x00, 0xC0, 0x09, 0x40, 0x03,
+  0xC0, 0x17, 0x44, 0x03, 0x4B, 0xAF, 0xC0, 0x07,
+  0x00, 0x00, 0x4B, 0xAF, 0x97, 0xCF, 0x00, 0x00
+};
+
+static unsigned char findex1[] = {
+  0xB6, 0xC3, 0x03, 0x00, 0x03, 0x64,
+
+  0x18, 0x00, 0x00, 0x00
+};
+
+static unsigned char firmware2[] = {
+  0xB6, 0xC3, 0x8F, 0x06, 0x02, 0x64,
+
+  0xE7, 0x07, 0x00, 0x00, 0x08, 0xC0, 0xE7, 0x07,
+  0x00, 0x00, 0x3E, 0xC0, 0xE7, 0x07, 0x54, 0x01,
+  0xAA, 0x00, 0xE7, 0x07, 0xC8, 0x05, 0xB6, 0x00,
+  0xE7, 0x07, 0x42, 0x01, 0xD2, 0x00, 0xE7, 0x07,
+  0x7C, 0x00, 0x16, 0x00, 0xE7, 0x07, 0x56, 0x00,
+  0x18, 0x00, 0xE7, 0x07, 0x06, 0x00, 0x92, 0xC0,
+  0xE7, 0x07, 0x00, 0x00, 0x1E, 0xC0, 0xE7, 0x07,
+  0xFF, 0xFF, 0x22, 0xC0, 0xE7, 0x07, 0x04, 0x00,
+  0x24, 0xC0, 0xE7, 0x07, 0xEC, 0x27, 0x28, 0xC0,
+  0xE7, 0x07, 0x16, 0x01, 0x8E, 0x00, 0xE7, 0x87,
+  0x01, 0x00, 0x0E, 0xC0, 0x97, 0xCF, 0xD7, 0x09,
+  0x00, 0xC0, 0xE7, 0x77, 0x01, 0x00, 0x92, 0xC0,
+  0x09, 0xC1, 0xE7, 0x09, 0xFE, 0x05, 0x24, 0x01,
+  0xE7, 0x09, 0x04, 0x06, 0x26, 0x01, 0xE7, 0x07,
+  0x07, 0x00, 0x92, 0xC0, 0xE7, 0x05, 0x00, 0xC0,
+  0xC0, 0xDF, 0x97, 0xCF, 0x17, 0x00, 0x57, 0x00,
+  0x17, 0x02, 0xD7, 0x09, 0x00, 0xC0, 0xE7, 0x77,
+  0x01, 0x00, 0x92, 0xC0, 0x0A, 0xC1, 0xE7, 0x57,
+  0xFF, 0xFF, 0xFA, 0x05, 0x0D, 0xC0, 0xE7, 0x57,
+  0x00, 0x00, 0xFA, 0x05, 0x0F, 0xC0, 0x9F, 0xAF,
+  0xC6, 0x00, 0xE7, 0x05, 0x00, 0xC0, 0xC8, 0x05,
+  0xC1, 0x05, 0xC0, 0x05, 0xC0, 0xDF, 0x97, 0xCF,
+  0x27, 0xDA, 0xFA, 0x05, 0xEF, 0x07, 0x01, 0x00,
+  0x0B, 0x06, 0x73, 0xCF, 0x9F, 0xAF, 0x78, 0x01,
+  0x9F, 0xAF, 0x1A, 0x03, 0x6E, 0xCF, 0xE7, 0x09,
+  0xFC, 0x05, 0x24, 0x01, 0xE7, 0x09, 0x02, 0x06,
+  0x26, 0x01, 0xE7, 0x07, 0x07, 0x00, 0x92, 0xC0,
+  0xE7, 0x09, 0xFC, 0x05, 0xFE, 0x05, 0xE7, 0x09,
+  0x02, 0x06, 0x04, 0x06, 0xE7, 0x09, 0x00, 0x06,
+  0xFC, 0x05, 0xE7, 0x09, 0xFE, 0x05, 0x00, 0x06,
+  0x27, 0xDA, 0xFA, 0x05, 0xE7, 0x57, 0x01, 0x00,
+  0xFA, 0x05, 0x02, 0xCA, 0x04, 0xC0, 0x97, 0xCF,
+  0x9F, 0xAF, 0x66, 0x05, 0x97, 0xCF, 0xE7, 0x07,
+  0x40, 0x00, 0x02, 0x06, 0xC8, 0x09, 0xFC, 0x05,
+  0x9F, 0xAF, 0xDA, 0x02, 0x97, 0xCF, 0xCF, 0x17,
+  0x02, 0x00, 0xEF, 0x57, 0x81, 0x00, 0x09, 0x06,
+  0x9F, 0xA0, 0xB6, 0x01, 0xEF, 0x57, 0x80, 0x00,
+  0x09, 0x06, 0x9F, 0xA0, 0x40, 0x02, 0xEF, 0x57,
+  0x01, 0x00, 0x0B, 0x06, 0x9F, 0xA0, 0x46, 0x03,
+  0xE7, 0x07, 0x01, 0x00, 0x0A, 0xC0, 0x46, 0xAF,
+  0x47, 0xAF, 0x9F, 0xAF, 0x40, 0x02, 0xE7, 0x07,
+  0x2E, 0x00, 0x0A, 0xC0, 0xEF, 0x87, 0x80, 0x00,
+  0x09, 0x06, 0x97, 0xCF, 0x00, 0x0E, 0x01, 0x00,
+  0xC0, 0x57, 0x51, 0x00, 0x9F, 0xC0, 0x9E, 0x02,
+  0xC0, 0x57, 0x50, 0x00, 0x20, 0xC0, 0xC0, 0x57,
+  0x55, 0x00, 0x12, 0xC0, 0xC0, 0x57, 0x56, 0x00,
+  0x9F, 0xC0, 0x72, 0x02, 0x9F, 0xCF, 0xD6, 0x02,
+  0xC1, 0x0B, 0x08, 0x06, 0x01, 0xD0, 0x6F, 0x90,
+  0x08, 0x06, 0xC0, 0x07, 0x08, 0x00, 0xC1, 0x0B,
+  0x08, 0x06, 0x9F, 0xAF, 0x28, 0x05, 0x97, 0xCF,
+  0x2F, 0x0E, 0x02, 0x00, 0x08, 0x06, 0xC0, 0x07,
+  0x08, 0x00, 0xC1, 0x0B, 0x08, 0x06, 0x9F, 0xAF,
+  0x28, 0x05, 0x9F, 0xCF, 0xD6, 0x02, 0x2F, 0x0E,
+  0x02, 0x00, 0x09, 0x06, 0xEF, 0x87, 0x80, 0x00,
+  0x09, 0x06, 0x9F, 0xCF, 0xD6, 0x02, 0xEF, 0x67,
+  0x7F, 0xFF, 0x09, 0x06, 0xE7, 0x67, 0xFF, 0xFD,
+  0x22, 0xC0, 0xE7, 0x67, 0xEF, 0xFF, 0x24, 0xC0,
+  0xE7, 0x87, 0x10, 0x00, 0x28, 0xC0, 0x9F, 0xAF,
+  0xB8, 0x05, 0xE7, 0x87, 0xE0, 0x21, 0x24, 0xC0,
+  0x9F, 0xAF, 0xA8, 0x05, 0xE7, 0x87, 0x08, 0x00,
+  0x24, 0xC0, 0xE7, 0x67, 0xDF, 0xFF, 0x24, 0xC0,
+  0xC8, 0x07, 0x0A, 0x00, 0xC0, 0x07, 0x00, 0x00,
+  0xC1, 0x07, 0x01, 0x00, 0x9F, 0xAF, 0x28, 0x05,
+  0x9F, 0xAF, 0xB8, 0x05, 0xC0, 0x07, 0x9E, 0x00,
+  0x9F, 0xAF, 0x44, 0x05, 0xE7, 0x67, 0xFF, 0xFE,
+  0x24, 0xC0, 0xC0, 0x09, 0x20, 0xC0, 0xE7, 0x87,
+  0x00, 0x01, 0x24, 0xC0, 0xC0, 0x77, 0x00, 0x02,
+  0x0F, 0xC1, 0xE7, 0x67, 0xF7, 0xFF, 0x24, 0xC0,
+  0xE7, 0x67, 0xF7, 0xFF, 0x24, 0xC0, 0xE7, 0x87,
+  0x08, 0x00, 0x24, 0xC0, 0x08, 0xDA, 0x5E, 0xC1,
+  0xEF, 0x07, 0x80, 0x00, 0x09, 0x06, 0x97, 0xCF,
+  0xEF, 0x07, 0x01, 0x00, 0x0A, 0x06, 0x97, 0xCF,
+  0xEF, 0x07, 0x00, 0x00, 0x0B, 0x06, 0xEF, 0x07,
+  0x00, 0x00, 0x0A, 0x06, 0xEF, 0x67, 0x7F, 0xFF,
+  0x09, 0x06, 0xEF, 0x07, 0x00, 0x00, 0x0D, 0x06,
+  0xE7, 0x67, 0xEF, 0xFF, 0x28, 0xC0, 0xE7, 0x67,
+  0x17, 0xD8, 0x24, 0xC0, 0xE7, 0x07, 0x00, 0x00,
+  0x1E, 0xC0, 0xE7, 0x07, 0xFF, 0xFF, 0x22, 0xC0,
+  0x97, 0xCF, 0xC8, 0x07, 0x0E, 0x06, 0x9F, 0xAF,
+  0xDA, 0x02, 0xE7, 0x07, 0x00, 0x00, 0xF2, 0x05,
+  0xE7, 0x07, 0x10, 0x00, 0xF6, 0x05, 0xE7, 0x07,
+  0x0E, 0x06, 0xF4, 0x05, 0xE7, 0x07, 0xD6, 0x02,
+  0xF8, 0x05, 0xC8, 0x07, 0xF2, 0x05, 0xC1, 0x07,
+  0x00, 0x80, 0x50, 0xAF, 0x97, 0xCF, 0x2F, 0x0C,
+  0x02, 0x00, 0x07, 0x06, 0x2F, 0x0C, 0x04, 0x00,
+  0x06, 0x06, 0xE7, 0x07, 0x00, 0x00, 0xF2, 0x05,
+  0xE7, 0x07, 0x10, 0x00, 0xF6, 0x05, 0xE7, 0x07,
+  0xE2, 0x05, 0xF4, 0x05, 0xE7, 0x07, 0xCE, 0x02,
+  0xF8, 0x05, 0xC8, 0x07, 0xF2, 0x05, 0xC1, 0x07,
+  0x00, 0x80, 0x51, 0xAF, 0x97, 0xCF, 0x9F, 0xAF,
+  0x66, 0x04, 0x9F, 0xAF, 0x1A, 0x03, 0x59, 0xAF,
+  0x97, 0xCF, 0xC0, 0x07, 0x0E, 0x00, 0xC1, 0x0B,
+  0x0C, 0x06, 0x41, 0xD1, 0x9F, 0xAF, 0x28, 0x05,
+  0xC0, 0x07, 0x3C, 0x00, 0x9F, 0xAF, 0x44, 0x05,
+  0x68, 0x00, 0xC0, 0x07, 0x3B, 0x00, 0x9F, 0xAF,
+  0x44, 0x05, 0x6F, 0x00, 0x0C, 0x06, 0x68, 0x00,
+  0xE0, 0x07, 0x04, 0x01, 0xE8, 0x0B, 0x0A, 0x06,
+  0xE8, 0x07, 0x00, 0x00, 0xE0, 0x07, 0x00, 0x02,
+  0xE0, 0x07, 0xEC, 0x01, 0xE0, 0x07, 0xFC, 0xFF,
+  0x97, 0xCF, 0xE7, 0x07, 0xFF, 0xFF, 0xFA, 0x05,
+  0xEF, 0x07, 0x00, 0x00, 0x0B, 0x06, 0xE7, 0x07,
+  0x0E, 0x06, 0x24, 0x01, 0xE7, 0x07, 0x0E, 0x06,
+  0xFE, 0x05, 0xE7, 0x07, 0x40, 0x00, 0x26, 0x01,
+  0xE7, 0x07, 0x40, 0x00, 0x04, 0x06, 0xE7, 0x07,
+  0x07, 0x00, 0x92, 0xC0, 0x97, 0xCF, 0xEF, 0x07,
+  0x02, 0x00, 0x0B, 0x06, 0x9F, 0xAF, 0x78, 0x01,
+  0xEF, 0x77, 0x80, 0x00, 0x07, 0x06, 0x9F, 0xC0,
+  0x14, 0x04, 0xEF, 0x77, 0x01, 0x00, 0x07, 0x06,
+  0x37, 0xC0, 0xEF, 0x77, 0x01, 0x00, 0x0D, 0x06,
+  0x0F, 0xC1, 0xEF, 0x07, 0x01, 0x00, 0x0D, 0x06,
+  0xC0, 0x07, 0x02, 0x00, 0xC1, 0x07, 0x30, 0x00,
+  0x9F, 0xAF, 0x28, 0x05, 0xC0, 0x07, 0x01, 0x00,
+  0xC1, 0x07, 0x02, 0x00, 0x9F, 0xAF, 0x28, 0x05,
+  0xC8, 0x07, 0xFF, 0x4F, 0x9F, 0xAF, 0xA8, 0x05,
+  0xC0, 0x07, 0x38, 0x00, 0x9F, 0xAF, 0x44, 0x05,
+  0xC1, 0x77, 0x03, 0x00, 0x02, 0xC1, 0x08, 0xDA,
+  0x75, 0xC1, 0xC1, 0x77, 0x01, 0x00, 0x0A, 0xC1,
+  0xC0, 0x07, 0x01, 0x00, 0xC1, 0x07, 0x02, 0x00,
+  0x9F, 0xAF, 0x28, 0x05, 0xEF, 0x07, 0x01, 0x00,
+  0x06, 0x06, 0x2C, 0xCF, 0xC0, 0x07, 0x01, 0x00,
+  0xC1, 0x07, 0x04, 0x00, 0x9F, 0xAF, 0x28, 0x05,
+  0xEF, 0x07, 0x00, 0x00, 0x06, 0x06, 0x22, 0xCF,
+  0xEF, 0x07, 0x00, 0x00, 0x0D, 0x06, 0xEF, 0x57,
+  0x01, 0x00, 0x06, 0x06, 0x1B, 0xC0, 0xC0, 0x07,
+  0x01, 0x00, 0xC1, 0x07, 0x01, 0x00, 0x9F, 0xAF,
+  0x28, 0x05, 0xC0, 0x07, 0x02, 0x00, 0xC1, 0x07,
+  0x30, 0x00, 0x9F, 0xAF, 0x28, 0x05, 0xC8, 0x07,
+  0xFF, 0x4F, 0x9F, 0xAF, 0xA8, 0x05, 0xC0, 0x07,
+  0x38, 0x00, 0x9F, 0xAF, 0x44, 0x05, 0xC1, 0x67,
+  0x03, 0x00, 0xC1, 0x57, 0x03, 0x00, 0x02, 0xC0,
+  0x08, 0xDA, 0x73, 0xC1, 0xC0, 0x07, 0x02, 0x00,
+  0xC1, 0x07, 0x12, 0x00, 0xEF, 0x57, 0x00, 0x00,
+  0x06, 0x06, 0x02, 0xC0, 0xC1, 0x07, 0x23, 0x00,
+  0x9F, 0xAF, 0x28, 0x05, 0xC0, 0x07, 0x14, 0x00,
+  0xC1, 0x0B, 0xEA, 0x05, 0x9F, 0xAF, 0x28, 0x05,
+  0xC0, 0x07, 0x3E, 0x00, 0x9F, 0xAF, 0x0A, 0x05,
+  0xE7, 0x09, 0xE4, 0x05, 0xFA, 0x05, 0x27, 0xD8,
+  0xFA, 0x05, 0xE7, 0x07, 0x0E, 0x06, 0xFC, 0x05,
+  0xE7, 0x07, 0x4E, 0x06, 0x00, 0x06, 0xE7, 0x07,
+  0x40, 0x00, 0x02, 0x06, 0x9F, 0xAF, 0x66, 0x05,
+  0x9F, 0xAF, 0xC6, 0x00, 0x97, 0xCF, 0xC1, 0x0B,
+  0xE2, 0x05, 0x41, 0xD0, 0x01, 0xD2, 0xC1, 0x17,
+  0x23, 0x00, 0x9F, 0xAF, 0xDC, 0x04, 0xC0, 0x07,
+  0x04, 0x00, 0xC1, 0x0B, 0xE3, 0x05, 0x9F, 0xAF,
+  0x28, 0x05, 0xC0, 0x07, 0x06, 0x00, 0xC1, 0x09,
+  0xE6, 0x05, 0x9F, 0xAF, 0x28, 0x05, 0xC0, 0x07,
+  0x07, 0x00, 0xC1, 0x09, 0xE6, 0x05, 0xC1, 0xD1,
+  0x9F, 0xAF, 0x28, 0x05, 0xC0, 0x07, 0x0B, 0x00,
+  0xC1, 0x09, 0xE8, 0x05, 0x9F, 0xAF, 0x28, 0x05,
+  0xC0, 0x07, 0x0C, 0x00, 0xC1, 0x09, 0xE8, 0x05,
+  0xC1, 0xD1, 0x9F, 0xAF, 0x28, 0x05, 0xC0, 0x07,
+  0x0D, 0x00, 0xC1, 0x07, 0x09, 0x00, 0x9F, 0xAF,
+  0x28, 0x05, 0xC0, 0x07, 0x03, 0x00, 0xC1, 0x07,
+  0x32, 0x00, 0x9F, 0xAF, 0x28, 0x05, 0xC0, 0x07,
+  0x0F, 0x00, 0xC1, 0x07, 0x00, 0x00, 0x9F, 0xAF,
+  0x28, 0x05, 0x97, 0xCF, 0xE7, 0x67, 0xFF, 0xD9,
+  0x24, 0xC0, 0xC8, 0x07, 0x0A, 0x00, 0x40, 0x00,
+  0xC0, 0x67, 0x00, 0x02, 0x27, 0x80, 0x24, 0xC0,
+  0xE7, 0x87, 0x00, 0x04, 0x24, 0xC0, 0xE7, 0x67,
+  0xFF, 0xF9, 0x24, 0xC0, 0x01, 0xD2, 0x08, 0xDA,
+  0x72, 0xC1, 0xE7, 0x87, 0x00, 0x20, 0x24, 0xC0,
+  0x97, 0xCF, 0x27, 0x00, 0x1E, 0xC0, 0xE7, 0x87,
+  0xFF, 0x00, 0x22, 0xC0, 0xE7, 0x67, 0x7F, 0xFF,
+  0x24, 0xC0, 0xE7, 0x87, 0x80, 0x00, 0x24, 0xC0,
+  0xE7, 0x87, 0x80, 0x00, 0x24, 0xC0, 0x97, 0xCF,
+  0x9F, 0xAF, 0x0A, 0x05, 0x67, 0x00, 0x1E, 0xC0,
+  0xE7, 0x67, 0xBF, 0xFF, 0x24, 0xC0, 0xE7, 0x87,
+  0x40, 0x00, 0x24, 0xC0, 0xE7, 0x87, 0x40, 0x00,
+  0x24, 0xC0, 0x97, 0xCF, 0x9F, 0xAF, 0x0A, 0x05,
+  0xE7, 0x67, 0x00, 0xFF, 0x22, 0xC0, 0xE7, 0x67,
+  0xFF, 0xFE, 0x24, 0xC0, 0xE7, 0x67, 0xFF, 0xFE,
+  0x24, 0xC0, 0xC1, 0x09, 0x20, 0xC0, 0xE7, 0x87,
+  0x00, 0x01, 0x24, 0xC0, 0x97, 0xCF, 0xC0, 0x07,
+  0x40, 0x00, 0xC8, 0x09, 0xFC, 0x05, 0xE7, 0x67,
+  0x00, 0xFF, 0x22, 0xC0, 0xE7, 0x67, 0xFF, 0xFE,
+  0x24, 0xC0, 0xE7, 0x67, 0xBF, 0xFF, 0x24, 0xC0,
+  0xE7, 0x67, 0xBF, 0xFF, 0x24, 0xC0, 0x00, 0xDA,
+  0xE8, 0x09, 0x20, 0xC0, 0xE7, 0x87, 0x40, 0x00,
+  0x24, 0xC0, 0xE7, 0x87, 0x40, 0x00, 0x24, 0xC0,
+  0x00, 0xDA, 0xE8, 0x09, 0x20, 0xC0, 0x6D, 0xC1,
+  0xE7, 0x87, 0x00, 0x01, 0x24, 0xC0, 0x97, 0xCF,
+  0xE7, 0x07, 0x32, 0x00, 0x12, 0xC0, 0xE7, 0x77,
+  0x00, 0x80, 0x12, 0xC0, 0x7C, 0xC0, 0x97, 0xCF,
+  0xE7, 0x07, 0x20, 0x4E, 0x12, 0xC0, 0xE7, 0x77,
+  0x00, 0x80, 0x12, 0xC0, 0x7C, 0xC0, 0x97, 0xCF,
+  0x09, 0x02, 0x19, 0x00, 0x01, 0x01, 0x00, 0x80,
+  0x96, 0x09, 0x04, 0x00, 0x00, 0x01, 0x00, 0x00,
+  0x00, 0x00, 0x07, 0x05, 0x81, 0x02, 0x40, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static unsigned char findex2[] = {
+  0xB6, 0xC3, 0x2F, 0x01, 0x03, 0x64,
+
+  0x0E, 0x00, 0x14, 0x00, 0x1A, 0x00, 0x20, 0x00,
+  0x26, 0x00, 0x4A, 0x00, 0x64, 0x00, 0x6A, 0x00,
+  0x92, 0x00, 0x9A, 0x00, 0xA0, 0x00, 0xB2, 0x00,
+  0xB8, 0x00, 0xBE, 0x00, 0xC2, 0x00, 0xC8, 0x00,
+  0xCE, 0x00, 0xDC, 0x00, 0xDA, 0x00, 0xE2, 0x00,
+  0xE0, 0x00, 0xE8, 0x00, 0xE6, 0x00, 0xEE, 0x00,
+  0xEC, 0x00, 0xF2, 0x00, 0xF8, 0x00, 0x02, 0x01,
+  0x0A, 0x01, 0x0E, 0x01, 0x12, 0x01, 0x1E, 0x01,
+  0x22, 0x01, 0x28, 0x01, 0x2C, 0x01, 0x32, 0x01,
+  0x36, 0x01, 0x44, 0x01, 0x50, 0x01, 0x5E, 0x01,
+  0x72, 0x01, 0x76, 0x01, 0x7A, 0x01, 0x80, 0x01,
+  0x88, 0x01, 0x8C, 0x01, 0x94, 0x01, 0x9C, 0x01,
+  0xA0, 0x01, 0xA4, 0x01, 0xAA, 0x01, 0xB0, 0x01,
+  0xB4, 0x01, 0xBA, 0x01, 0xD0, 0x01, 0xDA, 0x01,
+  0xF6, 0x01, 0xFA, 0x01, 0x02, 0x02, 0x34, 0x02,
+  0x3C, 0x02, 0x44, 0x02, 0x4A, 0x02, 0x50, 0x02,
+  0x56, 0x02, 0x74, 0x02, 0x78, 0x02, 0x7E, 0x02,
+  0x84, 0x02, 0x8A, 0x02, 0x88, 0x02, 0x90, 0x02,
+  0x8E, 0x02, 0x94, 0x02, 0xA2, 0x02, 0xA8, 0x02,
+  0xAE, 0x02, 0xB4, 0x02, 0xBA, 0x02, 0xB8, 0x02,
+  0xC0, 0x02, 0xBE, 0x02, 0xC4, 0x02, 0xD0, 0x02,
+  0xD4, 0x02, 0xE0, 0x02, 0xE6, 0x02, 0xEE, 0x02,
+  0xF8, 0x02, 0xFC, 0x02, 0x06, 0x03, 0x1E, 0x03,
+  0x24, 0x03, 0x28, 0x03, 0x30, 0x03, 0x2E, 0x03,
+  0x3C, 0x03, 0x4A, 0x03, 0x4E, 0x03, 0x54, 0x03,
+  0x58, 0x03, 0x5E, 0x03, 0x66, 0x03, 0x6E, 0x03,
+  0x7A, 0x03, 0x86, 0x03, 0x8E, 0x03, 0x96, 0x03,
+  0xB2, 0x03, 0xB8, 0x03, 0xC6, 0x03, 0xCC, 0x03,
+  0xD4, 0x03, 0xDA, 0x03, 0xE8, 0x03, 0xF4, 0x03,
+  0xFC, 0x03, 0x04, 0x04, 0x20, 0x04, 0x2A, 0x04,
+  0x32, 0x04, 0x36, 0x04, 0x3E, 0x04, 0x44, 0x04,
+  0x42, 0x04, 0x48, 0x04, 0x4E, 0x04, 0x4C, 0x04,
+  0x54, 0x04, 0x52, 0x04, 0x5A, 0x04, 0x5E, 0x04,
+  0x62, 0x04, 0x68, 0x04, 0x74, 0x04, 0x7C, 0x04,
+  0x80, 0x04, 0x88, 0x04, 0x8C, 0x04, 0x94, 0x04,
+  0x9A, 0x04, 0xA2, 0x04, 0xA6, 0x04, 0xAE, 0x04,
+  0xB4, 0x04, 0xC0, 0x04, 0xCC, 0x04, 0xD8, 0x04,
+  0x2A, 0x05, 0x46, 0x05, 0x6C, 0x05, 0x00, 0x00
+};
+
+#endif
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/video/aty/atyfb_base.c linux/drivers/video/aty/atyfb_base.c
--- linux.orig/drivers/video/aty/atyfb_base.c	Mon Jan  7 23:41:09 2002
+++ linux/drivers/video/aty/atyfb_base.c	Fri Dec 21 19:43:44 2001
@@ -366,6 +366,7 @@
 
 #if defined(CONFIG_FB_ATY_GX) || defined(CONFIG_FB_ATY_CT)
 static char ram_dram[] __initdata = "DRAM";
+static char ram_resv[] __initdata = "RESV";
 #endif /* CONFIG_FB_ATY_GX || CONFIG_FB_ATY_CT */
 
 #ifdef CONFIG_FB_ATY_GX
@@ -378,7 +379,6 @@
 static char ram_sgram[] __initdata = "SGRAM";
 static char ram_wram[] __initdata = "WRAM";
 static char ram_off[] __initdata = "OFF";
-static char ram_resv[] __initdata = "RESV";
 #endif /* CONFIG_FB_ATY_CT */
 
 #ifdef CONFIG_FB_ATY_GX
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/video/aty128.h linux/drivers/video/aty128.h
--- linux.orig/drivers/video/aty128.h	Sun Sep 17 16:48:04 2000
+++ linux/drivers/video/aty128.h	Wed Dec 26 18:13:49 2001
@@ -13,6 +13,7 @@
 #define CLOCK_CNTL_DATA				0x000c
 #define BIOS_0_SCRATCH				0x0010
 #define BUS_CNTL				0x0030
+#define BUS_CNTL1				0x0034
 #define GEN_INT_CNTL				0x0040
 #define CRTC_GEN_CNTL				0x0050
 #define CRTC_EXT_CNTL				0x0054
@@ -24,6 +25,7 @@
 #define GEN_RESET_CNTL				0x00f0
 #define CONFIG_MEMSIZE				0x00f8
 #define MEM_CNTL				0x0140
+#define MEM_POWER_MISC				0x015c
 #define AGP_BASE				0x0170
 #define AGP_CNTL				0x0174
 #define AGP_APER_OFFSET				0x0178
@@ -37,6 +39,9 @@
 #define CRTC_H_SYNC_STRT_WID			0x0204
 #define CRTC_V_TOTAL_DISP			0x0208
 #define CRTC_V_SYNC_STRT_WID			0x020c
+#define CRTC_VLINE_CRNT_VLINE			0x0210
+#define CRTC_CRNT_FRAME				0x0214
+#define CRTC_GUI_TRIG_VLINE			0x0218
 #define CRTC_OFFSET				0x0224
 #define CRTC_OFFSET_CNTL			0x0228
 #define CRTC_PITCH				0x022c
@@ -48,6 +53,20 @@
 #define DDA_ON_OFF				0x02e4
 #define VGA_DDA_CONFIG				0x02e8
 #define VGA_DDA_ON_OFF				0x02ec
+#define CRTC2_H_TOTAL_DISP			0x0300
+#define CRTC2_H_SYNC_STRT_WID			0x0304
+#define CRTC2_V_TOTAL_DISP			0x0308
+#define CRTC2_V_SYNC_STRT_WID			0x030c
+#define CRTC2_VLINE_CRNT_VLINE			0x0310
+#define CRTC2_CRNT_FRAME			0x0314
+#define CRTC2_GUI_TRIG_VLINE			0x0318
+#define CRTC2_OFFSET				0x0324
+#define CRTC2_OFFSET_CNTL			0x0328
+#define CRTC2_PITCH				0x032c
+#define DDA2_CONFIG				0x03e0
+#define DDA2_ON_OFF				0x03e4
+#define CRTC2_GEN_CNTL				0x03f8
+#define CRTC2_STATUS				0x03fc
 #define OV0_SCALE_CNTL				0x0420
 #define SUBPIC_CNTL				0x0540
 #define PM4_BUFFER_OFFSET			0x0700
@@ -237,6 +256,10 @@
 #define AGP_PLL_CNTL				0x0010
 #define FCP_CNTL				0x0012
 #define PLL_TEST_CNTL				0x0013
+#define P2PLL_CNTL				0x002a
+#define P2PLL_REF_DIV				0x002b
+#define P2PLL_DIV_0				0x002b
+#define POWER_MANAGEMENT			0x002f
 
 #define PPLL_RESET				0x01
 #define PPLL_ATOMIC_UPDATE_EN			0x10000
@@ -254,6 +277,14 @@
 /* CRTC control values (CRTC_GEN_CNTL) */
 #define CRTC_CSYNC_EN				0x00000010
 
+#define CRTC2_DBL_SCAN_EN			0x00000001
+#define CRTC2_DISPLAY_DIS			0x00800000
+#define CRTC2_FIFO_EXTSENSE			0x00200000
+#define CRTC2_ICON_EN				0x00100000
+#define CRTC2_CUR_EN				0x00010000
+#define CRTC2_EN				0x02000000
+#define CRTC2_DISP_REQ_EN_B			0x04000000
+
 #define CRTC_PIX_WIDTH_MASK			0x00000700
 #define CRTC_PIX_WIDTH_4BPP			0x00000100
 #define CRTC_PIX_WIDTH_8BPP			0x00000200
@@ -267,10 +298,14 @@
 #define DAC_MASK				0xFF000000
 #define DAC_BLANKING				0x00000004
 #define DAC_RANGE_CNTL				0x00000003
-#define DAC_RANGE_CNTL				0x00000003
+#define DAC_CLK_SEL				0x00000010
 #define DAC_PALETTE_ACCESS_CNTL			0x00000020
+#define DAC_PALETTE2_SNOOP_EN			0x00000040
 #define DAC_PDWN				0x00008000
 
+/* CRTC_EXT_CNTL */
+#define CRT_CRTC_ON				0x00008000
+
 /* GEN_RESET_CNTL bit constants */
 #define SOFT_RESET_GUI				0x00000001
 #define SOFT_RESET_VCLK				0x00000100
@@ -348,5 +383,37 @@
 #define LVDS_BL_MOD_EN				0x00010000
 #define LVDS_DIGION				0x00040000
 #define LVDS_BLON				0x00080000
+#define LVDS_ON					0x00000001
+#define LVDS_DISPLAY_DIS			0x00000002
+#define LVDS_PANEL_TYPE_2PIX_PER_CLK		0x00000004
+#define LVDS_PANEL_24BITS_TFT			0x00000008
+#define LVDS_FRAME_MOD_NO			0x00000000
+#define LVDS_FRAME_MOD_2_LEVELS			0x00000010
+#define LVDS_FRAME_MOD_4_LEVELS			0x00000020
+#define LVDS_RST_FM				0x00000040
+#define LVDS_EN					0x00000080
+
+/* CRTC2_GEN_CNTL constants */
+#define CRTC2_EN				0x02000000
+
+/* POWER_MANAGEMENT constants */
+#define	PWR_MGT_ON				0x00000001
+#define PWR_MGT_MODE_MASK			0x00000006
+#define PWR_MGT_MODE_PIN			0x00000000
+#define PWR_MGT_MODE_REGISTER			0x00000002
+#define PWR_MGT_MODE_TIMER			0x00000004
+#define PWR_MGT_MODE_PCI			0x00000006
+#define PWR_MGT_AUTO_PWR_UP_EN			0x00000008
+#define PWR_MGT_ACTIVITY_PIN_ON			0x00000010
+#define PWR_MGT_STANDBY_POL			0x00000020
+#define PWR_MGT_SUSPEND_POL			0x00000040
+#define PWR_MGT_SELF_REFRESH			0x00000080
+#define PWR_MGT_ACTIVITY_PIN_EN			0x00000100
+#define PWR_MGT_KEYBD_SNOOP			0x00000200
+#define PWR_MGT_TRISTATE_MEM_EN			0x00000800
+#define PWR_MGT_SELW4MS				0x00001000
+#define PWR_MGT_SLOWDOWN_MCLK			0x00002000
+
+#define PMI_PMSCR_REG				0x60
 
 #endif /* REG_RAGE128_H */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/video/aty128fb.c linux/drivers/video/aty128fb.c
--- linux.orig/drivers/video/aty128fb.c	Sun Nov 11 18:09:37 2001
+++ linux/drivers/video/aty128fb.c	Wed Dec 26 18:13:49 2001
@@ -7,13 +7,19 @@
  *                Ani Joshi / Jeff Garzik
  *                      - Code cleanup
  *
+ *                Michel Dänzer <michdaen@iiic.ethz.ch>
+ *                      - 15/16 bit cleanup
+ *                      - fix panning
+ *                      
+ *                Benjamin Herrenschmidt
+ *                      - pmac-specific PM stuff
+ *
  *                Andreas Hundt <andi@convergence.de>
  *                      - FB_ACTIVATE fixes
  *
  *  Based off of Geert's atyfb.c and vfb.c.
  *
  *  TODO:
- *		- panning
  *		- monitor sensing (DDC)
  *              - virtual display
  *		- other platform support (only ppc/x86 supported)
@@ -70,6 +76,9 @@
 #ifdef CONFIG_FB_COMPAT_XPMAC
 #include <asm/vc_ioctl.h>
 #endif
+#ifdef CONFIG_BOOTX_TEXT
+#include <asm/btext.h>
+#endif /* CONFIG_BOOTX_TEXT */
 
 #include <video/fbcon.h>
 #include <video/fbcon-cfb8.h>
@@ -154,6 +163,7 @@
     {"Rage128 RL (AGP)", PCI_DEVICE_ID_ATI_RAGE128_RL, rage_128},
     {"Rage128 Pro PF (AGP)", PCI_DEVICE_ID_ATI_RAGE128_PF, rage_128_pro},
     {"Rage128 Pro PR (PCI)", PCI_DEVICE_ID_ATI_RAGE128_PR, rage_128_pro},
+    {"Rage128 Pro TR (AGP)", PCI_DEVICE_ID_ATI_RAGE128_U3, rage_128_pro},
     {"Rage Mobility M3 (PCI)", PCI_DEVICE_ID_ATI_RAGE128_LE, rage_M3},
     {"Rage Mobility M3 (AGP)", PCI_DEVICE_ID_ATI_RAGE128_LF, rage_M3},
     {NULL, 0, rage_128}
@@ -227,6 +237,11 @@
 static int default_cmode __initdata = CMODE_8;
 #endif
 
+#ifdef CONFIG_PMAC_PBOOK
+static int default_crt_on __initdata = 0;
+static int default_lcd_on __initdata = 1;
+#endif
+
 #ifdef CONFIG_MTRR
 static int mtrr = 1;
 #endif
@@ -251,7 +266,7 @@
     u32 offset, offset_cntl;
     u32 xoffset, yoffset;
     u32 vxres, vyres;
-    u32 bpp;
+    u32 depth, bpp;
 };
 
 struct aty128_pll {
@@ -307,10 +322,23 @@
     int currcon;
     int blitter_may_be_busy;
     int fifo_slots;                 /* free slots in FIFO (64 max) */
+#ifdef CONFIG_PMAC_PBOOK
+    unsigned char *save_framebuffer;
+    int	pm_reg;
+    int crt_on, lcd_on;
+    u32 save_lcd_gen_cntl;
+#endif
 };
 
 static struct fb_info_aty128 *board_list = NULL;
 
+#ifdef CONFIG_PMAC_PBOOK
+  int aty128_sleep_notify(struct pmu_sleep_notifier *self, int when);
+  static struct pmu_sleep_notifier aty128_sleep_notifier = {
+  	aty128_sleep_notify, SLEEP_LEVEL_VIDEO,
+  };
+#endif
+
 #define round_div(n, d) ((n+(d/2))/d)
 
     /*
@@ -331,6 +359,8 @@
 			struct fb_info *info);
 static int aty128fb_pan_display(struct fb_var_screeninfo *var, int con,
 			   struct fb_info *fb);
+static int aty128fb_ioctl(struct inode *inode, struct file *file, u_int cmd,
+		       u_long arg, int con, struct fb_info *info);
 static int aty128fb_rasterimg(struct fb_info *info, int start);
 
 
@@ -379,7 +409,7 @@
 static void do_wait_for_fifo(u16 entries, struct fb_info_aty128 *info);
 static void wait_for_fifo(u16 entries, struct fb_info_aty128 *info);
 static void wait_for_idle(struct fb_info_aty128 *info);
-static u32 bpp_to_depth(u32 bpp);
+static u32 depth_to_dst(u32 depth);
 
 #ifdef FBCON_HAS_CFB8
 static struct display_switch fbcon_aty128_8;
@@ -422,6 +452,7 @@
 	fb_get_cmap:	aty128fb_get_cmap,
 	fb_set_cmap:	aty128fb_set_cmap,
 	fb_pan_display:	aty128fb_pan_display,
+	fb_ioctl:	aty128fb_ioctl,
 	fb_rasterimg:	aty128fb_rasterimg,
 };
 
@@ -496,7 +527,7 @@
 _aty_ld_pll(unsigned int pll_index,
 			const struct fb_info_aty128 *info)
 {       
-    aty_st_8(CLOCK_CNTL_INDEX, pll_index & 0x1F);
+    aty_st_8(CLOCK_CNTL_INDEX, pll_index & 0x3F);
     return aty_ld_le32(CLOCK_CNTL_DATA);
 }
 
@@ -505,7 +536,7 @@
 _aty_st_pll(unsigned int pll_index, u32 val,
 			const struct fb_info_aty128 *info)
 {
-    aty_st_8(CLOCK_CNTL_INDEX, (pll_index & 0x1F) | PLL_WR_EN);
+    aty_st_8(CLOCK_CNTL_INDEX, (pll_index & 0x3F) | PLL_WR_EN);
     aty_st_le32(CLOCK_CNTL_DATA, val);
 }
 
@@ -698,7 +729,7 @@
 		GMC_SRC_CLIP_DEFAULT			|
 		GMC_DST_CLIP_DEFAULT			|
 		GMC_BRUSH_SOLIDCOLOR			|
-		(bpp_to_depth(par->crtc.bpp) << 8)	|
+		(depth_to_dst(par->crtc.depth) << 8)	|
 		GMC_SRC_DSTCOLOR			|
 		GMC_BYTE_ORDER_MSB_TO_LSB		|
 		GMC_DP_CONVERSION_TEMP_6500		|
@@ -731,18 +762,20 @@
 }
 
 
-/* convert bpp values to their register representation */
+/* convert depth values to their register representation */
 static u32
-bpp_to_depth(u32 bpp)
-{
-    if (bpp <= 8)
-	return DST_8BPP;
-    else if (bpp <= 16)
-        return DST_15BPP;
-    else if (bpp <= 24)
-	return DST_24BPP;
-    else if (bpp <= 32)
-	return DST_32BPP;
+depth_to_dst(u32 depth)
+ {
+    if (depth <= 8)
+ 	return DST_8BPP;
+    else if (depth <= 15)
+         return DST_15BPP;
+    else if (depth == 16)
+        return DST_16BPP;
+    else if (depth <= 24)
+ 	return DST_24BPP;
+    else if (depth <= 32)
+ 	return DST_32BPP;
 
     return -EINVAL;
 }
@@ -765,12 +798,8 @@
     aty_st_le32(CRTC_PITCH, crtc->pitch);
     aty_st_le32(CRTC_OFFSET, crtc->offset);
     aty_st_le32(CRTC_OFFSET_CNTL, crtc->offset_cntl);
-    /* Disable ATOMIC updating.  Is this the right place?
-     * -- BenH: Breaks on my G4
-     */
-#if 0
-    aty_st_le32(PPLL_CNTL, aty_ld_le32(PPLL_CNTL) & ~(0x00030000));
-#endif
+    /* Disable ATOMIC updating.  Is this the right place? */
+    aty_st_pll(PPLL_CNTL, aty_ld_pll(PPLL_CNTL) & ~(0x00030000));
 }
 
 
@@ -779,7 +808,7 @@
 			struct aty128_crtc *crtc,
 			const struct fb_info_aty128 *info)
 {
-    u32 xres, yres, vxres, vyres, xoffset, yoffset, bpp;
+    u32 xres, yres, vxres, vyres, xoffset, yoffset, bpp, dst;
     u32 left, right, upper, lower, hslen, vslen, sync, vmode;
     u32 h_total, h_disp, h_sync_strt, h_sync_wid, h_sync_pol;
     u32 v_total, v_disp, v_sync_strt, v_sync_wid, v_sync_pol, c_sync;
@@ -804,6 +833,11 @@
     sync  = var->sync;
     vmode = var->vmode;
 
+    if (bpp != 16)
+        depth = bpp;
+    else
+        depth = (var->green.length == 6) ? 16 : 15;
+
     /* check for mode eligibility
      * accept only non interlaced modes */
     if ((vmode & FB_VMODE_MASK) != FB_VMODE_NONINTERLACED)
@@ -819,17 +853,16 @@
     if (vyres < yres + yoffset)
 	vyres = yres + yoffset;
 
-    /* convert bpp into ATI register depth */
-    depth = bpp_to_depth(bpp);
+    /* convert depth into ATI register depth */
+    dst = depth_to_dst(depth);
 
-    /* make sure we didn't get an invalid depth */
-    if (depth == -EINVAL) {
-        printk(KERN_ERR "aty128fb: Invalid depth\n");
+    if (dst == -EINVAL) {
+        printk(KERN_ERR "aty128fb: Invalid depth or RGBA\n");
         return -EINVAL;
     }
 
-    /* convert depth to bpp */
-    bytpp = mode_bytpp[depth];
+    /* convert register depth to bytes per pixel */
+    bytpp = mode_bytpp[dst];
 
     /* make sure there is enough video ram for the mode */
     if ((u32)(vxres * vyres * bytpp) > info->vram_size) {
@@ -870,7 +903,7 @@
     
     c_sync = sync & FB_SYNC_COMP_HIGH_ACT ? (1 << 4) : 0;
 
-    crtc->gen_cntl = 0x3000000L | c_sync | (depth << 8);
+    crtc->gen_cntl = 0x3000000L | c_sync | (dst << 8);
 
     crtc->h_total = h_total | (h_disp << 16);
     crtc->v_total = v_total | (v_disp << 16);
@@ -893,6 +926,7 @@
     crtc->vyres = vyres;
     crtc->xoffset = xoffset;
     crtc->yoffset = yoffset;
+    crtc->depth = depth;
     crtc->bpp = bpp;
 
     return 0;
@@ -900,7 +934,7 @@
 
 
 static int
-aty128_bpp_to_var(int pix_width, struct fb_var_screeninfo *var)
+aty128_pix_width_to_var(int pix_width, struct fb_var_screeninfo *var)
 {
 
     /* fill in pixel info */
@@ -917,7 +951,6 @@
 	var->transp.length = 0;
 	break;
     case CRTC_PIX_WIDTH_15BPP:
-    case CRTC_PIX_WIDTH_16BPP:
 	var->bits_per_pixel = 16;
 	var->red.offset = 10;
 	var->red.length = 5;
@@ -928,6 +961,17 @@
 	var->transp.offset = 0;
 	var->transp.length = 0;
 	break;
+    case CRTC_PIX_WIDTH_16BPP:
+	var->bits_per_pixel = 16;
+	var->red.offset = 11;
+	var->red.length = 5;
+	var->green.offset = 5;
+	var->green.length = 6;
+	var->blue.offset = 0;
+	var->blue.length = 5;
+	var->transp.offset = 0;
+	var->transp.length = 0;
+	break;
     case CRTC_PIX_WIDTH_24BPP:
         var->bits_per_pixel = 24;
         var->red.offset = 16;
@@ -996,7 +1040,7 @@
             (v_sync_pol ? 0 : FB_SYNC_VERT_HIGH_ACT) |
             (c_sync ? FB_SYNC_COMP_HIGH_ACT : 0);
 
-    aty128_bpp_to_var(pix_width, var);
+    aty128_pix_width_to_var(pix_width, var);
 
     var->xres = xres;
     var->yres = yres;
@@ -1017,6 +1061,42 @@
 }
 
 static void
+aty128_set_crt_enable(struct fb_info_aty128 *info, int on)
+{
+    if (on) {
+	aty_st_le32(CRTC_EXT_CNTL, aty_ld_le32(CRTC_EXT_CNTL) | CRT_CRTC_ON);
+	aty_st_le32(DAC_CNTL, (aty_ld_le32(DAC_CNTL) | DAC_PALETTE2_SNOOP_EN));
+    } else
+	aty_st_le32(CRTC_EXT_CNTL, aty_ld_le32(CRTC_EXT_CNTL) & ~CRT_CRTC_ON);
+}
+
+static void
+aty128_set_lcd_enable(struct fb_info_aty128 *info, int on)
+{
+    u32 reg;
+    
+    if (on) {
+	reg = aty_ld_le32(LVDS_GEN_CNTL);
+	reg |= LVDS_ON | LVDS_EN | LVDS_BLON | LVDS_DIGION;
+	reg &= ~LVDS_DISPLAY_DIS;
+	aty_st_le32(LVDS_GEN_CNTL, reg);
+#ifdef CONFIG_PMAC_BACKLIGHT
+	aty128_set_backlight_enable(get_backlight_enable(), get_backlight_level(), info);
+#endif	
+    } else {
+#ifdef CONFIG_PMAC_BACKLIGHT
+	aty128_set_backlight_enable(0, 0, info);
+#endif	
+	reg = aty_ld_le32(LVDS_GEN_CNTL);
+	reg |= LVDS_DISPLAY_DIS;
+        aty_st_le32(LVDS_GEN_CNTL, reg);
+        mdelay(100);
+	reg &= ~(LVDS_ON /*| LVDS_EN*/);
+	aty_st_le32(LVDS_GEN_CNTL, reg);
+    }
+}
+
+static void
 aty128_set_pll(struct aty128_pll *pll, const struct fb_info_aty128 *info)
 {
     u32 div3;
@@ -1053,6 +1133,23 @@
 
     /* clear the reset, just in case */
     aty_st_pll(PPLL_CNTL, aty_ld_pll(PPLL_CNTL) & ~PPLL_RESET);
+
+#if 0
+    if (info->chip_gen == rage_M3) {
+	/* XXX energy saving, disable VCLK during blanking */
+	aty_pll_wait_readupdate(info);
+    	aty_st_pll(VCLK_ECP_CNTL, aty_ld_pll(VCLK_ECP_CNTL) | 0xc0);
+		aty_pll_writeupdate(info);
+
+	/* Set PM clocks */
+	aty_pll_wait_readupdate(info);
+	aty_st_pll(XCLK_CNTL, aty_ld_pll(XCLK_CNTL) | 0x00330000);
+	aty_pll_writeupdate(info);
+	aty_pll_wait_readupdate(info);
+	aty_st_pll(MCLK_CNTL, aty_ld_pll(MCLK_CNTL) | 0x00000700);
+	aty_pll_writeupdate(info);
+    }
+#endif    
 }
 
 
@@ -1121,7 +1218,7 @@
 static int
 aty128_ddafifo(struct aty128_ddafifo *dsp,
 		const struct aty128_pll *pll,
-		u32 bpp,
+		u32 depth,
 		const struct fb_info_aty128 *info)
 {
     const struct aty128_meminfo *m = info->mem;
@@ -1129,11 +1226,10 @@
     u32 fifo_width = info->constants.fifo_width;
     u32 fifo_depth = info->constants.fifo_depth;
     s32 x, b, p, ron, roff;
-    u32 n, d;
+    u32 n, d, bpp;
 
-    /* 15bpp is really 16bpp */
-    if (bpp == 15)
-	bpp = 16;
+    /* round up to multiple of 8 */
+    bpp = (depth+7) & ~7;
 
     n = xclk * fifo_width;
     d = pll->vclk * bpp;
@@ -1214,15 +1310,21 @@
     config = aty_ld_le32(CONFIG_CNTL) & ~3;
 
 #if defined(__BIG_ENDIAN)
-    if (par->crtc.bpp >= 24)
-	config |= 2;	/* make aperture do 32 byte swapping */
-    else if (par->crtc.bpp > 8)
-	config |= 1;	/* make aperture do 16 byte swapping */
+    if (par->crtc.bpp == 32)
+	config |= 2;	/* make aperture do 32 bit swapping */
+    else if (par->crtc.bpp == 16)
+	config |= 1;	/* make aperture do 16 bit swapping */
 #endif
 
     aty_st_le32(CONFIG_CNTL, config);
     aty_st_8(CRTC_EXT_CNTL + 1, 0);	/* turn the video back on */
 
+#ifdef CONFIG_PMAC_PBOOK
+    if (info->chip_gen == rage_M3) {
+	aty128_set_crt_enable(info, info->crt_on);
+	aty128_set_lcd_enable(info, info->lcd_on);
+    }
+#endif
     if (par->accel_flags & FB_ACCELF_TEXT)
         aty128_init_engine(par, info);
 
@@ -1247,6 +1349,13 @@
 	display_info.disp_reg_address = info->regbase_phys;
     }
 #endif /* CONFIG_FB_COMPAT_XPMAC */
+#if defined(CONFIG_BOOTX_TEXT)
+    btext_update_display(info->frame_buffer_phys,
+		    (((par->crtc.h_total>>16) & 0xff)+1)*8,
+		    ((par->crtc.v_total>>16) & 0x7ff)+1,
+		    par->crtc.bpp,
+		    par->crtc.vxres*par->crtc.bpp/8);
+#endif /* CONFIG_BOOTX_TEXT */
 }
 
     /*
@@ -1265,7 +1374,7 @@
     if ((err = aty128_var_to_pll(var->pixclock, &par->pll, info)))
 	return err;
 
-    if ((err = aty128_ddafifo(&par->fifo_reg, &par->pll, par->crtc.bpp, info)))
+    if ((err = aty128_ddafifo(&par->fifo_reg, &par->pll, par->crtc.depth, info)))
 	return err;
 
     if (var->accel_flags & FB_ACCELF_TEXT)
@@ -1333,7 +1442,7 @@
     struct fb_info_aty128 *info = (struct fb_info_aty128 *)fb;
     struct aty128fb_par par;
     struct display *display;
-    int oldxres, oldyres, oldvxres, oldvyres, oldbpp, oldaccel;
+    int oldxres, oldyres, oldvxres, oldvyres, oldbpp, oldgreen, oldaccel;
     int accel, err;
 
     display = (con >= 0) ? &fb_display[con] : fb->disp;
@@ -1378,11 +1487,13 @@
     oldvxres = display->var.xres_virtual;
     oldvyres = display->var.yres_virtual;
     oldbpp = display->var.bits_per_pixel;
+    oldgreen = display->var.green.length;
     oldaccel = display->var.accel_flags;
     display->var = *var;
     if (oldxres != var->xres || oldyres != var->yres ||
 	oldvxres != var->xres_virtual || oldvyres != var->yres_virtual ||
-	oldbpp != var->bits_per_pixel || oldaccel != var->accel_flags) {
+	oldgreen != var->green.length || oldbpp != var->bits_per_pixel ||
+	oldaccel != var->accel_flags) {
 
 	struct fb_fix_screeninfo fix;
 
@@ -1412,7 +1523,7 @@
     if (!info->fb_info.display_fg || info->fb_info.display_fg->vc_num == con)
 	aty128_set_par(&par, info);
 
-    if (oldbpp != var->bits_per_pixel) {
+    if (oldbpp != var->bits_per_pixel || oldgreen != var->green.length) {
 	if ((err = fb_alloc_cmap(&display->cmap, 0, 0)))
 	    return err;
 	do_install_cmap(con, &info->fb_info);
@@ -1433,7 +1544,6 @@
 	break;
 #endif
 #ifdef FBCON_HAS_CFB16
-    case 15:
     case 16:
 	disp->dispsw = accel ? &fbcon_aty128_16 : &fbcon_cfb16;
 	disp->dispsw_data = info->fbcon_cmap.cfb16;
@@ -1475,7 +1585,7 @@
     fix->type        = FB_TYPE_PACKED_PIXELS;
     fix->type_aux    = 0;
     fix->line_length = (par->crtc.vxres * par->crtc.bpp) >> 3;
-    fix->visual      = par->crtc.bpp <= 8 ? FB_VISUAL_PSEUDOCOLOR
+    fix->visual      = par->crtc.bpp == 8 ? FB_VISUAL_PSEUDOCOLOR
                                           : FB_VISUAL_DIRECTCOLOR;
     fix->ywrapstep = 0;
     fix->xpanstep  = 8;
@@ -1509,8 +1619,6 @@
 
     /*
      *  Pan or Wrap the Display
-     *
-     *  Not supported (yet!)
      */
 static int
 aty128fb_pan_display(struct fb_var_screeninfo *var, int con,
@@ -1534,7 +1642,10 @@
     par->crtc.xoffset = xoffset;
     par->crtc.yoffset = yoffset;
 
-    offset = ((yoffset * par->crtc.vxres + xoffset) * par->crtc.bpp) >> 6;
+    offset = ((yoffset * par->crtc.vxres + xoffset)*(par->crtc.bpp >> 3)) & ~7;
+
+    if (par->crtc.bpp == 24)
+        offset += 8 * (offset % 3); /* Must be multiple of 8 and 3 */
 
     aty_st_le32(CRTC_OFFSET, offset);
 
@@ -1550,20 +1661,16 @@
 aty128fb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
 			struct fb_info *info)
 {
-#if 1
-    fb_copy_cmap(&info->cmap, cmap, kspc ? 0 : 2);
-#else
-    struct fb_info_aty128 fb = (struct fb_info_aty128 *)info;
+    struct fb_info_aty128 *fb = (struct fb_info_aty128 *)info;
+    struct display *disp = (con < 0) ? info->disp : (fb_display + con);
 
     if (con == fb->currcon) /* current console? */
-	return fb_get_cmap(cmap, kspc, aty128_getcolreg, info);
-    else if (fb_display[con].cmap.len) /* non default colormap? */
-	fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
-    else {  
-	int size = (fb_display[con].var.bits_per_pixel <= 8) ? 256 : 32;
-	fb_copy_cmap(fb_default_cmap(size), cmap, kspc ? 0 : 2);
-    }
-#endif
+        return fb_get_cmap(cmap, kspc, aty128_getcolreg, info);
+    else if (disp->cmap.len) /* non default colormap? */
+        fb_copy_cmap(&disp->cmap, cmap, kspc ? 0 : 2);
+    else
+        fb_copy_cmap(fb_default_cmap((disp->var.bits_per_pixel==8) ? 256 : 32),
+                     cmap, kspc ? 0 : 2);
 
     return 0;
 }
@@ -1576,19 +1683,19 @@
 aty128fb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
 			struct fb_info *info)
 {
-    int err;
     struct fb_info_aty128 *fb = (struct fb_info_aty128 *)info;
-    struct display *disp;  
+    struct display *disp = (con < 0) ? info->disp : (fb_display + con);
+    unsigned int cmap_len = (disp->var.bits_per_pixel==8) ? 256 : 32;
 
-    if (con >= 0)
-	disp = &fb_display[con];
-    else
-	disp = info->disp;
+    if (disp->cmap.len != cmap_len) {
+        int err = fb_alloc_cmap(&disp->cmap, cmap_len, 0);
 
-    if (!disp->cmap.len) {      /* no colormap allocated? */
-        int size = (disp->var.bits_per_pixel <= 8) ? 256 : 32;
-	if ((err = fb_alloc_cmap(&disp->cmap, size, 0)))
-	    return err;
+    	if (!disp->cmap.len) {      /* no colormap allocated? */
+        	int size = (disp->var.bits_per_pixel <= 8) ? 256 : 32;
+		if ((err = fb_alloc_cmap(&disp->cmap, size, 0)))
+		    return err;
+	}
+        if (err) return err;
     }
 
     if (con == fb->currcon) /* current console? */
@@ -1599,6 +1706,31 @@
     return 0;                
 }
 
+    /*
+     *  Helper function to store a single palette register
+     */
+static __inline__ void
+aty128_st_pal(u_int regno, u_int red, u_int green, u_int blue,
+	      struct fb_info_aty128 *info)
+{
+    /* Note: For now, on M3, we set palette on both heads, which may
+     * be useless. Can someone with a M3 check this ?
+     * 
+     * This code would still be useful if using the second CRTC to 
+     * do mirroring
+     */
+
+    if (info->chip_gen == rage_M3) {
+#if 0
+        aty_st_le32(DAC_CNTL, aty_ld_le32(DAC_CNTL) | DAC_PALETTE_ACCESS_CNTL);
+        aty_st_8(PALETTE_INDEX, regno);
+        aty_st_le32(PALETTE_DATA, (red<<16)|(green<<8)|blue);
+#endif
+        aty_st_le32(DAC_CNTL, aty_ld_le32(DAC_CNTL) & ~DAC_PALETTE_ACCESS_CNTL);
+    }
+    aty_st_8(PALETTE_INDEX, regno);
+    aty_st_le32(PALETTE_DATA, (red<<16)|(green<<8)|blue);
+}
 
 static int
 aty128fb_rasterimg(struct fb_info *info, int start)
@@ -1620,7 +1752,7 @@
     if (!options || !*options)
 	return 0;
 
-    while (this_opt = strsep(&options, ",")) {
+    while ((this_opt = strsep(&options, ",")) != 0) {
 	if (!strncmp(this_opt, "font:", 5)) {
 	    char *p;
 	    int i;
@@ -1633,6 +1765,12 @@
 	    fontname[i] = 0;
 	} else if (!strncmp(this_opt, "noaccel", 7)) {
 	    noaccel = 1;
+#ifdef CONFIG_PMAC_PBOOK
+        } else if (!strncmp(this_opt, "lcd:", 4)) {
+            default_lcd_on = simple_strtoul(this_opt+4, NULL, 0);
+        } else if (!strncmp(this_opt, "crt:", 4)) {
+            default_crt_on = simple_strtoul(this_opt+4, NULL, 0);
+#endif
         }
 #ifdef CONFIG_MTRR
         else if(!strncmp(this_opt, "nomtrr", 6)) {
@@ -1713,7 +1851,11 @@
     info->fb_info.updatevar  = NULL;
     info->fb_info.blank = &aty128fbcon_blank;
     info->fb_info.flags = FBINFO_FLAG_DEFAULT;
-
+#ifdef CONFIG_PMAC_PBOOK
+    info->lcd_on = default_lcd_on;
+    info->crt_on = default_crt_on;
+#endif
+    
     var = default_var;
 #ifdef CONFIG_PPC
     if (_machine == _MACH_Pmac) {
@@ -1724,6 +1866,29 @@
             if (default_vmode <= 0 || default_vmode > VMODE_MAX)
                 default_vmode = VMODE_1024_768_60;
 
+	    /* iMacs need that resolution
+	     * PowerMac2,1 first r128 iMacs
+	     * PowerMac2,2 summer 2000 iMacs
+	     * PowerMac4,1 january 2001 iMacs "flower power"
+	     */
+	    if (machine_is_compatible("PowerMac2,1") ||
+		machine_is_compatible("PowerMac2,2") ||
+		machine_is_compatible("PowerMac4,1"))
+		default_vmode = VMODE_1024_768_75;
+
+	    /* iBook SE */
+	    if (machine_is_compatible("PowerBook2,2"))
+		default_vmode = VMODE_800_600_60;
+ 
+	    /* PowerBook Firewire (Pismo), iBook Dual USB */
+	    if (machine_is_compatible("PowerBook3,1") ||
+	    	machine_is_compatible("PowerBook4,1"))
+		default_vmode = VMODE_1024_768_60;
+
+	    /* PowerBook Titanium */
+	    if (machine_is_compatible("PowerBook3,2"))
+		default_vmode = VMODE_1152_768_60;
+
             if (default_cmode < CMODE_8 || default_cmode > CMODE_32)
                 default_cmode = CMODE_8;
 
@@ -1760,6 +1925,8 @@
     dac = aty_ld_le32(DAC_CNTL);
     dac |= (DAC_8BIT_EN | DAC_RANGE_CNTL);
     dac |= DAC_MASK;
+    if (info->chip_gen == rage_M3)
+    	dac |= DAC_PALETTE2_SNOOP_EN;
     aty_st_le32(DAC_CNTL, dac);
 
     /* turn off bus mastering, just in case */
@@ -1778,6 +1945,14 @@
     if (info->chip_gen == rage_M3)
     	register_backlight_controller(&aty128_backlight_controller, info, "ati");
 #endif /* CONFIG_PMAC_BACKLIGHT */
+#ifdef CONFIG_PMAC_PBOOK
+    if (!info->pdev)
+    	printk(KERN_WARNING "aty128fb: Not a PCI card, can't enable power management\n");
+    else {
+	    info->pm_reg = pci_find_capability(info->pdev, PCI_CAP_ID_PM);
+	    pmu_register_sleep_notifier(&aty128_sleep_notifier);
+    }
+#endif
 
     printk(KERN_INFO "fb%d: %s frame buffer device on %s\n",
 	   GET_FB_IDX(info->fb_info.node), aty128fb_name, name);
@@ -1843,7 +2018,7 @@
 	if ((err = pci_enable_device(pdev))) {
 		printk(KERN_ERR "aty128fb: Cannot enable PCI device: %d\n",
 				err);
-		goto err_out;
+		return -ENODEV;
 	}
 
 	fb_addr = pci_resource_start(pdev, 0);
@@ -2162,6 +2337,12 @@
 
     aty_st_8(CRTC_EXT_CNTL+1, state);
 
+#ifdef CONFIG_PMAC_PBOOK
+    if (info->chip_gen == rage_M3) {
+	aty128_set_crt_enable(info, info->crt_on && !blank);
+	aty128_set_lcd_enable(info, info->lcd_on && !blank);
+    }
+#endif	
 #ifdef CONFIG_PMAC_BACKLIGHT
     if ((_machine == _MACH_Pmac) && !blank)
     	set_backlight_enable(1);
@@ -2200,7 +2381,7 @@
                          u_int transp, struct fb_info *fb)
 {
     struct fb_info_aty128 *info = (struct fb_info_aty128 *)fb;
-    u32 col;
+    u32 palreg;
 
     if (regno > 255)
 	return 1;
@@ -2220,65 +2401,47 @@
     if ((info->current_par.crtc.bpp > 8) && (regno == 0)) {
         int i;
 
-        if (info->chip_gen == rage_M3)
-            aty_st_le32(DAC_CNTL, aty_ld_le32(DAC_CNTL) & ~DAC_PALETTE_ACCESS_CNTL);
-
-        for (i=16; i<256; i++) {
-            aty_st_8(PALETTE_INDEX, i);
-            col = (i << 16) | (i << 8) | i;
-            aty_st_le32(PALETTE_DATA, col);
-        }
-
-        if (info->chip_gen == rage_M3) {
-            aty_st_le32(DAC_CNTL, aty_ld_le32(DAC_CNTL) | DAC_PALETTE_ACCESS_CNTL);
-
-            for (i=16; i<256; i++) {
-                aty_st_8(PALETTE_INDEX, i);
-                col = (i << 16) | (i << 8) | i;
-                aty_st_le32(PALETTE_DATA, col);
-            }
-        }
+        for (i=0; i<256; i++)
+            aty128_st_pal(i, i, i, i, info);
     }
 
     /* initialize palette */
 
-    if (info->chip_gen == rage_M3)
-        aty_st_le32(DAC_CNTL, aty_ld_le32(DAC_CNTL) & ~DAC_PALETTE_ACCESS_CNTL);
+    palreg = regno;
 
     if (info->current_par.crtc.bpp == 16)
-        aty_st_8(PALETTE_INDEX, (regno << 3));
-    else
-        aty_st_8(PALETTE_INDEX, regno);
-    col = (red << 16) | (green << 8) | blue;
-    aty_st_le32(PALETTE_DATA, col);
-    if (info->chip_gen == rage_M3) {
-    	aty_st_le32(DAC_CNTL, aty_ld_le32(DAC_CNTL) | DAC_PALETTE_ACCESS_CNTL);
-        if (info->current_par.crtc.bpp == 16)
-            aty_st_8(PALETTE_INDEX, (regno << 3));
-        else
-            aty_st_8(PALETTE_INDEX, regno);
-        aty_st_le32(PALETTE_DATA, col);
+        palreg = regno * 8;
+
+    if (info->current_par.crtc.depth == 16) {
+        aty128_st_pal(palreg/2, info->palette[regno/2].red, green,
+                      info->palette[regno/2].blue, info);
+        green = info->palette[regno*2].green;
     }
 
+    if (info->current_par.crtc.bpp == 8 || regno < 32)
+        aty128_st_pal(palreg, red, green, blue, info);
+        
     if (regno < 16)
-	switch (info->current_par.crtc.bpp) {
+	switch (info->current_par.crtc.depth) {
 #ifdef FBCON_HAS_CFB16
-	case 9 ... 16:
+	case 15:
 	    info->fbcon_cmap.cfb16[regno] = (regno << 10) | (regno << 5) |
                 regno;
 	    break;
+	case 16:
+	    info->fbcon_cmap.cfb16[regno] = (regno << 11) | (regno << 5) |
+                regno;
+	    break;
 #endif
 #ifdef FBCON_HAS_CFB24
-	case 17 ... 24:
+	case 24:
 	    info->fbcon_cmap.cfb24[regno] = (regno << 16) | (regno << 8) |
 		regno;
 	    break;
 #endif
 #ifdef FBCON_HAS_CFB32
-	case 25 ... 32: {
-            u32 i;
-
-            i = (regno << 8) | regno;
+	case 32: {
+            u32 i = (regno << 8) | regno;
             info->fbcon_cmap.cfb32[regno] = (i << 16) | i;
 	    break;
         }
@@ -2291,41 +2454,115 @@
 static void
 do_install_cmap(int con, struct fb_info *info)
 {
-    struct fb_info_aty128 *fb = (struct fb_info_aty128 *)info;
+    struct display *disp = (con < 0) ? info->disp : (fb_display + con);
+
+    if (disp->cmap.len)
+	fb_set_cmap(&disp->cmap, 1, aty128_setcolreg, info);
+    else
+	fb_set_cmap(fb_default_cmap((disp->var.bits_per_pixel==8) ? 256 :32),
+		    1, aty128_setcolreg, info);
+}
 
-    if (con != fb->currcon)
-	return;
+#define ATY_MIRROR_LCD_ON	0x00000001
+#define ATY_MIRROR_CRT_ON	0x00000002
 
-    if (fb_display[con].cmap.len)
-	fb_set_cmap(&fb_display[con].cmap, 1, aty128_setcolreg, info);
-    else {
-	int size = (fb_display[con].var.bits_per_pixel <= 8) ? 256 : 16;
-	fb_set_cmap(fb_default_cmap(size), 1, aty128_setcolreg, info);
+/* out param: u32*	backlight value: 0 to 15 */
+#define FBIO_ATY128_GET_MIRROR	_IOR('@', 1, sizeof(__u32*))
+/* in param: u32*	backlight value: 0 to 15 */
+#define FBIO_ATY128_SET_MIRROR	_IOW('@', 2, sizeof(__u32*))
+
+static int aty128fb_ioctl(struct inode *inode, struct file *file, u_int cmd,
+		       u_long arg, int con, struct fb_info *info)
+{
+    struct fb_info_aty128 *fb = (struct fb_info_aty128 *)info;
+    u32 value;
+    int rc;
+    
+    switch (cmd) {
+#ifdef CONFIG_PMAC_PBOOK
+    case FBIO_ATY128_SET_MIRROR:
+    	if (fb->chip_gen != rage_M3)
+    		return -EINVAL;
+    	rc = get_user(value, (__u32*)arg);
+    	if (rc)
+    		return rc;
+    	fb->lcd_on = (value & 0x01) != 0;
+    	fb->crt_on = (value & 0x02) != 0;
+    	if (!fb->crt_on && !fb->lcd_on)
+    		fb->lcd_on = 1;
+    	aty128_set_crt_enable(fb, fb->crt_on);	
+    	aty128_set_lcd_enable(fb, fb->lcd_on);	
+	break;
+    case FBIO_ATY128_GET_MIRROR:
+    	if (fb->chip_gen != rage_M3)
+    		return -EINVAL;
+	value = (fb->crt_on << 1) | fb->lcd_on;
+    	return put_user(value, (__u32*)arg);
+#endif
+    default:
+	return -EINVAL;
     }
+    return 0;
 }
 
-
 #ifdef CONFIG_PMAC_BACKLIGHT
 static int backlight_conv[] = {
 	0xff, 0xc0, 0xb5, 0xaa, 0x9f, 0x94, 0x89, 0x7e,
 	0x73, 0x68, 0x5d, 0x52, 0x47, 0x3c, 0x31, 0x24
 };
 
+/* We turn off the LCD completely instead of just dimming the backlight.
+ * This provides greater power saving and the display is useless without
+ * backlight anyway
+ */
+#define BACKLIGHT_LVDS_OFF
+/* That one prevents proper CRT output with LCD off */
+#undef BACKLIGHT_DAC_OFF
+
 static int
 aty128_set_backlight_enable(int on, int level, void* data)
 {
 	struct fb_info_aty128 *info = (struct fb_info_aty128 *)data;
 	unsigned int reg = aty_ld_le32(LVDS_GEN_CNTL);
-	
+
+	if (!info->lcd_on)
+		on = 0;
 	reg |= LVDS_BL_MOD_EN | LVDS_BLON;
 	if (on && level > BACKLIGHT_OFF) {
+		reg |= LVDS_DIGION;
+		if (!reg & LVDS_ON) {
+			reg &= ~LVDS_BLON;
+			aty_st_le32(LVDS_GEN_CNTL, reg);
+			(void)aty_ld_le32(LVDS_GEN_CNTL);
+			mdelay(10);
+			reg |= LVDS_BLON;
+			aty_st_le32(LVDS_GEN_CNTL, reg);
+		}
 		reg &= ~LVDS_BL_MOD_LEVEL_MASK;
 		reg |= (backlight_conv[level] << LVDS_BL_MOD_LEVEL_SHIFT);
+#ifdef BACKLIGHT_LVDS_OFF
+		reg |= LVDS_ON | LVDS_EN;
+		reg &= ~LVDS_DISPLAY_DIS;
+#endif
+		aty_st_le32(LVDS_GEN_CNTL, reg);
+#ifdef BACKLIGHT_DAC_OFF
+		aty_st_le32(DAC_CNTL, aty_ld_le32(DAC_CNTL) & (~DAC_PDWN));
+#endif		
 	} else {
 		reg &= ~LVDS_BL_MOD_LEVEL_MASK;
 		reg |= (backlight_conv[0] << LVDS_BL_MOD_LEVEL_SHIFT);
+#ifdef BACKLIGHT_LVDS_OFF
+		reg |= LVDS_DISPLAY_DIS;
+		aty_st_le32(LVDS_GEN_CNTL, reg);
+		(void)aty_ld_le32(LVDS_GEN_CNTL);
+		udelay(10);
+		reg &= ~(LVDS_ON | LVDS_EN | LVDS_BLON | LVDS_DIGION);
+#endif		
+		aty_st_le32(LVDS_GEN_CNTL, reg);
+#ifdef BACKLIGHT_DAC_OFF
+		aty_st_le32(DAC_CNTL, aty_ld_le32(DAC_CNTL) | DAC_PDWN);
+#endif		
 	}
-	aty_st_le32(LVDS_GEN_CNTL, reg);
 
 	return 0;
 }
@@ -2346,18 +2583,18 @@
 		u_int width, u_int height,
 		struct fb_info_aty128 *info)
 {
-    u32 save_dp_datatype, save_dp_cntl, bppval;
+    u32 save_dp_datatype, save_dp_cntl, dstval;
 
     if (!width || !height)
         return;
 
-    bppval = bpp_to_depth(info->current_par.crtc.bpp);
-    if (bppval == DST_24BPP) {
+    dstval = depth_to_dst(info->current_par.crtc.depth);
+    if (dstval == DST_24BPP) {
         srcx *= 3;
         dstx *= 3;
         width *= 3;
-    } else if (bppval == -EINVAL) {
-        printk("aty128fb: invalid depth\n");
+    } else if (dstval == -EINVAL) {
+        printk("aty128fb: invalid depth or RGBA\n");
         return;
     }
 
@@ -2369,7 +2606,7 @@
     aty_st_le32(SRC_Y_X, (srcy << 16) | srcx);
     aty_st_le32(DP_MIX, ROP3_SRCCOPY | DP_SRC_RECT);
     aty_st_le32(DP_CNTL, DST_X_LEFT_TO_RIGHT | DST_Y_TOP_TO_BOTTOM);
-    aty_st_le32(DP_DATATYPE, save_dp_datatype | bppval | SRC_DSTCOLOR);
+    aty_st_le32(DP_DATATYPE, save_dp_datatype | dstval | SRC_DSTCOLOR);
 
     aty_st_le32(DST_Y_X, (dsty << 16) | dstx);
     aty_st_le32(DST_HEIGHT_WIDTH, (height << 16) | width);
@@ -2594,6 +2831,139 @@
     fontwidthmask:	FONTWIDTH(4)|FONTWIDTH(8)|FONTWIDTH(12)|FONTWIDTH(16)
 };
 #endif
+
+#ifdef CONFIG_PMAC_PBOOK
+static void
+aty128_set_suspend(struct fb_info_aty128 *info, int suspend)
+{
+	u32	pmgt;
+	u16	pwr_command;
+
+	if (!info->pm_reg)
+		return;
+		
+	/* Set the chip into the appropriate suspend mode (we use D2,
+	 * D3 would require a complete re-initialisation of the chip,
+	 * including PCI config registers, clocks, AGP configuration, ...)
+	 */
+	if (suspend) {
+		/* Make sure CRTC2 is reset. Remove that the day we decide to
+		 * actually use CRTC2 and replace it with real code for disabling
+		 * the CRTC2 output during sleep
+		 */
+		aty_st_le32(CRTC2_GEN_CNTL, aty_ld_le32(CRTC2_GEN_CNTL) &
+			~(CRTC2_EN));
+
+		/* Set the power management mode to be PCI based */
+		pmgt = aty_ld_pll(POWER_MANAGEMENT);
+#if 0
+		pmgt &= ~PWR_MGT_MODE_MASK;
+		pmgt |= PWR_MGT_MODE_PCI | PWR_MGT_ON | PWR_MGT_TRISTATE_MEM_EN | PWR_MGT_AUTO_PWR_UP_EN;
+#else		/* Use this magic value for now */
+		pmgt = 0x0c005407;
+#endif
+		aty_st_pll(POWER_MANAGEMENT, pmgt);
+		(void)aty_ld_pll(POWER_MANAGEMENT);
+		aty_st_le32(BUS_CNTL1, 0x00000010);
+		aty_st_le32(MEM_POWER_MISC, 0x0c830000);
+		mdelay(100);
+		pci_read_config_word(info->pdev, info->pm_reg+PCI_PM_CTRL, &pwr_command);
+		/* Switch PCI power management to D2 */
+		pci_write_config_word(info->pdev, info->pm_reg+PCI_PM_CTRL,
+			(pwr_command & ~PCI_PM_CTRL_STATE_MASK) | 2);
+		pci_read_config_word(info->pdev, info->pm_reg+PCI_PM_CTRL, &pwr_command);
+	} else {
+		/* Switch back PCI power management to D0 */
+		mdelay(100);
+		pci_write_config_word(info->pdev, info->pm_reg+PCI_PM_CTRL, 0);
+		mdelay(100);
+		pci_read_config_word(info->pdev, info->pm_reg+PCI_PM_CTRL, &pwr_command);
+		mdelay(100);
+	}
+}
+
+extern struct display_switch fbcon_dummy;
+
+/*
+ * Save the contents of the frame buffer when we go to sleep,
+ * and restore it when we wake up again.
+ */
+int
+aty128_sleep_notify(struct pmu_sleep_notifier *self, int when)
+{
+	struct fb_info_aty128 *info;
+ 	int result;
+
+	result = PBOOK_SLEEP_OK;
+
+	for (info = board_list; info != NULL; info = info->next) {
+		struct fb_fix_screeninfo fix;
+		int nb;
+
+		aty128fb_get_fix(&fix, fg_console, (struct fb_info *)info);
+		nb = fb_display[fg_console].var.yres * fix.line_length;
+
+		switch (when) {
+		case PBOOK_SLEEP_REQUEST:
+			info->save_framebuffer = vmalloc(nb);
+			if (info->save_framebuffer == NULL)
+				return PBOOK_SLEEP_REFUSE;
+			break;
+		case PBOOK_SLEEP_REJECT:
+			if (info->save_framebuffer) {
+				vfree(info->save_framebuffer);
+				info->save_framebuffer = 0;
+			}
+			break;
+		case PBOOK_SLEEP_NOW:
+			if (info->currcon >= 0)
+				fb_display[info->currcon].dispsw = &fbcon_dummy;
+			
+			wait_for_idle(info);
+			aty128_reset_engine(info);
+			wait_for_idle(info);
+
+			/* Backup fb content */	
+			if (info->save_framebuffer)
+				memcpy_fromio(info->save_framebuffer,
+				       (void *)info->frame_buffer, nb);
+
+			/* Blank display and LCD */
+			aty128fbcon_blank(VESA_POWERDOWN+1, (struct fb_info *)info);
+			
+			/* Sleep the chip */
+			aty128_set_suspend(info, 1);
+
+			break;
+		case PBOOK_WAKE:
+			/* Wake the chip */
+			aty128_set_suspend(info, 0);
+			
+			aty128_reset_engine(info);
+			wait_for_idle(info);
+
+			/* Restore fb content */			
+			if (info->save_framebuffer) {
+				memcpy_toio((void *)info->frame_buffer,
+				       info->save_framebuffer, nb);
+				vfree(info->save_framebuffer);
+				info->save_framebuffer = 0;
+			}
+
+			if (info->currcon >= 0) {
+				aty128_set_dispsw(
+					&fb_display[info->currcon],
+					info,
+					info->current_par.crtc.bpp,
+					info->current_par.accel_flags & FB_ACCELF_TEXT);
+			}
+			aty128fbcon_blank(0, (struct fb_info *)info);
+			break;
+		}
+	}
+	return result;
+}
+#endif /* CONFIG_PMAC_PBOOK */
 
 #ifdef MODULE
 MODULE_AUTHOR("(c)1999-2000 Brad Douglas <brad@neruo.com>");
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/video/controlfb.c linux/drivers/video/controlfb.c
--- linux.orig/drivers/video/controlfb.c	Mon Jan  7 23:41:09 2002
+++ linux/drivers/video/controlfb.c	Wed Dec 26 16:50:52 2001
@@ -621,14 +621,10 @@
 
 	full = p->total_vram == 0x400000;
 
+#ifdef CONFIG_NVRAM
 	/* Try to pick a video mode out of NVRAM if we have one. */
-	if (default_cmode == CMODE_NVRAM){
+	if (default_cmode == CMODE_NVRAM)
 		cmode = nvram_read_byte(NV_CMODE);
-		if(cmode < CMODE_8 || cmode > CMODE_32)
-			cmode = CMODE_8;
-	} else
-		cmode=default_cmode;
-
 	if (default_vmode == VMODE_NVRAM) {
 		vmode = nvram_read_byte(NV_VMODE);
 		if (vmode < 1 || vmode > VMODE_MAX ||
@@ -639,15 +635,16 @@
 			if (control_mac_modes[vmode - 1].m[full] < cmode)
 				vmode = VMODE_640_480_60;
 		}
-	} else {
-		vmode=default_vmode;
-		if (control_mac_modes[vmode - 1].m[full] < cmode) {
-			if (cmode > CMODE_8)
-				cmode--;
-			else
-				vmode = VMODE_640_480_60;
-		}
 	}
+#endif
+
+	/* If we didn't get something from NVRAM, pick a
+	 * sane default.
+	 */
+	if (vmode <= 0 || vmode > VMODE_MAX)
+		vmode = VMODE_640_480_67;
+	if (cmode < CMODE_8 || cmode > CMODE_32)
+		cmode = CMODE_8;
 
 	if (mac_vmode_to_var(vmode, cmode, &var) < 0) {
 		/* This shouldn't happen! */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/video/fbcon.c linux/drivers/video/fbcon.c
--- linux.orig/drivers/video/fbcon.c	Mon Jan  7 23:41:09 2002
+++ linux/drivers/video/fbcon.c	Wed Dec 26 16:50:52 2001
@@ -75,6 +75,7 @@
 #include <linux/selection.h>
 #include <linux/smp.h>
 #include <linux/init.h>
+#include <linux/pm.h>
 
 #include <asm/irq.h>
 #include <asm/system.h>
@@ -137,6 +138,12 @@
 static void fbcon_free_font(struct display *);
 static int fbcon_set_origin(struct vc_data *);
 
+#ifdef CONFIG_PM
+static int pm_fbcon_request(struct pm_dev *dev, pm_request_t rqst, void *data);
+static struct pm_dev *pm_fbcon;
+static int fbcon_sleeping;
+#endif
+
 /*
  * Emmanuel: fbcon will now use a hardware cursor if the
  * low-level driver provides a non-NULL dispsw->cursor pointer,
@@ -233,6 +240,7 @@
 static struct timer_list cursor_timer = {
     function: cursor_timer_handler
 };
+static int use_timer_cursor;
 
 static void cursor_timer_handler(unsigned long dev_addr)
 {
@@ -457,11 +465,16 @@
 #endif
 
     if (irqres) {
+	use_timer_cursor = 1;
 	cursor_blink_rate = DEFAULT_CURSOR_BLINK_RATE;
 	cursor_timer.expires = jiffies+HZ/50;
 	add_timer(&cursor_timer);
     }
 
+#ifdef CONFIG_PM
+    pm_fbcon = pm_register(PM_SYS_DEV, PM_SYS_VGA, pm_fbcon_request);
+#endif
+
     return display_desc;
 }
 
@@ -1558,6 +1571,10 @@
 
     if (blank < 0)	/* Entering graphics mode */
 	return 0;
+#ifdef CONFIG_PM
+    if (fbcon_sleeping)
+    	return 0;
+#endif /* CONFIG_PM */
 
     fbcon_cursor(p->conp, blank ? CM_ERASE : CM_DRAW);
 
@@ -2446,6 +2463,39 @@
 
     return done ? (LOGO_H + fontheight(p) - 1) / fontheight(p) : 0 ;
 }
+
+#ifdef CONFIG_PM
+/* console.c doesn't do enough here */
+static int
+pm_fbcon_request(struct pm_dev *dev, pm_request_t rqst, void *data)
+{
+	unsigned long flags;
+	
+	switch (rqst)
+	{
+	case PM_RESUME:
+		acquire_console_sem();
+		fbcon_sleeping = 0;
+		if (use_timer_cursor) {
+			cursor_timer.expires = jiffies+HZ/50;
+			add_timer(&cursor_timer);
+		}
+		release_console_sem();
+		break;
+	case PM_SUSPEND:
+		acquire_console_sem();
+		save_flags(flags);
+		cli();
+		if (use_timer_cursor)
+			del_timer(&cursor_timer);
+		fbcon_sleeping = 1;
+		restore_flags(flags);
+		release_console_sem();
+		break;
+	}
+	return 0;
+}
+#endif /* CONFIG_PM */
 
 /*
  *  The console `switch' structure for the frame buffer based console
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/video/imsttfb.c linux/drivers/video/imsttfb.c
--- linux.orig/drivers/video/imsttfb.c	Mon Jan  7 23:41:09 2002
+++ linux/drivers/video/imsttfb.c	Wed Dec 26 16:50:52 2001
@@ -371,7 +371,6 @@
 	TVP = 1
 };
 
-#define USE_NV_MODES		1
 #define INIT_BPP		8
 #define INIT_XRES		640
 #define INIT_YRES		480
@@ -384,7 +383,8 @@
 static char curblink __initdata = 1;
 static char noaccel __initdata = 0;
 #if defined(CONFIG_PPC)
-static signed char init_vmode __initdata = -1, init_cmode __initdata = -1;
+static signed char init_vmode __initdata = VMODE_NVRAM;
+static signed char init_cmode __initdata = CMODE_NVRAM;
 #endif
 
 static struct imstt_regvals tvp_reg_init_2 = {
@@ -1804,20 +1804,25 @@
 		}
 	}
 
-#if USE_NV_MODES && defined(CONFIG_PPC)
+#ifdef CONFIG_ALL_PPC
 	{
 		int vmode = init_vmode, cmode = init_cmode;
 
-		if (vmode == -1) {
+#ifdef CONFIG_NVRAM
+		/* Attempt to read vmode/cmode from NVRAM */
+		if (vmode == VMODE_NVRAM)
 			vmode = nvram_read_byte(NV_VMODE);
-			if (vmode <= 0 || vmode > VMODE_MAX)
-				vmode = VMODE_640_480_67;
-		}
-		if (cmode == -1) {
+		if (cmode == CMODE_NVRAM)
 			cmode = nvram_read_byte(NV_CMODE);
-			if (cmode < CMODE_8 || cmode > CMODE_32)
-				cmode = CMODE_8;
-		}
+#endif
+		/* If we didn't get something from NVRAM, pick a
+		 * sane default.
+		 */
+		if (vmode <= 0 || vmode > VMODE_MAX)
+			vmode = VMODE_640_480_67;
+		if (cmode < CMODE_8 || cmode > CMODE_32)
+			cmode = CMODE_8;
+
 		if (mac_vmode_to_var(vmode, cmode, &p->disp.var)) {
 			p->disp.var.xres = p->disp.var.xres_virtual = INIT_XRES;
 			p->disp.var.yres = p->disp.var.yres_virtual = INIT_YRES;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/video/offb.c linux/drivers/video/offb.c
--- linux.orig/drivers/video/offb.c	Mon Jan  7 23:41:09 2002
+++ linux/drivers/video/offb.c	Wed Dec 26 16:50:52 2001
@@ -52,7 +52,8 @@
 	cmap_r128,	/* ATI Rage128 */
 	cmap_M3A,	/* ATI Rage Mobility M3 Head A */
 	cmap_M3B,	/* ATI Rage Mobility M3 Head B */
-	cmap_radeon	/* ATI Radeon */
+	cmap_radeon,	/* ATI Radeon */
+	cmap_gxt2000	/* IBM GXT2000 */
 };
 
 struct fb_info_offb {
@@ -64,6 +65,7 @@
     volatile unsigned char *cmap_adr;
     volatile unsigned char *cmap_data;
     int cmap_type;
+    int blanked;
     union {
 #ifdef FBCON_HAS_CFB16
 	u16 cfb16[16];
@@ -210,9 +212,11 @@
 static int offb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
 			 struct fb_info *info)
 {
-    if (con == currcon) /* current console? */
+    struct fb_info_offb *info2 = (struct fb_info_offb *)info;
+
+    if (con == currcon && !info2->blanked) /* current console? */
 	return fb_get_cmap(cmap, kspc, offb_getcolreg, info);
-    else if (fb_display[con].cmap.len) /* non default colormap? */
+    if (fb_display[con].cmap.len) /* non default colormap? */
 	fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
     else
     {
@@ -240,7 +244,7 @@
 	if ((err = fb_alloc_cmap(&fb_display[con].cmap, size, 0)))
 	    return err;
     }
-    if (con == currcon)			/* current console? */
+    if (con == currcon && !info2->blanked)		/* current console? */
 	return fb_set_cmap(cmap, kspc, offb_setcolreg, info);
     else
 	fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
@@ -426,7 +430,7 @@
     info->cmap_type = cmap_unknown;
     if (depth == 8)
     {
-    	/* XXX kludge for ati */
+    	/* XXX kludge for ati's */
 	if (dp && !strncmp(name, "ATY,Rage128", 11)) {
 		unsigned long regbase = dp->addrs[2].address;
 		info->cmap_adr = ioremap(regbase, 0x1FFF);
@@ -445,10 +449,19 @@
 		info->cmap_adr = ioremap(regbase, 0x1FFF);
 		info->cmap_type = cmap_radeon;
 	} else if (!strncmp(name, "ATY,", 4)) {
+		/* Hrm... this is bad... any recent ATI not covered
+		 * by the previous cases will get there, while this
+		 * cose is only good for mach64's. Gotta figure out
+		 * a proper fix... --BenH.
+		 */
 		unsigned long base = address & 0xff000000UL;
 		info->cmap_adr = ioremap(base + 0x7ff000, 0x1000) + 0xcc0;
 		info->cmap_data = info->cmap_adr + 1;
 		info->cmap_type = cmap_m64;
+	} else if (dp && device_is_compatible(dp, "pci1014,b7")) {
+		unsigned long regbase = dp->addrs[0].address;
+		info->cmap_adr = ioremap(regbase + 0x6000, 0x1000);
+		info->cmap_type = cmap_gxt2000;
 	}
         fix->visual = info->cmap_adr ? FB_VISUAL_PSEUDOCOLOR
 				     : FB_VISUAL_STATIC_PSEUDOCOLOR;
@@ -620,8 +633,10 @@
 
 static int offbcon_switch(int con, struct fb_info *info)
 {
+    struct fb_info_offb *info2 = (struct fb_info_offb *)info;
+
     /* Do we have to save the colormap? */
-    if (fb_display[currcon].cmap.len)
+    if (fb_display[currcon].cmap.len && !info2->blanked)
 	fb_get_cmap(&fb_display[currcon].cmap, 1, offb_getcolreg, info);
 
     currcon = con;
@@ -652,6 +667,15 @@
     if (!info2->cmap_adr)
 	return;
 
+    if (!info2->blanked) {
+	if (!blank)
+	    return;
+	if (fb_display[currcon].cmap.len)
+	    fb_get_cmap(&fb_display[currcon].cmap, 1, offb_getcolreg, info);
+    }
+
+    info2->blanked = blank;
+
     if (blank)
 	for (i = 0; i < 256; i++) {
 	    switch(info2->cmap_type) {
@@ -664,26 +688,29 @@
 	    	}
 	    	break;
 	    case cmap_M3A:
-	        /* Clear PALETTE_ACCESS_CNTL in DAC_CNTL */
-	    	out_le32((unsigned *)(info2->cmap_adr + 0x58),
-	    		in_le32((unsigned *)(info2->cmap_adr + 0x58)) & ~0x20);
+		/* Clear PALETTE_ACCESS_CNTL in DAC_CNTL */
+		out_le32((unsigned *)(info2->cmap_adr + 0x58),
+			in_le32((unsigned *)(info2->cmap_adr + 0x58)) & ~0x20);
 	    case cmap_r128:
-	    	/* Set palette index & data */
-    	        out_8(info2->cmap_adr + 0xb0, i);
-	    	out_le32((unsigned *)(info2->cmap_adr + 0xb4), 0);
-	    	break;
+		/* Set palette index & data */
+		out_8(info2->cmap_adr + 0xb0, i);
+		out_le32((unsigned *)(info2->cmap_adr + 0xb4), 0);
+		break;
 	    case cmap_M3B:
-	        /* Set PALETTE_ACCESS_CNTL in DAC_CNTL */
-	    	out_le32((unsigned *)(info2->cmap_adr + 0x58),
-	    		in_le32((unsigned *)(info2->cmap_adr + 0x58)) | 0x20);
-	    	/* Set palette index & data */
-	    	out_8(info2->cmap_adr + 0xb0, i);
-	    	out_le32((unsigned *)(info2->cmap_adr + 0xb4), 0);
-	    	break;
+		/* Set PALETTE_ACCESS_CNTL in DAC_CNTL */
+		out_le32((unsigned *)(info2->cmap_adr + 0x58),
+			in_le32((unsigned *)(info2->cmap_adr + 0x58)) | 0x20);
+		/* Set palette index & data */
+		out_8(info2->cmap_adr + 0xb0, i);
+		out_le32((unsigned *)(info2->cmap_adr + 0xb4), 0);
+		break;
 	    case cmap_radeon:
-    	        out_8(info2->cmap_adr + 0xb0, i);
-	    	out_le32((unsigned *)(info2->cmap_adr + 0xb4), 0);
-	    	break;
+		out_8(info2->cmap_adr + 0xb0, i);
+		out_le32((unsigned *)(info2->cmap_adr + 0xb4), 0);
+		break;
+	    case cmap_gxt2000:
+		out_le32((unsigned *)info2->cmap_adr + i, 0);
+		break;
 	    }
 	}
     else
@@ -768,6 +795,10 @@
 	out_8(info2->cmap_adr + 0xb0, regno);
   	out_le32((unsigned *)(info2->cmap_adr + 0xb4),
     		(red << 16 | green << 8 | blue));
+	break;
+    case cmap_gxt2000:
+	out_le32((unsigned *)info2->cmap_adr + regno,
+		 (red << 16 | green << 8 | blue));
 	break;
     }
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/video/platinumfb.c linux/drivers/video/platinumfb.c
--- linux.orig/drivers/video/platinumfb.c	Mon Jan  7 23:41:09 2002
+++ linux/drivers/video/platinumfb.c	Wed Dec 26 16:50:52 2001
@@ -126,7 +126,9 @@
  */
 
 static void platinum_of_init(struct device_node *dp);
-static inline int platinum_vram_reqd(int video_mode, int color_mode);
+static inline int platinum_vram_reqd(const struct fb_info_platinum* info,
+					int video_mode,
+					int color_mode);
 static int read_platinum_sense(struct fb_info_platinum *info);
 static void set_platinum_clock(struct fb_info_platinum *info);
 static void platinum_set_par(const struct fb_par_platinum *par, struct fb_info_platinum *info);
@@ -443,10 +445,12 @@
 	}
 }
 
-static inline int platinum_vram_reqd(int video_mode, int color_mode)
+static inline int platinum_vram_reqd(const struct fb_info_platinum *info, int video_mode, int color_mode)
 {
-	return vmode_attrs[video_mode-1].vres *
-	       (vmode_attrs[video_mode-1].hres * (1<<color_mode) + 0x20) +0x1000;
+	unsigned int pitch = 
+	       (vmode_attrs[video_mode-1].hres * (1<<color_mode) + 0x20);
+	fixup_pitch(pitch, info, color_mode);
+	return vmode_attrs[video_mode-1].vres * pitch;
 }
 
 #define STORE_D2(a, d) { \
@@ -487,7 +491,7 @@
 	volatile struct cmap_regs	*cmap_regs = info->cmap_regs;
 	struct platinum_regvals		*init;
 	int				i;
-	int				vmode, cmode;
+	int				vmode, cmode, pitch;
 	
 	info->current_par = *par;
 
@@ -506,7 +510,9 @@
 						init->offset[cmode] + 4 - cmode :
 						init->offset[cmode]));
 	out_be32(&platinum_regs->reg[16].r, (unsigned) info->frame_buffer_phys+init->fb_offset+0x10);
-	out_be32(&platinum_regs->reg[18].r, init->pitch[cmode]);
+	pitch = init->pitch[cmode];
+	fixup_pitch(pitch, info, cmode);
+	out_be32(&platinum_regs->reg[18].r, pitch);
 	out_be32(&platinum_regs->reg[19].r, (info->total_vram == 0x100000 ?
 					     init->mode[cmode+1] :
 					     init->mode[cmode]));
@@ -535,6 +541,7 @@
 		display_info.depth = ( (cmode == CMODE_32) ? 32 :
 				      ((cmode == CMODE_16) ? 16 : 8));
 		display_info.pitch = vmode_attrs[vmode-1].hres * (1<<cmode) + 0x20;
+		fixup_pitch(display_info.pitch, info, cmode);
 		display_info.mode = vmode;
 		strncpy(display_info.name, "platinum",
 			sizeof(display_info.name));
@@ -558,25 +565,27 @@
 	sense = read_platinum_sense(info);
 	printk(KERN_INFO "Monitor sense value = 0x%x, ", sense);
 
+#ifdef CONFIG_NVRAM
 	if (default_vmode == VMODE_NVRAM) {
 		default_vmode = nvram_read_byte(NV_VMODE);
 		if (default_vmode <= 0 || default_vmode > VMODE_MAX ||
 		    !platinum_reg_init[default_vmode-1])
 			default_vmode = VMODE_CHOOSE;
 	}
-	if (default_vmode == VMODE_CHOOSE) {
+	if (default_cmode == CMODE_NVRAM)
+		default_cmode = nvram_read_byte(NV_CMODE);
+#endif
+	if (default_vmode == VMODE_CHOOSE)
 		default_vmode = mac_map_monitor_sense(sense);
-	}
 	if (default_vmode <= 0 || default_vmode > VMODE_MAX)
 		default_vmode = VMODE_640_480_60;
-	if (default_cmode == CMODE_NVRAM)
-		default_cmode = nvram_read_byte(NV_CMODE);
 	if (default_cmode < CMODE_8 || default_cmode > CMODE_32)
 		default_cmode = CMODE_8;
 	/*
 	 * Reduce the pixel size if we don't have enough VRAM.
 	 */
-	while(default_cmode > CMODE_8 && platinum_vram_reqd(default_vmode, default_cmode) > info->total_vram)
+	while(default_cmode > CMODE_8 && platinum_vram_reqd(info, default_vmode, default_cmode)
+	    > info->total_vram)
 		default_cmode--;
 
 	printk("using video mode %d and color mode %d.\n", default_vmode, default_cmode);
@@ -782,7 +791,7 @@
 		return -EINVAL;
 	}
 
-	if (platinum_vram_reqd(par->vmode, par->cmode) > info->total_vram) {
+	if (platinum_vram_reqd(info, par->vmode, par->cmode) > info->total_vram) {
 		printk(KERN_ERR "platinum_var_to_par, not enough ram for vmode %d, cmode %d.\n", par->vmode, par->cmode);
 		return -EINVAL;
 	}
@@ -826,7 +835,8 @@
 	fix->visual = (par->cmode == CMODE_8) ?
 		FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_DIRECTCOLOR;
 	fix->line_length = vmode_attrs[par->vmode-1].hres * (1<<par->cmode) + 0x20;
-
+	fixup_pitch(fix->line_length, info, par->cmode);
+	
 	return 0;
 }
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/video/platinumfb.h linux/drivers/video/platinumfb.h
--- linux.orig/drivers/video/platinumfb.h	Mon Jan  7 23:41:09 2002
+++ linux/drivers/video/platinumfb.h	Wed Dec 26 16:50:52 2001
@@ -158,7 +158,7 @@
 /* 832x624, 75Hz (13) */
 static struct platinum_regvals platinum_reg_init_13 = {
 	0x70,
-	{ 864, 1680, 3360 },	/* MacOS does 1680 instead of 1696 to fit 16bpp in 1MB */
+	{ 864, 1696, 3360 },
 	{ 0xff0, 4, 0, 0, 0, 0, 0x299, 0,
 	  0, 0x21e, 0x120, 0x10, 0x23f, 0x1f, 0x25, 0x37,
 	  0x8a, 0x22a, 0x23e, 0x536, 0x534, 4, 9, 0x52,
@@ -310,6 +310,13 @@
 	{ 2, 0, 0xff }, { 0x11, 0x15, 0x19 },
 	{{ 94, 5 + DIV16 }, { 48, 7 + DIV8 }}
 };
+
+/* MacOS does 1680 instead of 1696 to fit 832x624@75-16bpp in 1MB */
+#define fixup_pitch(ll, info, cmode) \
+	do { \
+	  if ((cmode) == CMODE_16 && (ll) == 1696 && info->total_vram == 0x100000) \
+	      (ll) = 1680; \
+	} while(0)
 
 static struct platinum_regvals *platinum_reg_init[VMODE_MAX] = {
 	&platinum_reg_init_1,
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/video/radeonfb.c linux/drivers/video/radeonfb.c
--- linux.orig/drivers/video/radeonfb.c	Mon Jan  7 23:41:09 2002
+++ linux/drivers/video/radeonfb.c	Fri Dec 21 18:02:33 2001
@@ -15,13 +15,16 @@
  *			blanking, pan_display, and cmap fixes, 0.1.0
  *	2001-10-10	Radeon 7500 and 8500 support, and experimental
  *			flat panel support, 0.1.1
+ *	2001-11-17	Radeon M6 (ppc) support, Daniel Berlin, 0.1.2
+ *	2001-11-18	DFP fixes, Kevin Hendricks, 0.1.3
+ *	2001-11-29	more cmap, backlight fixes, Benjamin Herrenschmidt
  *
  *	Special thanks to ATI DevRel team for their hardware donations.
  *
  */
 
 
-#define RADEON_VERSION	"0.1.1"
+#define RADEON_VERSION	"0.1.3"
 
 
 #include <linux/config.h>
@@ -39,12 +42,33 @@
 #include <linux/ioport.h>
 #include <linux/init.h>
 #include <linux/pci.h>
+#include <linux/vmalloc.h>
 
 #include <asm/io.h>
 #if defined(__powerpc__)
 #include <asm/prom.h>
+#include <asm/pci-bridge.h>
+#include <video/macmodes.h>
+
+#ifdef CONFIG_NVRAM
+#include <linux/nvram.h>
+#endif
+
+#ifdef CONFIG_PMAC_BACKLIGHT
+#include <asm/backlight.h>
+#endif
+
+#ifdef CONFIG_BOOTX_TEXT
+#include <asm/btext.h>
+#endif
+
+#ifdef CONFIG_ADB_PMU
+#include <linux/adb.h>
+#include <linux/pmu.h>
 #endif
 
+#endif /* __powerpc__ */
+
 #include <video/fbcon.h> 
 #include <video/fbcon-cfb8.h>
 #include <video/fbcon-cfb16.h>
@@ -65,17 +89,17 @@
 
 
 enum radeon_chips {
-	RADEON_QD,
-	RADEON_QE,
-	RADEON_QF,
-	RADEON_QG,
-	RADEON_QY,
-	RADEON_QZ,
-	RADEON_QL,
-	RADEON_QW,
-	RADEON_LW,
-	RADEON_LY,
-	RADEON_LZ
+	RADEON_QD,	/* Radeon R100 */
+	RADEON_QE,	/* Radeon R100 */
+	RADEON_QF,	/* Radeon R100 */
+	RADEON_QG,	/* Radeon R100 */
+	RADEON_QY,	/* Radeon RV100 (VE) */
+	RADEON_QZ,	/* Radeon RV100 (VE) */
+	RADEON_QL,	/* Radeon R200 (8500) */
+	RADEON_QW,	/* Radeon RV200 (7500) */
+	RADEON_LW,	/* Radeon Mobility M7 */
+	RADEON_LY,	/* Radeon Mobility M6 */
+	RADEON_LZ	/* Radeon Mobility M6 */
 };
 
 
@@ -192,7 +216,6 @@
 	u32 flags;
 	u32 pix_clock;
 	int xres, yres;
-	int bpp;
 
 	/* DDA regs */
 	u32 dda_config;
@@ -214,6 +237,7 @@
 	u32 lvds_gen_cntl;
 	u32 lvds_pll_cntl;
 	u32 tmds_crc;
+	u32 tmds_transmitter_cntl;
 
 #if defined(__BIG_ENDIAN)
 	u32 surface_cntl;
@@ -238,6 +262,8 @@
 
 	struct pci_dev *pdev;
 
+	unsigned char *EDID;
+
 	struct display disp;
 	int currcon;
 	struct display *currcon_display;
@@ -250,13 +276,18 @@
 	int pitch, bpp, depth;
 	int xres, yres, pixclock;
 
+	int use_default_var;
+
 	int hasCRTC2;
 	int crtDisp_type;
 	int dviDisp_type;
 
 	int panel_xres, panel_yres;
+	int clock;
 	int hOver_plus, hSync_width, hblank;
 	int vOver_plus, vSync_width, vblank;
+	int hAct_high, vAct_high, interlaced;
+	int synct, misc;
 
 	u32 dp_gui_master_cntl;
 
@@ -281,6 +312,13 @@
 #endif  
         } con_cmap;
 #endif  
+
+#ifdef CONFIG_PMAC_PBOOK
+	unsigned char *save_framebuffer;
+	int pm_reg;
+#endif
+
+	struct radeonfb_info *next;
 };
 
 
@@ -433,6 +471,14 @@
 }
 
 
+static inline int var_to_depth(const struct fb_var_screeninfo *var)
+{
+	if (var->bits_per_pixel != 16)
+		return var->bits_per_pixel;
+	return (var->green.length == 6) ? 16 : 15;
+}
+
+
 static void _radeon_engine_reset(struct radeonfb_info *rinfo)
 {
 	u32 clock_cntl_index, mclk_cntl, rbbm_soft_reset;
@@ -546,7 +592,9 @@
 static char fontname[40] __initdata;
 static char *mode_option __initdata;
 static char noaccel __initdata = 0;
-static char panel_yres __initdata = 0;
+static int panel_yres __initdata = 0;
+static char force_dfp __initdata = 0;
+static struct radeonfb_info *board_list = NULL;
 
 #ifdef FBCON_HAS_CFB8
 static struct display_switch fbcon_radeon8;
@@ -598,11 +646,30 @@
 static void radeon_get_pllinfo(struct radeonfb_info *rinfo, char *bios_seg);
 static void radeon_get_moninfo (struct radeonfb_info *rinfo);
 static int radeon_get_dfpinfo (struct radeonfb_info *rinfo);
-static int radeon_read_OF (struct radeonfb_info *rinfo);
+static void radeon_get_EDID(struct radeonfb_info *rinfo);
+static int radeon_dfp_parse_EDID(struct radeonfb_info *rinfo);
+static void radeon_update_default_var(struct radeonfb_info *rinfo);
 
-#if defined(__powerpc__)
+
+#ifdef CONFIG_ALL_PPC
+static int radeon_read_OF (struct radeonfb_info *rinfo);
+static int radeon_get_EDID_OF(struct radeonfb_info *rinfo);
 extern struct device_node *pci_device_to_OF_node(struct pci_dev *dev);
-#endif
+
+#ifdef CONFIG_PMAC_PBOOK
+int radeon_sleep_notify(struct pmu_sleep_notifier *self, int when);
+static struct pmu_sleep_notifier radeon_sleep_notifier = {
+	radeon_sleep_notify, SLEEP_LEVEL_VIDEO,
+};
+static int radeon_set_backlight_enable(int on, int level, void *data);
+static int radeon_set_backlight_level(int level, void *data);
+static struct backlight_controller radeon_backlight_controller = {
+	radeon_set_backlight_enable,
+	radeon_set_backlight_level
+};
+#endif /* CONFIG_PMAC_PBOOK */
+
+#endif /* CONFIG_ALL_PPC */
 
 static struct fb_ops radeon_fb_ops = {
 	fb_get_fix:		radeonfb_get_fix,
@@ -619,7 +686,7 @@
 	name:		"radeonfb",
 	id_table:	radeonfb_pci_table,
 	probe:		radeonfb_pci_register,
-	remove:		__devexit_p(radeonfb_pci_unregister),
+	remove:		radeonfb_pci_unregister,
 };
 
 
@@ -656,6 +723,8 @@
                         memcpy(fontname, this_opt + 5, i);
                 } else if (!strncmp(this_opt, "noaccel", 7)) {
 			noaccel = 1;
+		} else if (!strncmp(this_opt, "dfp", 3)) {
+			force_dfp = 1;
 		} else if (!strncmp(this_opt, "panel_yres:", 11)) {
 			panel_yres = simple_strtoul((this_opt+11), NULL, 0);
                 } else
@@ -845,7 +914,23 @@
 
 	RTRACE("radeonfb: probed %s %dk videoram\n", (rinfo->ram_type), (rinfo->video_ram/1024));
 
+#if !defined(__powerpc__)
 	radeon_get_moninfo(rinfo);
+#else
+	switch (pdev->device) {
+		case PCI_DEVICE_ID_RADEON_LW:
+		case PCI_DEVICE_ID_RADEON_LY:
+		case PCI_DEVICE_ID_RADEON_LZ:
+			rinfo->dviDisp_type = MT_LCD;
+			break;
+		default:
+			radeon_get_moninfo(rinfo);
+			break;
+	}
+#endif
+
+	radeon_get_EDID(rinfo);
+
 	if ((rinfo->dviDisp_type == MT_DFP) || (rinfo->dviDisp_type == MT_LCD) ||
 	    (rinfo->crtDisp_type == MT_DFP)) {
 		if (!radeon_get_dfpinfo(rinfo)) {
@@ -875,6 +960,9 @@
 	/* XXX turn off accel for now, blts aren't working right */
 	noaccel = 1;
 
+	/* currcon not yet configured, will be set by first switch */
+	rinfo->currcon = -1;
+
 	/* set all the vital stuff */
 	radeon_set_fbinfo (rinfo);
 
@@ -892,6 +980,8 @@
 	}
 
 	pci_set_drvdata(pdev, rinfo);
+	rinfo->next = board_list;
+	board_list = rinfo;
 
 	if (register_framebuffer ((struct fb_info *) rinfo) < 0) {
 		printk ("radeonfb: could not register framebuffer\n");
@@ -910,6 +1000,19 @@
 		radeon_engine_init (rinfo);
 	}
 
+#ifdef CONFIG_PMAC_BACKLIGHT
+	if (rinfo->dviDisp_type == MT_LCD)
+		register_backlight_controller(&radeon_backlight_controller,
+					      rinfo, "ati");
+#endif
+
+#ifdef CONFIG_PMAC_PBOOK
+	if (rinfo->dviDisp_type == MT_LCD) {
+		rinfo->pm_reg = pci_find_capability(pdev, PCI_CAP_ID_PM);
+		pmu_register_sleep_notifier(&radeon_sleep_notifier);
+	}
+#endif
+
 	printk ("radeonfb: ATI %s %s %d MB\n", rinfo->name, rinfo->ram_type,
 		(rinfo->video_ram/(1024*1024)));
 
@@ -1054,7 +1157,7 @@
 		printk("radeonfb: ref_clk=%d, ref_div=%d, xclk=%d from BIOS\n",
 			rinfo->pll.ref_clk, rinfo->pll.ref_div, rinfo->pll.xclk);
 	} else {
-#if defined(__powerpc__)
+#ifdef CONFIG_ALL_PPC
 		if (radeon_read_OF(rinfo)) {
 			unsigned int tmp, Nx, M, ref_div, xclk;
 
@@ -1116,6 +1219,11 @@
 {
 	unsigned int tmp;
 
+	if (force_dfp) {
+		rinfo->dviDisp_type = MT_DFP;
+		return;
+	}
+
 	tmp = INREG(RADEON_BIOS_4_SCRATCH);
 
 	if (rinfo->hasCRTC2) {
@@ -1155,11 +1263,116 @@
 }
 
 
+
+static void radeon_get_EDID(struct radeonfb_info *rinfo)
+{
+#ifdef CONFIG_ALL_PPC
+	if (!radeon_get_EDID_OF(rinfo))
+		RTRACE("radeonfb: could not retrieve EDID from OF\n");
+#else
+	/* XXX use other methods later */
+#endif
+}
+
+
+#ifdef CONFIG_ALL_PPC
+static int radeon_get_EDID_OF(struct radeonfb_info *rinfo)
+{
+	struct device_node *dp;
+	unsigned char *pedid = NULL;
+
+	dp = pci_device_to_OF_node(rinfo->pdev);
+	pedid = (unsigned char *) get_property(dp, "DFP,EDID", 0);
+	if (!pedid)
+		pedid = (unsigned char *) get_property(dp, "LCD,EDID", 0);
+	if (!pedid)
+		pedid = (unsigned char *) get_property(dp, "EDID", 0);
+
+	if (pedid) {
+		rinfo->EDID = pedid;
+		return 1;
+	} else
+		return 0;
+}
+#endif /* CONFIG_ALL_PPC */
+
+
+static int radeon_dfp_parse_EDID(struct radeonfb_info *rinfo)
+{
+	unsigned char *block = rinfo->EDID;
+
+	if (!block)
+		return 0;
+
+	/* jump to the detailed timing block section */
+	block += 54;
+
+	rinfo->clock = (block[0] + (block[1] << 8));
+	rinfo->panel_xres = (block[2] + ((block[4] & 0xf0) << 4));
+	rinfo->hblank = (block[3] + ((block[4] & 0x0f) << 8));
+	rinfo->panel_yres = (block[5] + ((block[7] & 0xf0) << 4));
+	rinfo->vblank = (block[6] + ((block[7] & 0x0f) << 8));
+	rinfo->hOver_plus = (block[8] + ((block[11] & 0xc0) << 2));
+	rinfo->hSync_width = (block[9] + ((block[11] & 0x30) << 4));
+	rinfo->vOver_plus = ((block[10] >> 4) + ((block[11] & 0x0c) << 2));
+	rinfo->vSync_width = ((block[10] & 0x0f) + ((block[11] & 0x03) << 4));
+	rinfo->interlaced = ((block[17] & 0x80) >> 7);
+	rinfo->synct = ((block[17] & 0x18) >> 3);
+	rinfo->misc = ((block[17] & 0x06) >> 1);
+	rinfo->hAct_high = rinfo->vAct_high = 0;
+	if (rinfo->synct == 3) {
+		if (rinfo->misc & 2)
+			rinfo->hAct_high = 1;
+		if (rinfo->misc & 1)
+			rinfo->vAct_high = 1;
+	}
+
+	return 1;
+}
+
+
+static void radeon_update_default_var(struct radeonfb_info *rinfo)
+{
+	struct fb_var_screeninfo *var = &radeonfb_default_var;
+
+	var->xres = rinfo->panel_xres;
+	var->yres = rinfo->panel_yres;
+	var->xres_virtual = rinfo->panel_xres;
+	var->yres_virtual = rinfo->panel_yres;
+	var->xoffset = var->yoffset = 0;
+	var->bits_per_pixel = 8;
+	var->pixclock = 100000000 / rinfo->clock;
+	var->left_margin = (rinfo->hblank - rinfo->hOver_plus - rinfo->hSync_width);
+	var->right_margin = rinfo->hOver_plus;
+	var->upper_margin = (rinfo->vblank - rinfo->vOver_plus - rinfo->vSync_width);
+	var->lower_margin = rinfo->vOver_plus;
+	var->hsync_len = rinfo->hSync_width;
+	var->vsync_len = rinfo->vSync_width;
+	var->sync = 0;
+	if (rinfo->synct == 3) {
+		if (rinfo->hAct_high)
+			var->sync |= FB_SYNC_HOR_HIGH_ACT;
+		if (rinfo->vAct_high)
+			var->sync |= FB_SYNC_VERT_HIGH_ACT;
+	}
+
+	var->vmode = 0;
+	if (rinfo->interlaced)
+		var->vmode |= FB_VMODE_INTERLACED;
+
+	rinfo->use_default_var = 1;
+}
+
+
+
 static int radeon_get_dfpinfo (struct radeonfb_info *rinfo)
 {
 	unsigned int tmp;
 	unsigned short a, b;
 
+	if (radeon_dfp_parse_EDID(rinfo))
+		radeon_update_default_var(rinfo);
+
 	if (panel_yres) {
 		rinfo->panel_yres = panel_yres;
 	} else {
@@ -1175,7 +1388,12 @@
 		case 600:
 			rinfo->panel_xres = 800;
 			break;
-		case 786:
+		case 768:
+#if defined(__powerpc__)
+			if (rinfo->dviDisp_type == MT_LCD)
+				rinfo->panel_xres = 1152;
+			else
+#endif
 			rinfo->panel_xres = 1024;
 			break;
 		case 1024:
@@ -1222,7 +1440,7 @@
 }
 
 
-#if defined(__powerpc__)
+#ifdef CONFIG_ALL_PPC
 static int radeon_read_OF (struct radeonfb_info *rinfo)
 {
 	struct device_node *dp;
@@ -1255,7 +1473,7 @@
 	OUTREG(DSTCACHE_MODE, 0);
 
 	/* XXX */
-	rinfo->pitch = ((rinfo->xres * (rinfo->depth / 8) + 0x3f)) >> 6;
+	rinfo->pitch = ((rinfo->xres * (rinfo->bpp / 8) + 0x3f)) >> 6;
 
 	radeon_fifo_wait (1);
 	temp = INREG(DEFAULT_PITCH_OFFSET);
@@ -1333,6 +1551,16 @@
         disp = &rinfo->disp;
         
         disp->var = radeonfb_default_var;
+#if defined(__powerpc__)
+	if (rinfo->dviDisp_type == MT_LCD) {
+		if (mac_vmode_to_var(VMODE_1152_768_60, CMODE_8, &disp->var))
+			disp->var = radeonfb_default_var;
+	}
+#endif
+
+	rinfo->depth = var_to_depth(&disp->var);
+	rinfo->bpp = disp->var.bits_per_pixel;
+
         info->disp = disp;
 
         radeon_set_dispsw (rinfo, disp);
@@ -1360,6 +1588,18 @@
                               NULL, 0, NULL, 8);
         else
 #endif
+#if defined(__powerpc__)
+	if (rinfo->dviDisp_type == MT_LCD) {
+		if (mac_vmode_to_var(VMODE_1152_768_60, CMODE_8, &rinfo->disp.var))
+			rinfo->disp.var = radeonfb_default_var;
+	}
+	else
+#endif
+	if (rinfo->use_default_var)
+		/* We will use the modified default far */
+		rinfo->disp.var = radeonfb_default_var;
+	else
+
                 fb_find_mode (&rinfo->disp.var, &rinfo->info, "640x480-8@60",
                               NULL, 0, NULL, 0);
 
@@ -1390,7 +1630,6 @@
         disp->can_soft_blank = 1;
         disp->inverse = 0;
 
-        rinfo->depth = disp->var.bits_per_pixel;
         switch (disp->var.bits_per_pixel) {
 #ifdef FBCON_HAS_CFB8
                 case 8:
@@ -1443,7 +1682,7 @@
         if (fb_display[con].cmap.len)
                 fb_set_cmap(&fb_display[con].cmap, 1, radeon_setcolreg, info);
         else {
-                int size = fb_display[con].var.bits_per_pixel == 8 ? 256 : 32;
+		int size = radeon_get_cmap_len(&fb_display[con].var);
                 fb_set_cmap(fb_default_cmap(size), 1, radeon_setcolreg, info);
         }
 }
@@ -1535,7 +1774,7 @@
         fix->type_aux = disp->type_aux;
         fix->visual = disp->visual;
 
-        fix->xpanstep = 1;
+        fix->xpanstep = 8;
         fix->ypanstep = 1;
         fix->ywrapstep = 0;
         
@@ -1592,6 +1831,23 @@
         memcpy (&v, var, sizeof (v));
 
         switch (v.bits_per_pixel) {
+		case 0 ... 8:
+			v.bits_per_pixel = 8;
+			break;
+		case 9 ... 16:
+			v.bits_per_pixel = 16;
+			break;
+		case 17 ... 24:
+			v.bits_per_pixel = 24;
+			break;
+		case 25 ... 32:
+			v.bits_per_pixel = 32;
+			break;
+		default:
+			return -EINVAL;
+	}
+
+	switch (var_to_depth(&v)) {
 #ifdef FBCON_HAS_CFB8
                 case 8:
                         nom = den = 1;
@@ -1604,6 +1860,17 @@
 #endif
                         
 #ifdef FBCON_HAS_CFB16
+		case 15:
+			nom = 2;
+			den = 1;
+			disp->line_length = v.xres_virtual * 2;
+			disp->visual = FB_VISUAL_DIRECTCOLOR;
+			v.red.offset = 10;
+			v.green.offset = 5;
+			v.red.offset = 0;
+			v.red.length = v.green.length = v.blue.length = 5;
+			v.transp.offset = v.transp.length = 0;
+			break;
                 case 16:
                         nom = 2;
                         den = 1;
@@ -1785,7 +2052,7 @@
         struct radeonfb_info *rinfo = (struct radeonfb_info *) info;
         struct display *disp;
         struct fb_cmap *cmap;
-        int switchcon = 0;
+        int switchmode = 0;
         
         disp = (con < 0) ? rinfo->info.disp : &fb_display[con];
                 
@@ -1795,17 +2062,13 @@
                         fb_get_cmap (cmap, 1, radeon_getcolreg, info);
         }   
                 
-        if ((disp->var.xres != rinfo->xres) ||
-            (disp->var.yres != rinfo->yres) ||
-            (disp->var.pixclock != rinfo->pixclock) ||
-            (disp->var.bits_per_pixel != rinfo->depth))
-                switchcon = 1;
-        
-        if (switchcon) {
-                rinfo->currcon = con;
-                rinfo->currcon_display = disp;
-                disp->var.activate = FB_ACTIVATE_NOW;
+	switchmode = (con != rinfo->currcon);
+
+	rinfo->currcon = con;
+	rinfo->currcon_display = disp;
+	disp->var.activate = FB_ACTIVATE_NOW;
         
+        if (switchmode) {
                 radeonfb_set_var (&disp->var, con, info);
                 radeon_set_dispsw (rinfo, disp);
                 do_install_cmap(con, info);
@@ -1837,11 +2100,20 @@
 {
         struct radeonfb_info *rinfo = (struct radeonfb_info *) info;
         u32 val = INREG(CRTC_EXT_CNTL);
+	u32 val2 = INREG(LVDS_GEN_CNTL);
+
+#ifdef CONFIG_PMAC_BACKLIGHT
+	if (rinfo->dviDisp_type == MT_LCD && _machine == _MACH_Pmac) {
+		set_backlight_enable(!blank);
+		return;
+	}
+#endif
                         
         /* reset it */
         val &= ~(CRTC_DISPLAY_DIS | CRTC_HSYNC_DIS |
                  CRTC_VSYNC_DIS);
-                
+	val2 &= ~(LVDS_DISPLAY_DIS);
+
         switch (blank) {
                 case VESA_NO_BLANKING:
                         break;
@@ -1854,25 +2126,33 @@
                 case VESA_POWERDOWN:
                         val |= (CRTC_DISPLAY_DIS | CRTC_VSYNC_DIS | 
                                 CRTC_HSYNC_DIS);
+			val2 |= (LVDS_DISPLAY_DIS);
                         break;
         }
-        
-        OUTREG(CRTC_EXT_CNTL, val);
-}
 
+	switch (rinfo->dviDisp_type) {
+		case MT_LCD:
+			OUTREG(LVDS_GEN_CNTL, val2);
+			break;
+		case MT_CRT:
+		default:
+		        OUTREG(CRTC_EXT_CNTL, val);
+			break;
+	}
+}
 
 
 static int radeon_get_cmap_len (const struct fb_var_screeninfo *var)
 {
-        int rc = 16;            /* reasonable default */
+        int rc = 256;            /* reasonable default */
         
-        switch (var->bits_per_pixel) {
-                case 8:
-                        rc = 256;
-                        break;
-                default:
+        switch (var_to_depth(var)) {
+                case 15:
                         rc = 32;
                         break;
+		case 16:
+			rc = 64;
+			break;
         }
                 
         return rc;
@@ -1915,40 +2195,38 @@
 	rinfo->palette[regno].green = green;
 	rinfo->palette[regno].blue = blue;
 
-        /* init gamma for hicolor */
-        if ((rinfo->depth > 8) && (regno == 0)) {
-                int i;
-                
-                for (i=0; i<255; i++) {
-                        OUTREG(PALETTE_INDEX, i);
-                        OUTREG(PALETTE_DATA, (i << 16) | (i << 8) | i);
-                }
-        }
-         
         /* default */
         pindex = regno;
         
-        /* XXX actually bpp, fixme */
-        if (rinfo->depth == 16)     
-                pindex  = regno * 8;
-                
-        if (rinfo->depth == 16) {
-                OUTREG(PALETTE_INDEX, pindex/2);
-                OUTREG(PALETTE_DATA, (rinfo->palette[regno/2].red << 16) |
-                        (green << 8) | (rinfo->palette[regno/2].blue));
-                green = rinfo->palette[regno/2].green;
-        }
-                
-        if ((rinfo->depth == 8) || (regno < 32)) {
-                OUTREG(PALETTE_INDEX, pindex);
-                OUTREG(PALETTE_DATA, (red << 16) | (green << 8) | blue);
-        }
+	if (rinfo->bpp == 16) {
+		pindex = regno * 8;
 
+		if (rinfo->depth == 16 && regno > 63)
+			return 1;
+		if (rinfo->depth == 15 && regno > 31)
+			return 1;
 
-#if defined(FBCON_HAS_CFB16) || defined(FBCON_HAS_CFB32)
- 	if (regno < 32) {
+		/* For 565, the green component is mixed one order below */
+		if (rinfo->depth == 16) {
+	                OUTREG(PALETTE_INDEX, pindex>>1);
+       	         	OUTREG(PALETTE_DATA, (rinfo->palette[regno>>1].red << 16) |
+                        	(green << 8) | (rinfo->palette[regno>>1].blue));
+                	green = rinfo->palette[regno<<1].green;
+        	}
+	}
+
+	if (rinfo->depth != 16 || regno < 32) {
+		OUTREG(PALETTE_INDEX, pindex);
+		OUTREG(PALETTE_DATA, (red << 16) | (green << 8) | blue);
+	}
+
+ 	if (regno < 16) {
         	switch (rinfo->depth) {
 #ifdef FBCON_HAS_CFB16
+		        case 15:
+        			rinfo->con_cmap.cfb16[regno] = (regno << 10) | (regno << 5) |
+				                       	 	  regno;   
+			        break;
 		        case 16:
         			rinfo->con_cmap.cfb16[regno] = (regno << 11) | (regno << 5) |
 				                       	 	  regno;   
@@ -1970,7 +2248,6 @@
 #endif
 		}
         }
-#endif
 	return 0;
 }
 
@@ -2003,6 +2280,7 @@
 	save->lvds_gen_cntl = INREG(LVDS_GEN_CNTL);
 	save->lvds_pll_cntl = INREG(LVDS_PLL_CNTL);
 	save->tmds_crc = INREG(TMDS_CRC);
+	save->tmds_transmitter_cntl = INREG(TMDS_TRANSMITTER_CNTL);
 }
 
 
@@ -2023,6 +2301,7 @@
         int min_bits, format = 0;
 	int hsync_start, hsync_fudge, bytpp, hsync_wid, vsync_wid;
 	int primary_mon = PRIMARY_MONITOR(rinfo);
+	int depth = var_to_depth(mode);
 
 	rinfo->xres = mode->xres;
 	rinfo->yres = mode->yres;
@@ -2042,13 +2321,13 @@
 		if (rinfo->panel_yres < mode->yres)
 			rinfo->yres = mode->yres = rinfo->panel_yres;
 
-		hTotal = mode->xres + rinfo->hblank + mode->left_margin;
-		hSyncStart = mode->xres + rinfo->hOver_plus + mode->right_margin;
-		hSyncEnd = hSyncStart + rinfo->hOver_plus + mode->hsync_len;
-
-		vTotal = mode->yres + rinfo->vblank + mode->upper_margin;
-		vSyncStart = mode->yres + rinfo->vOver_plus + mode->lower_margin;
-		vSyncEnd = vSyncStart + rinfo->vSync_width + mode->vsync_len;
+		hTotal = mode->xres + rinfo->hblank;
+		hSyncStart = mode->xres + rinfo->hOver_plus;
+		hSyncEnd = hSyncStart + rinfo->hSync_width;
+
+		vTotal = mode->yres + rinfo->vblank;
+		vSyncStart = mode->yres + rinfo->vOver_plus;
+		vSyncEnd = vSyncStart + rinfo->vSync_width;
 	}
 
 	sync = mode->sync;
@@ -2066,7 +2345,7 @@
 		hsync_wid = 1;
 	else if (hsync_wid > 0x3f)	/* max */
 		hsync_wid = 0x3f;
-	vsync_wid = mode->vsync_len;
+
 	if (vsync_wid == 0)
 		vsync_wid = 1;
 	else if (vsync_wid > 0x1f)	/* max */
@@ -2077,24 +2356,8 @@
 
 	cSync = mode->sync & FB_SYNC_COMP_HIGH_ACT ? (1 << 4) : 0;
 
-	switch (mode->bits_per_pixel) {
-		case 8:
-			format = DST_8BPP;
-			bytpp = 1;
-			break;
-		case 16:
-			format = DST_16BPP;
-			bytpp = 2;
-			break;
-		case 24:
-			format = DST_24BPP;
-			bytpp = 3;
-			break;
-		case 32:
-			format = DST_32BPP;
-			bytpp = 4;
-			break;
-	}
+	format = radeon_get_dstbpp(depth);
+	bytpp = mode->bits_per_pixel >> 3;
 
 	if ((primary_mon == MT_DFP) || (primary_mon == MT_LCD))
 		hsync_fudge = hsync_fudge_fp[format-1];
@@ -2118,7 +2381,7 @@
 	newmode.dac_cntl = /* INREG(DAC_CNTL) | */ DAC_MASK_ALL | DAC_VGA_ADR_EN |
 			   DAC_8BIT_EN;
 
-	newmode.crtc_h_total_disp = ((((hTotal / 8) - 1) & 0xffff) |
+	newmode.crtc_h_total_disp = ((((hTotal / 8) - 1) & 0x3ff) |
 				     (((mode->xres / 8) - 1) << 16));
 
 	newmode.crtc_h_sync_strt_wid = ((hsync_start & 0x1fff) |
@@ -2158,6 +2421,7 @@
 	newmode.yres = mode->yres;
 
 	rinfo->bpp = mode->bits_per_pixel;
+	rinfo->depth = depth;
 
 	rinfo->hack_crtc_ext_cntl = newmode.crtc_ext_cntl;
 	rinfo->hack_crtc_v_sync_strt_wid = newmode.crtc_v_sync_strt_wid;
@@ -2234,50 +2498,38 @@
 	newmode.dda_on_off = (ron << 16) | roff;
 
 	if ((primary_mon == MT_DFP) || (primary_mon == MT_LCD)) {
-		int hRatio, vRatio;
-
-		if ((rinfo->panel_xres == 0) || (rinfo->panel_yres == 0)) {
-			hRatio = vRatio = 1;
-		} else {
-			if (mode->xres > rinfo->panel_xres)
-				mode->xres = rinfo->panel_xres;
-			if (mode->yres > rinfo->panel_yres)
-				mode->yres = rinfo->panel_yres;
-	
-			hRatio = mode->xres / rinfo->panel_xres;
-			vRatio = mode->yres / rinfo->panel_yres;
-		}
+		unsigned int hRatio, vRatio;
 
-		if (hRatio == 1) {
-			newmode.fp_horz_stretch =
-				rinfo->init_state.fp_horz_stretch;
-			newmode.fp_horz_stretch &= ~(HORZ_STRETCH_BLEND |
-						     HORZ_STRETCH_ENABLE);
-		} else {
-			newmode.fp_horz_stretch =
-				((((unsigned long)(hRatio * HORZ_STRETCH_RATIO_MAX +
-				(int)0.5)) & HORZ_STRETCH_RATIO_MASK)) |
-				(rinfo->init_state.fp_horz_stretch &
-				(HORZ_PANEL_SIZE | HORZ_FP_LOOP_STRETCH |
-				HORZ_AUTO_RATIO_INC));
+		if (mode->xres > rinfo->panel_xres)
+			mode->xres = rinfo->panel_xres;
+		if (mode->yres > rinfo->panel_yres)
+			mode->yres = rinfo->panel_yres;
+
+		newmode.fp_horz_stretch = (((rinfo->panel_xres / 8) - 1)
+					   << HORZ_PANEL_SHIFT);
+		newmode.fp_vert_stretch = ((rinfo->panel_yres - 1)
+					   << VERT_PANEL_SHIFT);
+
+		if (mode->xres != rinfo->panel_xres) {
+			hRatio = round_div(mode->xres * HORZ_STRETCH_RATIO_MAX,
+					   rinfo->panel_xres);
+			newmode.fp_horz_stretch = (((((unsigned long)hRatio) & HORZ_STRETCH_RATIO_MASK)) |
+						   (newmode.fp_horz_stretch &
+						    (HORZ_PANEL_SIZE | HORZ_FP_LOOP_STRETCH |
+						     HORZ_AUTO_RATIO_INC)));
 			newmode.fp_horz_stretch |= (HORZ_STRETCH_BLEND |
 						    HORZ_STRETCH_ENABLE);
 		}
 		newmode.fp_horz_stretch &= ~HORZ_AUTO_RATIO;
 
-		if (vRatio == 1) {
-			newmode.fp_vert_stretch =
-				rinfo->init_state.fp_vert_stretch;
-			newmode.fp_vert_stretch &= ~(VERT_STRETCH_BLEND |
-						     VERT_STRETCH_ENABLE);
-		} else {
-			newmode.fp_vert_stretch =
-				((((unsigned long)(vRatio * VERT_STRETCH_RATIO_MAX +
-				(int)0.5)) & VERT_STRETCH_RATIO_MASK)) |
-				(rinfo->init_state.fp_vert_stretch &
-				(VERT_PANEL_SIZE | VERT_STRETCH_RESERVED));
-			newmode.fp_vert_stretch |= (VERT_STRETCH_BLEND |
-						    VERT_STRETCH_ENABLE);
+		if (mode->yres != rinfo->panel_yres) {
+				vRatio = round_div(mode->yres * VERT_STRETCH_RATIO_MAX,
+						   rinfo->panel_yres);
+				newmode.fp_vert_stretch = (((((unsigned long)vRatio) & VERT_STRETCH_RATIO_MASK)) |
+							   (newmode.fp_vert_stretch &
+							   (VERT_PANEL_SIZE | VERT_STRETCH_RESERVED)));
+				newmode.fp_vert_stretch |= (VERT_STRETCH_BLEND |
+							    VERT_STRETCH_ENABLE);
 		}
 		newmode.fp_vert_stretch &= ~VERT_AUTO_RATIO_EN;
 
@@ -2290,14 +2542,14 @@
 					 FP_USE_SHADOW_EN |
 					 FP_CRTC_USE_SHADOW_VEND |
 					 FP_CRT_SYNC_ALT));
+
 		newmode.fp_gen_cntl |= (FP_CRTC_DONT_SHADOW_VPAR |
 					FP_CRTC_DONT_SHADOW_HEND);
 
 		newmode.lvds_gen_cntl = rinfo->init_state.lvds_gen_cntl;
 		newmode.lvds_pll_cntl = rinfo->init_state.lvds_pll_cntl;
 		newmode.tmds_crc = rinfo->init_state.tmds_crc;
-
-		newmode.crtc_ext_cntl &= ~CRTC_CRT_ON;
+		newmode.tmds_transmitter_cntl = rinfo->init_state.tmds_transmitter_cntl;
 
 		if (primary_mon == MT_LCD) {
 			newmode.lvds_gen_cntl |= (LVDS_ON | LVDS_BLON);
@@ -2305,21 +2557,25 @@
 		} else {
 			/* DFP */
 			newmode.fp_gen_cntl |= (FP_FPON | FP_TMDS_EN);
+			newmode.tmds_transmitter_cntl = (TMDS_RAN_PAT_RST |
+							 ICHCSEL) & ~(TMDS_PLLRST);
+			newmode.crtc_ext_cntl &= ~CRTC_CRT_ON;
 		}
 
-		newmode.fp_crtc_h_total_disp =
-				rinfo->init_state.fp_crtc_h_total_disp;
-		newmode.fp_crtc_v_total_disp =
-				rinfo->init_state.fp_crtc_v_total_disp;
-		newmode.fp_h_sync_strt_wid =
-				rinfo->init_state.fp_h_sync_strt_wid;
-		newmode.fp_v_sync_strt_wid =
-				rinfo->init_state.fp_v_sync_strt_wid;
+		newmode.fp_crtc_h_total_disp = newmode.crtc_h_total_disp;
+		newmode.fp_crtc_v_total_disp = newmode.crtc_v_total_disp;
+		newmode.fp_h_sync_strt_wid = newmode.crtc_h_sync_strt_wid;
+		newmode.fp_v_sync_strt_wid = newmode.crtc_v_sync_strt_wid;
 	}
 
 	/* do it! */
 	radeon_write_mode (rinfo, &newmode);
 
+#if defined(CONFIG_BOOTX_TEXT)
+	btext_update_display(rinfo->fb_base_phys, mode->xres, mode->yres,
+			     rinfo->depth, rinfo->pitch*64);
+#endif
+
 	return;
 }
 
@@ -2331,7 +2587,8 @@
 	int primary_mon = PRIMARY_MONITOR(rinfo);
 
 	/* blank screen */
-	OUTREG8(CRTC_EXT_CNTL + 1, 4);
+	OUTREGP(CRTC_EXT_CNTL, CRTC_DISPLAY_DIS | CRTC_VSYNC_DIS | CRTC_HSYNC_DIS,
+		~(CRTC_DISPLAY_DIS | CRTC_VSYNC_DIS | CRTC_HSYNC_DIS));
 
 	for (i=0; i<9; i++)
 		OUTREG(common_regs[i].reg, common_regs[i].val);
@@ -2347,16 +2604,6 @@
 	OUTREG(CRTC_OFFSET, 0);
 	OUTREG(CRTC_OFFSET_CNTL, 0);
 	OUTREG(CRTC_PITCH, mode->crtc_pitch);
-#if 1
-	printk("CRTC_H_TOTAL_DISP = 0x%x, H_SYNC = 0x%x\n",
-		mode->crtc_h_total_disp, mode->crtc_h_sync_strt_wid);
-	printk("CRTC_V_TOTAL_DISP = 0x%x, V_SYNC = 0x%x\n",
-		mode->crtc_v_total_disp, mode->crtc_v_sync_strt_wid);
-	printk("PPLL_DIV_3 = 0x%x, PPLL_REF_DIV = 0x%x\n",
-		mode->ppll_div_3, mode->ppll_ref_div);
-	printk("DDA_CONFIG = 0x%x, DDA_ON_OFF = 0x%x\n",
-		mode->dda_config, mode->dda_on_off);
-#endif
 
 #if defined(__BIG_ENDIAN)
 	OUTREG(SURFACE_CNTL, mode->surface_cntl);
@@ -2396,27 +2643,31 @@
 		OUTREG(FP_CRTC_V_TOTAL_DISP, mode->fp_crtc_v_total_disp);
 		OUTREG(FP_H_SYNC_STRT_WID, mode->fp_h_sync_strt_wid);
 		OUTREG(FP_V_SYNC_STRT_WID, mode->fp_v_sync_strt_wid);
-		OUTREG(TMDS_CRC, mode->tmds_crc);
 		OUTREG(FP_HORZ_STRETCH, mode->fp_horz_stretch);
 		OUTREG(FP_VERT_STRETCH, mode->fp_vert_stretch);
 		OUTREG(FP_GEN_CNTL, mode->fp_gen_cntl);
+		OUTREG(TMDS_CRC, mode->tmds_crc);
+		OUTREG(TMDS_TRANSMITTER_CNTL, mode->tmds_transmitter_cntl);
 
 		if (primary_mon == MT_LCD) {
 			unsigned int tmp = INREG(LVDS_GEN_CNTL);
 
+			mode->lvds_gen_cntl &= ~LVDS_STATE_MASK;
+			mode->lvds_gen_cntl |= (rinfo->init_state.lvds_gen_cntl & LVDS_STATE_MASK);
+
 			if ((tmp & (LVDS_ON | LVDS_BLON)) ==
-			    (mode->lvds_gen_cntl & (LVDS_ON | LVDS_BLON)))
-				OUTREG(LVDS_GEN_CNTL, mode->lvds_gen_cntl);
-		} else {
-			/* DVI */
-			if (mode->lvds_gen_cntl & (LVDS_ON | LVDS_BLON)) {
-				udelay(1000);
+			    (mode->lvds_gen_cntl & (LVDS_ON | LVDS_BLON))) {
 				OUTREG(LVDS_GEN_CNTL, mode->lvds_gen_cntl);
 			} else {
-				OUTREG(LVDS_GEN_CNTL, mode->lvds_gen_cntl |
-				       LVDS_BLON);
-				udelay(1000);
-				OUTREG(LVDS_GEN_CNTL, mode->lvds_gen_cntl);
+				if (mode->lvds_gen_cntl & (LVDS_ON | LVDS_BLON)) {
+					udelay(1000);
+					OUTREG(LVDS_GEN_CNTL, mode->lvds_gen_cntl);
+				} else {
+					OUTREG(LVDS_GEN_CNTL, mode->lvds_gen_cntl |
+					       LVDS_BLON);
+					udelay(1000);
+					OUTREG(LVDS_GEN_CNTL, mode->lvds_gen_cntl);
+				}
 			}
 		}
 	}
@@ -2428,6 +2679,204 @@
 }
 
 
+#ifdef CONFIG_PMAC_BACKLIGHT
+
+static int backlight_conv[] = {
+	0xff, 0xc0, 0xb5, 0xaa, 0x9f, 0x94, 0x89, 0x7e,
+	0x73, 0x68, 0x5d, 0x52, 0x47, 0x3c, 0x31, 0x24
+};
+
+#define BACKLIGHT_LVDS_OFF
+#undef BACKLIGHT_DAC_OFF
+
+/* We turn off the LCD completely instead of just dimming the backlight.
+ * This provides some greater power saving and the display is useless
+ * without backlight anyway.
+ */
+
+static int radeon_set_backlight_enable(int on, int level, void *data)
+{
+	struct radeonfb_info *rinfo = (struct radeonfb_info *)data;
+	unsigned int lvds_gen_cntl = INREG(LVDS_GEN_CNTL);
+
+	lvds_gen_cntl |= (LVDS_BL_MOD_EN | LVDS_BLON);
+	if (on && (level > BACKLIGHT_OFF)) {
+		lvds_gen_cntl |= LVDS_DIGON;
+		if (!lvds_gen_cntl & LVDS_ON) {
+			lvds_gen_cntl &= ~LVDS_BLON;
+			OUTREG(LVDS_GEN_CNTL, lvds_gen_cntl);
+			(void)INREG(LVDS_GEN_CNTL);
+			mdelay(10);
+			lvds_gen_cntl |= LVDS_BLON;
+			OUTREG(LVDS_GEN_CNTL, lvds_gen_cntl);
+		}
+		lvds_gen_cntl &= ~LVDS_BL_MOD_LEVEL_MASK;
+		lvds_gen_cntl |= (backlight_conv[level] <<
+				  LVDS_BL_MOD_LEVEL_SHIFT);
+		lvds_gen_cntl |= (LVDS_ON | LVDS_EN);
+		lvds_gen_cntl &= ~LVDS_DISPLAY_DIS;
+	} else {
+		lvds_gen_cntl &= ~LVDS_BL_MOD_LEVEL_MASK;
+		lvds_gen_cntl |= (backlight_conv[0] <<
+				  LVDS_BL_MOD_LEVEL_SHIFT);
+		lvds_gen_cntl |= LVDS_DISPLAY_DIS;
+		OUTREG(LVDS_GEN_CNTL, lvds_gen_cntl);
+		udelay(10);
+		lvds_gen_cntl &= ~(LVDS_ON | LVDS_EN | LVDS_BLON | LVDS_DIGON);
+	}
+
+	OUTREG(LVDS_GEN_CNTL, lvds_gen_cntl);
+	rinfo->init_state.lvds_gen_cntl &= ~LVDS_STATE_MASK;
+	rinfo->init_state.lvds_gen_cntl |= (lvds_gen_cntl & LVDS_STATE_MASK);
+
+	return 0;
+}
+
+static int radeon_set_backlight_level(int level, void *data)
+{
+	return radeon_set_backlight_enable(1, level, data);
+}
+#endif /* CONFIG_PMAC_BACKLIGHT */
+
+
+#ifdef CONFIG_PMAC_PBOOK
+static void radeon_set_suspend(struct radeonfb_info *rinfo, int suspend)
+{
+	u16 pwr_cmd;
+
+	if (!rinfo->pm_reg)
+		return;
+
+	/* Set the chip into appropriate suspend mode (we use D2,
+	 * D3 would require a compete re-initialization of the chip,
+	 * including PCI config registers, clocks, AGP conf, ...)
+	 */
+	if (suspend) {
+		/* Make sure CRTC2 is reset.  Remove that the day
+		 * we decide to actually use CRTC2 and replace it with
+		 * real code for disabling the CRTC2 output during sleep.
+		 */
+
+		pci_read_config_word(rinfo->pdev, rinfo->pm_reg+PCI_PM_CTRL,
+				     &pwr_cmd);
+
+		/* Switch PCI power managment to D2 */
+		pci_write_config_word(rinfo->pdev, rinfo->pm_reg+PCI_PM_CTRL,
+				      (pwr_cmd & ~PCI_PM_CTRL_STATE_MASK)
+				      | 2);
+		pci_read_config_word(rinfo->pdev, rinfo->pm_reg+PCI_PM_CTRL,
+				     &pwr_cmd);
+	} else {
+		/* Switch back PCI powermanagment to D0 */
+		mdelay(100);
+		pci_write_config_word(rinfo->pdev, rinfo->pm_reg+PCI_PM_CTRL, 0);
+		mdelay(100);
+		pci_read_config_word(rinfo->pdev, rinfo->pm_reg+PCI_PM_CTRL,
+				     &pwr_cmd);
+		mdelay(100);
+	}
+}
+
+/*
+ * Save the contents of the framebuffer when we go to sleep,
+ * and restore it when we wake up again.
+ */
+
+int radeon_sleep_notify(struct pmu_sleep_notifier *self, int when)
+{
+	struct radeonfb_info *rinfo;
+
+	for (rinfo = board_list; rinfo != NULL; rinfo = rinfo->next) {
+		struct fb_fix_screeninfo fix;
+		int nb;
+
+		switch (rinfo->chipset) {
+			case PCI_DEVICE_ID_RADEON_LW:
+			case PCI_DEVICE_ID_RADEON_LY:
+			case PCI_DEVICE_ID_RADEON_LZ:
+				break;
+			default:
+				return PBOOK_SLEEP_REFUSE;
+		}
+
+		radeonfb_get_fix(&fix, fg_console, (struct fb_info *)rinfo);
+		nb = fb_display[fg_console].var.yres * fix.line_length;
+
+		switch (when) {
+			case PBOOK_SLEEP_REQUEST:
+#if 0
+				rinfo->save_framebuffer = vmalloc(nb);
+				if (rinfo->save_framebuffer == NULL)
+					return PBOOK_SLEEP_REFUSE;
+#endif
+				break;
+			case PBOOK_SLEEP_REJECT:
+#if 0
+				if (rinfo->save_framebuffer) {
+					vfree(rinfo->save_framebuffer);
+					rinfo->save_framebuffer = 0;
+				}
+#endif
+				break;
+			case PBOOK_SLEEP_NOW:
+				radeon_engine_idle();
+				radeon_engine_reset();
+				radeon_engine_idle();
+
+#if 0
+				/* Backup framebuffer content */
+				if (rinfo->save_framebuffer)
+					memcpy_fromio(rinfo->save_framebuffer,
+						      (void *)rinfo->fb_base,
+						      nb);
+#endif
+
+				/* Blank display and LCD */
+				radeonfb_blank(VESA_POWERDOWN+1,
+					       (struct fb_info *)rinfo);
+
+				/* Sleep */
+				radeon_set_suspend(rinfo, 1);
+
+				break;
+			case PBOOK_WAKE:
+				/* Wakeup */
+				radeon_set_suspend(rinfo, 0);
+
+				radeon_engine_reset();
+				if (!noaccel) {
+					radeon_engine_init(rinfo);
+					radeon_engine_reset();
+				}
+
+#if 0
+				/* Restore framebuffer content */
+				if (rinfo->save_framebuffer) {
+					memcpy_toio((void *)rinfo->fb_base,
+						    rinfo->save_framebuffer,
+						    nb);
+					vfree(rinfo->save_framebuffer);
+					rinfo->save_framebuffer = 0;
+				}
+#endif
+
+				if (rinfo->currcon_display) {
+					radeonfb_set_var(&rinfo->currcon_display->var, rinfo->currcon,
+							 (struct fb_info *) rinfo);
+					radeon_set_dispsw(rinfo, rinfo->currcon_display);
+					do_install_cmap(rinfo->currcon,
+							(struct fb_info *)rinfo);
+				}
+
+				radeonfb_blank(0, (struct fb_info *)rinfo);
+				break;
+		}
+	}
+
+	return PBOOK_SLEEP_OK;
+}
+
+#endif /* CONFIG_PMAC_PBOOK */
 
 /*
  * text console acceleration
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/video/riva/fbdev.c linux/drivers/video/riva/fbdev.c
--- linux.orig/drivers/video/riva/fbdev.c	Fri Dec 21 17:41:55 2001
+++ linux/drivers/video/riva/fbdev.c	Wed Dec 26 18:20:25 2001
@@ -136,6 +136,11 @@
 	CH_GEFORCE2_GTS,
 	CH_GEFORCE2_ULTRA,
 	CH_QUADRO2_PRO,
+	CH_GEFORCE2_GO,
+        CH_GEFORCE3,
+        CH_GEFORCE3_1,
+        CH_GEFORCE3_2,
+        CH_QUADRO_DDC
 };
 
 /* directly indexed by riva_chips enum, above */
@@ -158,6 +163,11 @@
 	{ "GeForce2-GTS", NV_ARCH_10},
 	{ "GeForce2-ULTRA", NV_ARCH_10},
 	{ "Quadro2-PRO", NV_ARCH_10},
+        { "GeForce2-Go", NV_ARCH_10},
+        { "GeForce3", NV_ARCH_20}, 
+        { "GeForce3 Ti 200", NV_ARCH_20},
+        { "GeForce3 Ti 500", NV_ARCH_20},
+        { "Quadro DDC", NV_ARCH_20}
 };
 
 static struct pci_device_id rivafb_pci_tbl[] __devinitdata = {
@@ -195,6 +205,16 @@
 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_GEFORCE2_ULTRA },
 	{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO2_PRO,
 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_QUADRO2_PRO },
+        { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE2_GO,
+          PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_GEFORCE2_GO },
+        { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE3,
+          PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_GEFORCE3 },
+        { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE3_1,
+          PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_GEFORCE3_1 },
+        { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE3_2,
+          PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_GEFORCE3_2 },
+        { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO_DDC,
+          PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_QUADRO_DDC },
 	{ 0, } /* terminate list */
 };
 MODULE_DEVICE_TABLE(pci, rivafb_pci_tbl);
@@ -1315,6 +1335,7 @@
 		fix->accel = FB_ACCEL_NV4;
 		break;
 	case NV_ARCH_10:	/* FIXME: ID for GeForce */
+	case NV_ARCH_20:
 		fix->accel = FB_ACCEL_NV4;
 		break;
 
@@ -1930,6 +1951,7 @@
 		break;
 	case NV_ARCH_04:
 	case NV_ARCH_10:
+	case NV_ARCH_20:
 		rinfo->riva.PCRTC = (unsigned *)(rinfo->ctrl_base + 0x00600000);
 		rinfo->riva.PRAMIN = (unsigned *)(rinfo->ctrl_base + 0x00710000);
 		break;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/video/riva/riva_hw.c linux/drivers/video/riva/riva_hw.c
--- linux.orig/drivers/video/riva/riva_hw.c	Tue Feb 13 21:15:05 2001
+++ linux/drivers/video/riva/riva_hw.c	Wed Dec 26 18:20:25 2001
@@ -1220,6 +1220,7 @@
             state->repaint1 = hDisplaySize < 1280 ? 0x04 : 0x00;
             break;
         case NV_ARCH_10:
+	case NV_ARCH_20:
             nv10UpdateArbitrationSettings(VClk, 
                                           pixelDepth * 8, 
                                          &(state->arbitration0),
@@ -1285,6 +1286,7 @@
             chip->Tri05 = (RivaTexturedTriangle05 *)&(chip->FIFO[0x0000E000/4]);
             break;
         case NV_ARCH_10:
+	case NV_ARCH_20:
             /*
              * Initialize state for the RivaTriangle3D05 routines.
              */
@@ -1393,6 +1395,7 @@
             chip->PGRAPH[0x0000067C/4] = state->pitch3;
             break;
         case NV_ARCH_10:
+	case NV_ARCH_20:
             LOAD_FIXED_STATE(nv10,PFIFO);
             LOAD_FIXED_STATE(nv10,PRAMIN);
             LOAD_FIXED_STATE(nv10,PGRAPH);
@@ -1421,15 +1424,31 @@
                     chip->Tri03 = 0L;
                     break;
             }
-            chip->PGRAPH[0x00000640/4] = state->offset0;
-            chip->PGRAPH[0x00000644/4] = state->offset1;
-            chip->PGRAPH[0x00000648/4] = state->offset2;
-            chip->PGRAPH[0x0000064C/4] = state->offset3;
-            chip->PGRAPH[0x00000670/4] = state->pitch0;
-            chip->PGRAPH[0x00000674/4] = state->pitch1;
-            chip->PGRAPH[0x00000678/4] = state->pitch2;
-            chip->PGRAPH[0x0000067C/4] = state->pitch3;
-            chip->PGRAPH[0x00000680/4] = state->pitch3;
+
+	    if (chip->Architecture == NV_ARCH_10) {
+            	chip->PGRAPH[0x00000640/4] = state->offset0;
+            	chip->PGRAPH[0x00000644/4] = state->offset1;
+            	chip->PGRAPH[0x00000648/4] = state->offset2;
+            	chip->PGRAPH[0x0000064C/4] = state->offset3;
+            	chip->PGRAPH[0x00000670/4] = state->pitch0;
+            	chip->PGRAPH[0x00000674/4] = state->pitch1;
+            	chip->PGRAPH[0x00000678/4] = state->pitch2;
+            	chip->PGRAPH[0x0000067C/4] = state->pitch3;
+            	chip->PGRAPH[0x00000680/4] = state->pitch3;
+	    } else {
+		chip->PGRAPH[0x00000820/4] = state->offset0;
+		chip->PGRAPH[0x00000824/4] = state->offset1;
+		chip->PGRAPH[0x00000828/4] = state->offset2;
+		chip->PGRAPH[0x0000082C/4] = state->offset3;
+		chip->PGRAPH[0x00000850/4] = state->pitch0;
+		chip->PGRAPH[0x00000854/4] = state->pitch1;
+		chip->PGRAPH[0x00000858/4] = state->pitch2;
+		chip->PGRAPH[0x0000085C/4] = state->pitch3;
+		chip->PGRAPH[0x00000860/4] = state->pitch3;
+		chip->PGRAPH[0x00000864/4] = state->pitch3;
+		chip->PGRAPH[0x000009A4/4] = chip->PFB[0x00000200/4];
+		chip->PGRAPH[0x000009A8/4] = chip->PFB[0x00000204/4];
+	    }
             chip->PGRAPH[0x00000B00/4] = chip->PFB[0x00000240/4];
             chip->PGRAPH[0x00000B04/4] = chip->PFB[0x00000244/4];
             chip->PGRAPH[0x00000B08/4] = chip->PFB[0x00000248/4];
@@ -1607,6 +1626,7 @@
             state->pitch3   = chip->PGRAPH[0x0000067C/4];
             break;
         case NV_ARCH_10:
+	case NV_ARCH_20:
             state->offset0  = chip->PGRAPH[0x00000640/4];
             state->offset1  = chip->PGRAPH[0x00000644/4];
             state->offset2  = chip->PGRAPH[0x00000648/4];
@@ -1970,6 +1990,7 @@
             nv4GetConfig(chip);
             break;
         case NV_ARCH_10:
+	case NV_ARCH_20:
             nv10GetConfig(chip);
             break;
         default:
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/video/riva/riva_hw.h linux/drivers/video/riva/riva_hw.h
--- linux.orig/drivers/video/riva/riva_hw.h	Tue Feb 13 21:15:05 2001
+++ linux/drivers/video/riva/riva_hw.h	Wed Dec 26 18:20:25 2001
@@ -74,6 +74,8 @@
 #define NV_ARCH_03  0x03
 #define NV_ARCH_04  0x04
 #define NV_ARCH_10  0x10
+#define NV_ARCH_20  0x20
+
 /***************************************************************************\
 *                                                                           *
 *                             FIFO registers.                               *
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/video/valkyriefb.c linux/drivers/video/valkyriefb.c
--- linux.orig/drivers/video/valkyriefb.c	Mon Jan  7 23:41:09 2002
+++ linux/drivers/video/valkyriefb.c	Wed Dec 26 16:50:52 2001
@@ -7,6 +7,8 @@
  *  Vmode-switching changes and vmode 15/17 modifications created 29 August
  *  1998 by Barry K. Nathan <barryn@pobox.com>.
  *
+ *  Ported to m68k Macintosh by David Huggins-Daines <dhd@debian.org>
+ *
  *  Derived directly from:
  *
  *   controlfb.c -- frame buffer device for the PowerMac 'control' display
@@ -59,7 +61,12 @@
 #include <linux/adb.h>
 #include <linux/cuda.h>
 #include <asm/io.h>
+#ifdef CONFIG_MAC
+#include <asm/bootinfo.h>
+#include <asm/macintosh.h>
+#else
 #include <asm/prom.h>
+#endif
 #include <asm/pgtable.h>
 
 #include <video/fbcon.h>
@@ -71,8 +78,15 @@
 
 static int can_soft_blank = 1;
 
+#ifdef CONFIG_MAC
+/* We don't yet have functions to read the PRAM... perhaps we can
+   adapt them from the PPC code? */
+static int default_vmode = VMODE_640_480_67;
+static int default_cmode = CMODE_8;
+#else
 static int default_vmode = VMODE_NVRAM;
 static int default_cmode = CMODE_NVRAM;
+#endif
 static char fontname[40] __initdata = { 0 };
 
 static int currcon = 0;
@@ -117,7 +131,6 @@
 int valkyriefb_init(void);
 int valkyriefb_setup(char*);
 
-static void valkyrie_of_init(struct device_node *dp);
 static int valkyrie_get_fix(struct fb_fix_screeninfo *fix, int con,
 			 struct fb_info *info);
 static int valkyrie_get_var(struct fb_var_screeninfo *var, int con,
@@ -444,6 +457,7 @@
 	p->sense = read_valkyrie_sense(p);
 	printk(KERN_INFO "Monitor sense value = 0x%x, ", p->sense);
 
+#ifdef CONFIG_NVRAM
 	/* Try to pick a video mode out of NVRAM if we have one. */
 	if (default_vmode == VMODE_NVRAM) {
 		default_vmode = nvram_read_byte(NV_VMODE);
@@ -452,12 +466,13 @@
 		 || !valkyrie_reg_init[default_vmode - 1])
 			default_vmode = VMODE_CHOOSE;
 	}
+	if (default_cmode == CMODE_NVRAM)
+		default_cmode = nvram_read_byte(NV_CMODE);
+#endif
 	if (default_vmode == VMODE_CHOOSE)
 		default_vmode = mac_map_monitor_sense(p->sense);
 	if (!valkyrie_reg_init[default_vmode - 1])
 		default_vmode = VMODE_640_480_67;
-	if (default_cmode == CMODE_NVRAM)
-		default_cmode = nvram_read_byte(NV_CMODE);
 
 	/*
 	 * Reduce the pixel size if we don't have enough VRAM or bandwitdh.
@@ -550,48 +565,57 @@
 
 int __init valkyriefb_init(void)
 {
+	struct fb_info_valkyrie	*p;
+	unsigned long frame_buffer_phys, cmap_regs_phys, flags;
+
+#ifdef CONFIG_MAC
+	if (!MACH_IS_MAC)
+		return 0;
+	if (!(mac_bi_data.id == MAC_MODEL_Q630
+	      /* I'm not sure about this one */
+	    || mac_bi_data.id == MAC_MODEL_P588))
+		return 0;
+
+	/* Hardcoded addresses... welcome to 68k Macintosh country :-) */
+	frame_buffer_phys = 0xf9000000;
+	cmap_regs_phys = 0x50f24000;
+	flags = IOMAP_NOCACHE_SER; /* IOMAP_WRITETHROUGH?? */
+#else /* ppc (!CONFIG_MAC) */
 	struct device_node *dp;
 
 	dp = find_devices("valkyrie");
-	if (dp != 0)
-		valkyrie_of_init(dp);
-	return 0;
-}
+	if (dp == 0)
+		return 0;
 
-static void __init valkyrie_of_init(struct device_node *dp)
-{
-	struct fb_info_valkyrie	*p;
-	unsigned long addr;
-	
 	if(dp->n_addrs != 1) {
 		printk(KERN_ERR "expecting 1 address for valkyrie (got %d)", dp->n_addrs);
-		return;
+		return 0;
 	}	
 
+	frame_buffer_phys = dp->addrs[0].address;
+	cmap_regs_phys = dp->addrs[0].address+0x304000;
+	flags = _PAGE_WRITETHRU;
+#endif /* ppc (!CONFIG_MAC) */
+
 	p = kmalloc(sizeof(*p), GFP_ATOMIC);
 	if (p == 0)
-		return;
+		return 0;
 	memset(p, 0, sizeof(*p));
 
 	/* Map in frame buffer and registers */
-	addr = dp->addrs[0].address;
-	if (!request_mem_region(addr, dp->addrs[0].size, "valkyriefb")) {
+	if (!request_mem_region(frame_buffer_phys, 0x100000, "valkyriefb")) {
 		kfree(p);
-		return;
+		return 0;
 	}
-	p->frame_buffer_phys  = addr;
-	p->frame_buffer  = __ioremap(addr, 0x100000, _PAGE_WRITETHRU);
-	p->cmap_regs_phys     = addr + 0x304000;
-	p->cmap_regs     = ioremap(p->cmap_regs_phys,4096);
-	p->valkyrie_regs_phys = addr + 0x30a000;
-	p->valkyrie_regs = ioremap(p->valkyrie_regs_phys, 4096);
-
-	/*
-	 * kps: As far as I know, all Valkyries have fixed usable VRAM.
-	 */
 	p->total_vram = 0x100000;
-
+	p->frame_buffer_phys  = frame_buffer_phys;
+	p->frame_buffer = __ioremap(frame_buffer_phys, p->total_vram, flags);
+	p->cmap_regs_phys = cmap_regs_phys;
+	p->cmap_regs = ioremap(p->cmap_regs_phys, 0x1000);
+	p->valkyrie_regs_phys = cmap_regs_phys+0x6000;
+	p->valkyrie_regs = ioremap(p->valkyrie_regs_phys, 0x1000);
 	init_valkyrie(p);
+	return 0;
 }
 
 /*
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/video/valkyriefb.h linux/drivers/video/valkyriefb.h
--- linux.orig/drivers/video/valkyriefb.h	Mon Jan  7 23:41:09 2002
+++ linux/drivers/video/valkyriefb.h	Wed Dec 26 16:50:52 2001
@@ -9,6 +9,8 @@
  * 
  * vmode 10 changed by Steven Borley <sjb@salix.demon.co.uk>, 14 mai 2000
  *
+ * Ported to 68k Macintosh by David Huggins-Daines <dhd@debian.org>
+ *
  * 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
@@ -37,12 +39,19 @@
  *  Copyright (C) 1998 Jon Howell
  */
 
+#ifdef CONFIG_MAC
+/* Valkyrie registers are word-aligned on m68k */
+#define VALKYRIE_REG_PADSIZE	3
+#else
+#define VALKYRIE_REG_PADSIZE	7
+#endif
+
 /*
  * Structure of the registers for the Valkyrie colormap registers.
  */
 struct cmap_regs {
 	unsigned char addr;
-	char pad1[7];
+	char pad1[VALKYRIE_REG_PADSIZE];
 	unsigned char lut;
 };
 
@@ -52,7 +61,7 @@
 
 struct vpreg {			/* padded register */
 	unsigned char r;
-	char pad[7];
+	char pad[VALKYRIE_REG_PADSIZE];
 };
 
 
@@ -81,6 +90,7 @@
 	int	vres;
 };
 
+#ifndef CONFIG_MAC
 /* Register values for 1024x768, 75Hz mode (17) */
 /* I'm not sure which mode this is (16 or 17), so I'm defining it as 17,
  * since the equivalent mode in controlfb (which I adapted this from) is
@@ -125,14 +135,6 @@
 	1024, 768
 };
 
-/* Register values for 832x624, 75Hz mode (13) */
-static struct valkyrie_regvals valkyrie_reg_init_13 = {
-    9,
-    { 23, 42, 3 },  /* pixel clock = 57.07MHz for V=74.27Hz */
-    { 832, 0 },
-	832, 624
-};
-
 /* Register values for 800x600, 72Hz mode (11) */
 static struct valkyrie_regvals valkyrie_reg_init_11 = {
     13,
@@ -140,6 +142,15 @@
     { 800, 0 },
 	800, 600
 };
+#endif /* CONFIG_MAC */
+
+/* Register values for 832x624, 75Hz mode (13) */
+static struct valkyrie_regvals valkyrie_reg_init_13 = {
+    9,
+    { 23, 42, 3 },  /* pixel clock = 57.07MHz for V=74.27Hz */
+    { 832, 0 },
+	832, 624
+};
 
 /* Register values for 800x600, 60Hz mode (10) */
 static struct valkyrie_regvals valkyrie_reg_init_10 = {
@@ -177,6 +188,15 @@
 	NULL,
 	NULL,
 	&valkyrie_reg_init_10,
+#ifdef CONFIG_MAC
+	NULL,
+	NULL,
+	&valkyrie_reg_init_13,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+#else
 	&valkyrie_reg_init_11,
 	NULL,
 	&valkyrie_reg_init_13,
@@ -184,6 +204,7 @@
 	&valkyrie_reg_init_15,
 	NULL,
 	&valkyrie_reg_init_17,
+#endif
 	NULL,
 	NULL,
 	NULL
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/binfmt_elf.c linux/fs/binfmt_elf.c
--- linux.orig/fs/binfmt_elf.c	Fri Dec 21 17:41:55 2001
+++ linux/fs/binfmt_elf.c	Mon Jan  7 16:50:13 2002
@@ -517,11 +517,13 @@
 				set_personality(PER_SVR4);
 				interpreter = open_exec(elf_interpreter);
 
+				task_lock(current);
 				new_domain = current->exec_domain;
 				new_fs = current->fs;
 				current->personality = old_pers;
 				current->exec_domain = old_domain;
 				current->fs = old_fs;
+				task_unlock(current);
 				put_exec_domain(new_domain);
 				put_fs_struct(new_fs);
 			} else
@@ -1049,6 +1051,23 @@
 
 	}
 
+	memset(&prstatus, 0, sizeof(prstatus));
+	/*
+	 * This transfers the registers from regs into the standard
+	 * coredump arrangement, whatever that is.
+	 */
+#ifdef ELF_CORE_COPY_REGS
+	ELF_CORE_COPY_REGS(prstatus.pr_reg, regs)
+#else
+	if (sizeof(elf_gregset_t) != sizeof(struct pt_regs))
+	{
+		printk("sizeof(elf_gregset_t) (%ld) != sizeof(struct pt_regs) (%ld)\n",
+			(long)sizeof(elf_gregset_t), (long)sizeof(struct pt_regs));
+	}
+	else
+		*(struct pt_regs *)&prstatus.pr_reg = *regs;
+#endif
+
 	/* now stop all vm operations */
 	down_write(&current->mm->mmap_sem);
 	segs = current->mm->map_count;
@@ -1092,7 +1111,6 @@
 	 * Set up the notes in similar form to SVR4 core dumps made
 	 * with info from their /proc.
 	 */
-	memset(&prstatus, 0, sizeof(prstatus));
 
 	notes[0].name = "CORE";
 	notes[0].type = NT_PRSTATUS;
@@ -1114,22 +1132,6 @@
 	prstatus.pr_cstime.tv_sec = CT_TO_SECS(current->times.tms_cstime);
 	prstatus.pr_cstime.tv_usec = CT_TO_USECS(current->times.tms_cstime);
 
-	/*
-	 * This transfers the registers from regs into the standard
-	 * coredump arrangement, whatever that is.
-	 */
-#ifdef ELF_CORE_COPY_REGS
-	ELF_CORE_COPY_REGS(prstatus.pr_reg, regs)
-#else
-	if (sizeof(elf_gregset_t) != sizeof(struct pt_regs))
-	{
-		printk("sizeof(elf_gregset_t) (%ld) != sizeof(struct pt_regs) (%ld)\n",
-			(long)sizeof(elf_gregset_t), (long)sizeof(struct pt_regs));
-	}
-	else
-		*(struct pt_regs *)&prstatus.pr_reg = *regs;
-#endif
-
 #ifdef DEBUG
 	dump_regs("Passed in regs", (elf_greg_t *)regs);
 	dump_regs("prstatus regs", (elf_greg_t *)&prstatus.pr_reg);
@@ -1225,9 +1227,11 @@
 
 		if (!maydump(vma))
 			continue;
+
 #ifdef DEBUG
-		printk("elf_core_dump: writing %08lx %lx\n", addr, len);
+		printk("elf_core_dump: writing %08lx-%08lx\n", vma->vm_start, vma->vm_end);
 #endif
+
 		for (addr = vma->vm_start;
 		     addr < vma->vm_end;
 		     addr += PAGE_SIZE) {
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/intermezzo/dcache.c linux/fs/intermezzo/dcache.c
--- linux.orig/fs/intermezzo/dcache.c	Tue Nov 13 17:20:56 2001
+++ linux/fs/intermezzo/dcache.c	Mon Jan  7 14:09:06 2002
@@ -15,7 +15,6 @@
 #include <linux/stat.h>
 #include <linux/errno.h>
 #include <linux/locks.h>
-#include <linux/slab.h>
 #include <asm/segment.h>
 #include <asm/uaccess.h>
 #include <linux/string.h>
@@ -23,7 +22,7 @@
 #include <linux/intermezzo_fs.h>
 
 static int presto_dentry_revalidate(struct dentry *de, int );
-static kmem_cache_t * presto_dentry_slab;
+
 
 /* called when a cache lookup succeeds */
 static int presto_dentry_revalidate(struct dentry *de, int flag)
@@ -49,91 +48,15 @@
 	}
 }
 
-static void presto_d_release(struct dentry *dentry)
+static void presto_dentry_iput(struct dentry *dentry, struct inode *inode)
 {
-        if (!presto_d2d(dentry)) {
-                /* This should really only happen in the case of a dentry
-                 * with no inode. */
-                return;
-        }
-
-        presto_d2d(dentry)->dd_count--;
-
-        if (! presto_d2d(dentry)->dd_count) {
-                kmem_cache_free(presto_dentry_slab, presto_d2d(dentry));
-		dentry->d_fsdata = NULL;
-        }
+	dentry->d_time = 0;
+	iput(inode);
 }
 
 struct dentry_operations presto_dentry_ops = 
 {
 	d_revalidate: presto_dentry_revalidate,
-        d_release: presto_d_release
+        d_iput: presto_dentry_iput
 };
 
-
-// XXX THIS DEPENDS ON THE KERNEL LOCK!
-
-void presto_set_dd(struct dentry * dentry)
-{
-        ENTRY;
-        if (dentry == NULL)
-                BUG();
-
-        if (dentry->d_fsdata) {
-                printk("VERY BAD: dentry: %p\n", dentry);
-                if (dentry->d_inode)
-                        printk("    inode: %ld\n", dentry->d_inode->i_ino);
-                EXIT;
-                return;
-        }
-
-        if (dentry->d_inode == NULL) {
-                dentry->d_fsdata = kmem_cache_alloc(presto_dentry_slab,
-                                                    SLAB_KERNEL);
-                memset(dentry->d_fsdata, 0, sizeof(struct presto_dentry_data));
-                presto_d2d(dentry)->dd_count = 1;
-                EXIT;
-                return;
-        }
-
-        /* If there's already a dentry for this inode, share the data */
-        if (dentry->d_alias.next != &dentry->d_inode->i_dentry ||
-            dentry->d_alias.prev != &dentry->d_inode->i_dentry) {
-                struct dentry *de;
-
-                if (dentry->d_alias.next != &dentry->d_inode->i_dentry)
-                        de = list_entry(dentry->d_alias.next, struct dentry,
-                                        d_alias);
-                else
-                        de = list_entry(dentry->d_alias.prev, struct dentry,
-                                        d_alias);
-
-                dentry->d_fsdata = de->d_fsdata;
-                presto_d2d(dentry)->dd_count++;
-                EXIT;
-                return;
-        }
-
-        dentry->d_fsdata = kmem_cache_alloc(presto_dentry_slab, SLAB_KERNEL);
-        memset(dentry->d_fsdata, 0, sizeof(struct presto_dentry_data));
-        presto_d2d(dentry)->dd_count = 1;
-        EXIT;
-        return; 
-}
-
-void presto_init_ddata_cache(void)
-{
-        ENTRY;
-        presto_dentry_slab =
-                kmem_cache_create("presto_cache",
-                                  sizeof(struct presto_dentry_data), 0,
-                                  SLAB_HWCACHE_ALIGN, NULL,
-                                  NULL);
-        EXIT;
-}
-
-void presto_cleanup_ddata_cache(void)
-{
-        kmem_cache_destroy(presto_dentry_slab);
-}
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/intermezzo/dir.c linux/fs/intermezzo/dir.c
--- linux.orig/fs/intermezzo/dir.c	Tue Nov 13 17:20:56 2001
+++ linux/fs/intermezzo/dir.c	Mon Jan  7 14:09:06 2002
@@ -163,20 +163,13 @@
                 return ERR_PTR(-EPERM);
         }
         inode = iget(dir->i_sb, ino);
-        if (!inode || is_bad_inode(inode)) {
+        if (!inode || !inode->i_nlink || is_bad_inode(inode)) {
                 CDEBUG(D_PIOCTL, "fatal: invalid inode %ld (%s).\n",
                        ino, inode ? inode->i_nlink ? "bad inode" :
                        "no links" : "NULL");
                 error = -ENOENT;
                 EXIT;
                 goto cleanup_iput;
-        } else if (inode->i_nlink == 0) {
-                /* This is quite evil, but we have little choice.  If we were
-                 * to iput() again with i_nlink == 0, delete_inode would get
-                 * called again, which ext3 really Does Not Like. */
-                atomic_dec(&inode->i_count);
-                EXIT;
-                return ERR_PTR(-ENOENT);
         }
 
         /* We need to make sure we have the right inode (by checking the
@@ -216,7 +209,6 @@
         unsigned int generation;
 
         ENTRY;
-        CDEBUG(D_CACHE, "calling presto_prep on dentry %p\n", dentry);
         error = presto_prep(dentry->d_parent, &cache, &fset);
         if ( error  ) {
                 EXIT;
@@ -245,7 +237,7 @@
                 if (iops && iops->lookup) 
                         de = iops->lookup(dir, dentry);
                 else {
-		        printk("filesystem has no lookup\n");
+                        printk("filesystem has no lookup\n");
                         EXIT;
                         goto exit;
                 }
@@ -261,8 +253,6 @@
                 goto exit;
         }
 
-        presto_set_dd(dentry);
-
         /* some file systems set the methods in lookup, not in
            read_inode, as a result we should set the methods here 
            as well as in read_inode 
@@ -704,7 +694,6 @@
 		double_down(&old_dir->i_zombie, &new_dir->i_zombie);
 }
 
-
 // XXX this can be optimized: renamtes across filesets only require 
 //     multiple KML records, but can locally be executed normally. 
 int presto_rename(struct inode *old_dir, struct dentry *old_dentry,
@@ -724,6 +713,7 @@
                 EXIT;
                 return error;
         }
+
         error = presto_prep(new_parent, &new_cache, &new_fset);
         if ( error ) {
                 EXIT;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/intermezzo/inode.c linux/fs/intermezzo/inode.c
--- linux.orig/fs/intermezzo/inode.c	Sun Nov 11 18:20:21 2001
+++ linux/fs/intermezzo/inode.c	Mon Jan  7 14:09:06 2002
@@ -47,6 +47,8 @@
 void presto_set_ops(struct inode *inode, struct  filter_fs *filter)
 {
 	ENTRY; 
+        if (!inode || is_bad_inode(inode))
+                return;
 	if (inode->i_gid == presto_excluded_gid ) { 
 		EXIT;
                 CDEBUG(D_INODE, "excluded methods for %ld at %p, %p\n",
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/intermezzo/journal.c linux/fs/intermezzo/journal.c
--- linux.orig/fs/intermezzo/journal.c	Fri Dec 21 17:41:55 2001
+++ linux/fs/intermezzo/journal.c	Mon Jan  7 14:09:06 2002
@@ -301,9 +301,8 @@
 	/* XXX needs to be done after reservation, 
 	   disable ths until version 1.2 */
         if ( dentry ) { 
-                s.prevrec = cpu_to_le32(rec->offset - 
-                                        presto_d2d(dentry)->dd_kml_offset);
-                presto_d2d(dentry)->dd_kml_offset = rec->offset;
+                s.prevrec = cpu_to_le32(rec->offset - dentry->d_time);
+                dentry->d_time = (unsigned long) rec->offset;
         } else { 
                 s.prevrec = -1;
         }
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/intermezzo/presto.c linux/fs/intermezzo/presto.c
--- linux.orig/fs/intermezzo/presto.c	Tue Nov 13 17:20:56 2001
+++ linux/fs/intermezzo/presto.c	Mon Jan  7 14:09:06 2002
@@ -50,18 +50,16 @@
         return err;
 }
 
-inline struct presto_dentry_data *presto_d2d(struct dentry *dentry)
+
+static inline int presto_dentry_is_fsetroot(struct dentry *dentry)
 {
-        return (struct presto_dentry_data *)dentry->d_fsdata;
+        return ((long) dentry->d_fsdata) & PRESTO_FSETROOT;
 }
 
 static inline struct presto_file_set *presto_dentry2fset(struct dentry *dentry)
 {
-        if (dentry->d_fsdata == NULL) {
-                printk("fucked dentry: %p\n", dentry);
-                BUG();
-        }
-        return presto_d2d(dentry)->dd_fset;
+        return (struct presto_file_set *)
+                (((long) dentry->d_fsdata) - PRESTO_FSETROOT);
 }
 
 /* find the presto minor device for this inode */
@@ -114,7 +112,7 @@
         ENTRY;
         fsde = de;
         for ( ; ; ) {
-                if ( presto_dentry2fset(fsde) ) {
+                if ( presto_dentry_is_fsetroot(fsde) ) {
                         EXIT;
                         return presto_dentry2fset(fsde);
                 }
@@ -185,21 +183,36 @@
                 return 1;
         }
 
+        /* if it is a fsetroot, it's stored in the fset_flags */
+        if ( fset && presto_dentry_is_fsetroot(dentry) ) {
+                EXIT;
+                return fset->fset_data & flag;
+        }
+
         EXIT;
-        return (presto_d2d(dentry)->dd_flags & flag);
+        return ((int)dentry->d_fsdata & flag);
 }
 
 /* set a bit in the dentry flags */
 void presto_set(struct dentry *dentry, int flag)
 {
 
-        ENTRY;
         if ( dentry->d_inode ) {
                 CDEBUG(D_INODE, "SET ino %ld, flag %x\n",
                        dentry->d_inode->i_ino, flag);
         }
-        presto_d2d(dentry)->dd_flags |= flag;
-        EXIT;
+
+        if ( presto_dentry_is_fsetroot(dentry)) {
+                struct presto_file_set *fset = presto_dentry2fset(dentry);
+                if (fset) {
+                        fset->fset_data |= flag;
+                        CDEBUG(D_INODE, "Setting fset->fset_data: now %x\n",
+                               fset->fset_data);
+                }
+        } else {
+                CDEBUG(D_INODE, "Setting dentry->d_fsdata\n");
+                ((int)dentry->d_fsdata) |= flag;
+        }
 }
 
 /* given a path: complete the closes on the fset */
@@ -451,27 +464,39 @@
         struct dentry *dentry;
         int error;
 
+        CDEBUG(D_INODE, "name: %s, and flag %x, or flag %x\n",
+               name, and_flag, or_flag);
+
         error = presto_walk(name, &nd);
         if (error)
                 return error;
         dentry = nd.dentry;
-
-        CDEBUG(D_INODE, "name: %s, and flag %x, or flag %x, dd_flags %x\n",
-               name, and_flag, or_flag, presto_d2d(dentry)->dd_flags);
-
+        CDEBUG(D_INODE, "dentry at %p, d_fsdata %p\n", dentry, dentry->d_fsdata);
 
         error = -ENXIO;
         if ( !presto_ispresto(dentry->d_inode) )
                 goto out;
 
         error = 0;
+        if ( presto_dentry_is_fsetroot(dentry) ) {
+                struct presto_file_set *fset = presto_dentry2fset(dentry);
+                CDEBUG(D_INODE, "Setting fset fset_data: fset %p\n", fset);
+                if ( fset ) {
+                        fset->fset_data &= and_flag;
+                        fset->fset_data |= or_flag;
+                        if (res) {
+                                *res = fset->fset_data;
+                        }
+                }
+                CDEBUG(D_INODE, "fset %p, flags %x data %x\n", 
+                       fset, fset->fset_flags, fset->fset_data);
+        } else {
+                ((int)dentry->d_fsdata) &= and_flag;
+                ((int)dentry->d_fsdata) |= or_flag;
+                if (res) 
+                        *res = (int)dentry->d_fsdata;
+        }
 
-        presto_d2d(dentry)->dd_flags  &= and_flag;
-        presto_d2d(dentry)->dd_flags  |= or_flag;
-        if (res) 
-                *res = presto_d2d(dentry)->dd_flags;
-
-        // XXX this check makes no sense as d_count can change anytime.
         /* indicate if we were the only users while changing the flag */
         if ( atomic_read(&dentry->d_count) > 1 )
                 error = -EBUSY;
@@ -810,7 +835,6 @@
 
         error = -EEXIST;
         CDEBUG(D_INODE, "\n");
-
         fset2 = presto_fset(dentry);
         if (fset2 && (fset2->fset_mtpt == dentry) ) { 
                 printk(KERN_ERR "Fsetroot already set (path %s)\n", path);
@@ -825,7 +849,7 @@
         fset->fset_flags = flags;
 	fset->fset_file_maxio = FSET_DEFAULT_MAX_FILEIO; 
 
-        presto_d2d(dentry)->dd_fset = fset;
+        dentry->d_fsdata = (void *) ( ((long)fset) + PRESTO_FSETROOT );
         list_add(&fset->fset_list, &cache->cache_fset_list);
 
         error = presto_init_kml_file(fset);
@@ -869,15 +893,15 @@
                 cache->cache_flags |= CACHE_FSETROOT_SET;
         }
 
-        CDEBUG(D_PIOCTL, "-------> fset at %p, dentry at %p, mtpt %p, fset %s, cache %p, presto_d2d(dentry)->dd_fset %p\n",
-               fset, dentry, fset->fset_mtpt, fset->fset_name, cache, presto_d2d(dentry)->dd_fset);
+        CDEBUG(D_PIOCTL, "-------> fset at %p, dentry at %p, mtpt %p, fset %s, cache %p, d_fsdata %p\n",
+               fset, dentry, fset->fset_mtpt, fset->fset_name, cache, dentry->d_fsdata);
 
         EXIT;
         return 0;
 
  out_list_del:
         list_del(&fset->fset_list);
-        presto_d2d(dentry)->dd_fset = NULL;
+        dentry->d_fsdata = 0;
  out_dput:
         path_release(&fset->fset_nd); 
  out_free:
@@ -907,7 +931,7 @@
         }
 
         error = -EINVAL;
-        if ( ! presto_dentry2fset(dentry)) {
+        if ( ! presto_dentry_is_fsetroot(dentry)) {
                 EXIT;
                 goto kml_out;
         }
@@ -925,44 +949,12 @@
         return error;
 }
 
-static void presto_cleanup_fset(struct presto_file_set *fset)
-{
-	int error;
-	struct presto_cache *cache;
-
-	ENTRY;
-#ifdef  CONFIG_KREINT
-        error = kml_cleanup (fset);
-        if ( error ) {
-                printk("InterMezzo: Closing kml for fset %s: %d\n",
-                       fset->fset_name, error);
-        }
-#endif
-
-        error = presto_close_journal_file(fset);
-        if ( error ) {
-                printk("InterMezzo: Closing journal for fset %s: %d\n",
-                       fset->fset_name, error);
-        }
-        cache = fset->fset_cache;
-        cache->cache_flags &= ~CACHE_FSETROOT_SET;
-
-        list_del(&fset->fset_list);
-
-	presto_d2d(fset->fset_mtpt)->dd_fset = NULL;
-        path_release(&fset->fset_nd);
-
-        fset->fset_mtpt = NULL;
-        PRESTO_FREE(fset->fset_name, strlen(fset->fset_name) + 1);
-        PRESTO_FREE(fset, sizeof(*fset));
-        EXIT;
-}
-
 int presto_clear_fsetroot(char *path)
 {
         struct nameidata nd;
         struct presto_file_set *fset;
         struct dentry *dentry;
+        struct presto_cache *cache;
         int error;
 
         ENTRY;
@@ -980,7 +972,7 @@
         }
 
         error = -EINVAL;
-        if ( ! presto_dentry2fset(dentry)) {
+        if ( ! presto_dentry_is_fsetroot(dentry)) {
                 EXIT;
                 goto put_out;
         }
@@ -991,7 +983,28 @@
                 goto put_out;
         }
 
-	presto_cleanup_fset(fset);
+#ifdef  CONFIG_KREINT
+        error = kml_cleanup (fset);
+        if ( error ) {
+                printk("InterMezzo: Closing kml for fset %s: %d\n",
+                       fset->fset_name, error);
+        }
+#endif
+
+        error = presto_close_journal_file(fset);
+        if ( error ) {
+                printk("InterMezzo: Closing journal for fset %s: %d\n",
+                       fset->fset_name, error);
+        }
+        cache = fset->fset_cache;
+        cache->cache_flags &= ~CACHE_FSETROOT_SET;
+
+        list_del(&fset->fset_list);
+        dentry->d_fsdata = 0;
+        path_release(&fset->fset_nd);
+        fset->fset_mtpt = NULL;
+        PRESTO_FREE(fset->fset_name, strlen(fset->fset_name) + 1);
+        PRESTO_FREE(fset, sizeof(*fset));
         EXIT;
 
 put_out:
@@ -1024,7 +1037,7 @@
         }
 
         error = -EINVAL;
-        if ( ! presto_dentry2fset(dentry)) {
+        if ( ! presto_dentry_is_fsetroot(dentry)) {
                 EXIT;
                 goto put_out;
         }
@@ -1035,18 +1048,29 @@
                 goto put_out;
         }
 
-	error = 0;
+        cache = fset->fset_cache;
         cache = fset->fset_cache;
         cache->cache_flags &= ~CACHE_FSETROOT_SET;
 
         tmp = &cache->cache_fset_list;
         tmpnext = tmp->next;
         while ( tmpnext != &cache->cache_fset_list) {
-		tmp = tmpnext;
-                tmpnext = tmp->next;
+		tmp=tmpnext;
+                tmpnext=tmp->next;
                 fset = list_entry(tmp, struct presto_file_set, fset_list);
 
-		presto_cleanup_fset(fset);
+                
+                error = presto_close_journal_file(fset);
+                if ( error ) {
+                        printk("InterMezzo: Closing journal for fset %s: %d\n",
+                               fset->fset_name, error);
+                }
+                list_del(&fset->fset_list);
+                fset->fset_mtpt->d_fsdata = 0;
+                path_release(&fset->fset_nd);
+                fset->fset_mtpt = NULL;
+                PRESTO_FREE(fset->fset_name, strlen(fset->fset_name) +1);
+                PRESTO_FREE(fset, sizeof(*fset));
         }
 
         EXIT;
@@ -1079,7 +1103,7 @@
         }
 
         error = -EINVAL;
-        if ( ! presto_dentry2fset(dentry)) {
+        if ( ! presto_dentry_is_fsetroot(dentry)) {
                 EXIT;
                 goto kml_out;
         }
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/intermezzo/psdev.c linux/fs/intermezzo/psdev.c
--- linux.orig/fs/intermezzo/psdev.c	Fri Dec 21 17:41:55 2001
+++ linux/fs/intermezzo/psdev.c	Mon Jan  7 14:09:06 2002
@@ -1465,8 +1465,8 @@
                                 break;
                         /* signal is present: after timeout always return
                            really smart idea, probably useless ... */
-                        if ( jiffies > req->rq_posttime +
-                             upc_comms[minor].uc_timeout * HZ )
+                        if ( time_after(jiffies, req->rq_posttime +
+                             upc_comms[minor].uc_timeout * HZ) )
                                 break;
                 }
                 schedule();
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/intermezzo/super.c linux/fs/intermezzo/super.c
--- linux.orig/fs/intermezzo/super.c	Sun Nov 11 18:20:21 2001
+++ linux/fs/intermezzo/super.c	Mon Jan  7 14:09:06 2002
@@ -319,6 +319,7 @@
                 goto out_err;
         }
 
+	
 	cache->cache_sb = mysb;
         ops = filter_get_filter_fs(cache_type);
 
@@ -347,7 +348,6 @@
                                         &presto_dentry_ops);
                 presto_sb->s_root->d_op = filter_c2udops(cache->cache_filter);
                 cache->cache_mtde = mysb->s_root;
-                presto_set_dd(mysb->s_root);
         }
 
         CDEBUG(D_MALLOC, "after mounting: kmem %ld, vmem %ld\n",
@@ -481,7 +481,6 @@
         }
 
         presto_init_cache_hash();
-        presto_init_ddata_cache();
 
         status = register_filesystem(&presto_fs_type);
         if (status) {
@@ -513,7 +512,7 @@
 
         presto_psdev_cleanup();
         cleanup_intermezzo_sysctl();
-        presto_cleanup_ddata_cache();
+
 
 #ifdef PRESTO_DEVEL
         unregister_chrdev(PRESTO_PSDEV_MAJOR, "intermezzo_psdev_devel");
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/intermezzo/vfs.c linux/fs/intermezzo/vfs.c
--- linux.orig/fs/intermezzo/vfs.c	Tue Nov 13 17:20:56 2001
+++ linux/fs/intermezzo/vfs.c	Mon Jan  7 14:09:06 2002
@@ -197,7 +197,7 @@
 
         error = -EPERM;
         iops = filter_c2cdiops(fset->fset_cache->cache_filter); 
-        if (!iops) { 
+        if (!iops) {
                 EXIT;
                 return error;
         }
@@ -449,8 +449,17 @@
             dentry->d_inode->i_gid != presto_excluded_gid) {
                 struct presto_cache *cache = fset->fset_cache;
                 /* was this already done? */
-                presto_set_ops(dentry->d_inode, cache->cache_filter);
-
+                if ( !filter_c2cfiops(cache->cache_filter) )
+                        filter_setup_file_ops(cache->cache_filter, 
+                                              dentry->d_inode,
+                                              &presto_file_iops,
+                                              &presto_file_fops);
+
+                /* make the new inode ours */
+                dentry->d_inode->i_op = 
+                        filter_c2ufiops(cache->cache_filter);
+                dentry->d_inode->i_fop = 
+                        filter_c2uffops(cache->cache_filter);
                 filter_setup_dentry_ops(cache->cache_filter, 
                                         dentry->d_op, 
                                         &presto_dentry_ops);
@@ -480,8 +489,8 @@
         presto_getversion(&new_file_ver, dentry->d_inode);
         if ( presto_do_kml(info, dentry->d_inode) )
                 error = presto_journal_create(&rec, fset, dentry, &tgt_dir_ver,
-                                              &new_file_ver, 
-					      dentry->d_inode->i_mode);
+                                              &new_file_ver,
+                                              dentry->d_inode->i_mode);
 
         presto_debug_fail_blkdev(fset, PRESTO_OP_CREATE | 0x20);
         if ( presto_do_expect(info, dentry->d_inode) )
@@ -936,8 +945,15 @@
             dentry->d_inode->i_gid != presto_excluded_gid) {
                 struct presto_cache *cache = fset->fset_cache;
                 
-                presto_set_ops(dentry->d_inode, cache->cache_filter);
+                /* was this already done? */
+                if ( !filter_c2csiops(cache->cache_filter) )
+                        filter_setup_symlink_ops(cache->cache_filter, 
+                                                 dentry->d_inode,
+                                                 &presto_sym_iops,
+                                                 NULL);
 
+                /* make the new inode ours */
+                dentry->d_inode->i_op = filter_c2usiops(cache->cache_filter);
                 filter_setup_dentry_ops(cache->cache_filter, dentry->d_op, 
                                         &presto_dentry_ops);
                 dentry->d_op = filter_c2udops(cache->cache_filter);
@@ -1098,9 +1114,8 @@
         if ( dentry->d_inode && !error && 
              dentry->d_inode->i_gid != presto_excluded_gid) {
                 struct presto_cache *cache = fset->fset_cache;
-
-                presto_set_ops(dentry->d_inode, cache->cache_filter);
-
+                /* make it ours */
+                dentry->d_inode->i_op = filter_c2udiops(cache->cache_filter);
                 filter_setup_dentry_ops(cache->cache_filter, 
                                         dentry->d_op, 
                                         &presto_dentry_ops);
@@ -1128,8 +1143,8 @@
         presto_getversion(&new_dir_ver, dentry->d_inode);
         if ( presto_do_kml(info, dentry->d_inode) )
                 error = presto_journal_mkdir(&rec, fset, dentry, &tgt_dir_ver,
-                                             &new_dir_ver, 
-					     dentry->d_inode->i_mode);
+                                             &new_dir_ver,
+                                             dentry->d_inode->i_mode);
 
         presto_debug_fail_blkdev(fset, PRESTO_OP_MKDIR | 0x20);
         if ( presto_do_expect(info, dentry->d_inode) )
@@ -1398,12 +1413,15 @@
         }
 
         error = iops->mknod(dir->d_inode, dentry, mode, dev);
+        if (error) {
+                EXIT;
+                goto exit_commit;
+        }
         if ( dentry->d_inode &&
              dentry->d_inode->i_gid != presto_excluded_gid) {
                 struct presto_cache *cache = fset->fset_cache;
-
-                presto_set_ops(dentry->d_inode, cache->cache_filter);
-
+                /* make it ours */
+                dentry->d_inode->i_op = filter_c2udiops(cache->cache_filter);
                 filter_setup_dentry_ops(cache->cache_filter, dentry->d_op, 
                                         &presto_dentry_ops);
                 dentry->d_op = filter_c2udops(cache->cache_filter);
@@ -1429,8 +1447,8 @@
         presto_getversion(&new_node_ver, dentry->d_inode);
         if ( presto_do_kml(info, dentry->d_inode) )
                 error = presto_journal_mknod(&rec, fset, dentry, &tgt_dir_ver,
-                                             &new_node_ver, 
-					     dentry->d_inode->i_mode,
+                                             &new_node_ver,
+                                             dentry->d_inode->i_mode,
                                              MAJOR(dev), MINOR(dev) );
 
         presto_debug_fail_blkdev(fset, PRESTO_OP_MKNOD | 0x20);
@@ -1439,6 +1457,7 @@
 
         presto_debug_fail_blkdev(fset, PRESTO_OP_MKNOD | 0x30);
         EXIT;
+ exit_commit:
         presto_trans_commit(fset, handle);
  exit_lock2:
         unlock_kernel();
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/jffs2/dir.c linux/fs/jffs2/dir.c
--- linux.orig/fs/jffs2/dir.c	Fri Sep 14 21:04:07 2001
+++ linux/fs/jffs2/dir.c	Mon Jan  7 15:26:44 2002
@@ -31,13 +31,14 @@
  * provisions above, a recipient may use your version of this file
  * under either the RHEPL or the GPL.
  *
- * $Id: dir.c,v 1.42 2001/05/24 22:24:39 dwmw2 Exp $
+ * $Id: dir.c,v 1.45 2001/12/27 22:43:20 dwmw2 Exp $
  *
  */
 
 #include <linux/kernel.h>
 #include <linux/slab.h>
 #include <linux/fs.h>
+#include <linux/mtd/compatmac.h> /* For completion */
 #include <linux/jffs2.h>
 #include <linux/jffs2_fs_i.h>
 #include <linux/jffs2_fs_sb.h>
@@ -541,7 +542,7 @@
 
 	f = JFFS2_INODE_INFO(inode);
 
-	ri->dsize = ri->csize = strlen(target);
+	inode->i_size = ri->isize = ri->dsize = ri->csize = strlen(target);
 	ri->totlen = sizeof(*ri) + ri->dsize;
 	ri->hdr_crc = crc32(0, ri, sizeof(struct jffs2_unknown_node)-4);
 
@@ -931,20 +932,16 @@
 	ret = jffs2_do_unlink(old_dir_i, old_dentry, 1);
 	
 	if (ret) {
-		/* Try to delete the _new_ link to return a clean failure */
-		int ret2 = jffs2_do_unlink(new_dir_i, new_dentry, 1);
-		if (ret2) {
-			struct jffs2_inode_info *f = JFFS2_INODE_INFO(old_dentry->d_inode);
-			down(&f->sem);
-			old_dentry->d_inode->i_nlink = f->inocache->nlink++;
-			up(&f->sem);
+		/* Oh shit. We really ought to make a single node which can do both atomically */
+		struct jffs2_inode_info *f = JFFS2_INODE_INFO(old_dentry->d_inode);
+		down(&f->sem);
+		old_dentry->d_inode->i_nlink = f->inocache->nlink++;
+		up(&f->sem);
 		       
-			printk(KERN_NOTICE "jffs2_rename(): Link succeeded, unlink failed (old err %d, new err %d). You now have a hard link\n", ret, ret2);
-			/* Might as well let the VFS know */
-			d_instantiate(new_dentry, old_dentry->d_inode);
-			atomic_inc(&old_dentry->d_inode->i_count);
-		}
-		
+		printk(KERN_NOTICE "jffs2_rename(): Link succeeded, unlink failed (err %d). You now have a hard link\n", ret);
+		/* Might as well let the VFS know */
+		d_instantiate(new_dentry, old_dentry->d_inode);
+		atomic_inc(&old_dentry->d_inode->i_count);
 	}
 	return ret;
 }
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/jffs2/readinode.c linux/fs/jffs2/readinode.c
--- linux.orig/fs/jffs2/readinode.c	Fri Sep 14 21:04:07 2001
+++ linux/fs/jffs2/readinode.c	Mon Jan  7 15:26:44 2002
@@ -31,7 +31,7 @@
  * provisions above, a recipient may use your version of this file
  * under either the RHEPL or the GPL.
  *
- * $Id: readinode.c,v 1.56 2001/07/26 20:32:39 dwmw2 Exp $
+ * $Id: readinode.c,v 1.57 2001/12/27 22:49:46 dwmw2 Exp $
  *
  */
 
@@ -408,6 +408,12 @@
 
 	case S_IFLNK:
 		inode->i_op = &jffs2_symlink_inode_operations;
+		/* Hack to work around broken isize in old symlink code.
+		   Remove this when dwmw2 comes to his senses and stops
+		   symlinks from being an entirely gratuitous special
+		   case. */
+		if (!inode->i_size)
+			inode->i_size = latest_node.dsize;
 		break;
 		
 	case S_IFDIR:
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/jffs2/write.c linux/fs/jffs2/write.c
--- linux.orig/fs/jffs2/write.c	Thu Oct  4 22:13:18 2001
+++ linux/fs/jffs2/write.c	Mon Jan  7 14:08:22 2002
@@ -31,7 +31,7 @@
  * provisions above, a recipient may use your version of this file
  * under either the RHEPL or the GPL.
  *
- * $Id: write.c,v 1.28 2001/05/01 16:25:25 dwmw2 Exp $
+ * $Id: write.c,v 1.30 2001/12/30 16:01:11 dwmw2 Exp $
  *
  */
 
@@ -207,8 +207,6 @@
 	}
 	raw->flash_offset = flash_ofs;
 	raw->totlen = PAD(ri->totlen);
-	raw->next_in_ino = f->inocache->nodes;
-	f->inocache->nodes = raw;
 	raw->next_phys = NULL;
 
 	fn->ofs = ri->offset;
@@ -222,6 +220,14 @@
 		       sizeof(*ri)+datalen, flash_ofs, ret, retlen);
 		/* Mark the space as dirtied */
 		if (retlen) {
+			/* Doesn't belong to any inode */
+			raw->next_in_ino = NULL;
+
+			/* Don't change raw->size to match retlen. We may have 
+			   written the node header already, and only the data will
+			   seem corrupted, in which case the scan would skip over
+			   any node we write before the original intended end of 
+			   this node */
 			jffs2_add_physical_node_ref(c, raw, sizeof(*ri)+datalen, 1);
 			jffs2_mark_node_obsolete(c, raw);
 		} else {
@@ -237,6 +243,11 @@
 	}
 	/* Mark the space used */
 	jffs2_add_physical_node_ref(c, raw, retlen, 0);
+
+	/* Link into per-inode list */
+	raw->next_in_ino = f->inocache->nodes;
+	f->inocache->nodes = raw;
+
 	D1(printk(KERN_DEBUG "jffs2_write_dnode wrote node at 0x%08x with dsize 0x%x, csize 0x%x, node_crc 0x%08x, data_crc 0x%08x, totlen 0x%08x\n", flash_ofs, ri->dsize, ri->csize, ri->node_crc, ri->data_crc, ri->totlen));
 	if (writelen)
 		*writelen = retlen;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/minix/inode.c linux/fs/minix/inode.c
--- linux.orig/fs/minix/inode.c	Mon Jan  7 23:41:09 2002
+++ linux/fs/minix/inode.c	Fri Dec 21 19:17:53 2001
@@ -214,7 +214,7 @@
 	/* set up enough so that it can read an inode */
 	s->s_op = &minix_sops;
 	root_inode = iget(s, MINIX_ROOT_INO);
-	if (!root_inode)
+	if (!root_inode || is_bad_inode(root_inode))
 		goto out_no_root;
 
 	s->s_root = d_alloc_root(root_inode);
@@ -353,8 +353,10 @@
 	int i;
 
 	raw_inode = minix_V1_raw_inode(inode->i_sb, inode->i_ino, &bh);
-	if (!raw_inode)
+	if (!raw_inode) {
+		make_bad_inode(inode);
 		return;
+	}
 	inode->i_mode = raw_inode->i_mode;
 	inode->i_uid = (uid_t)raw_inode->i_uid;
 	inode->i_gid = (gid_t)raw_inode->i_gid;
@@ -378,8 +380,10 @@
 	int i;
 
 	raw_inode = minix_V2_raw_inode(inode->i_sb, inode->i_ino, &bh);
-	if (!raw_inode)
+	if (!raw_inode) {
+		make_bad_inode(inode);
 		return;
+	}
 	inode->i_mode = raw_inode->i_mode;
 	inode->i_uid = (uid_t)raw_inode->i_uid;
 	inode->i_gid = (gid_t)raw_inode->i_gid;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/nfs/file.c linux/fs/nfs/file.c
--- linux.orig/fs/nfs/file.c	Mon Jan  7 23:41:09 2002
+++ linux/fs/nfs/file.c	Wed Dec 26 17:15:45 2001
@@ -161,15 +161,10 @@
 static int nfs_commit_write(struct file *file, struct page *page, unsigned offset, unsigned to)
 {
 	long status;
-	loff_t pos = ((loff_t)page->index<<PAGE_CACHE_SHIFT) + to;
-	struct inode *inode = page->mapping->host;
 
 	lock_kernel();
 	status = nfs_updatepage(file, page, offset, to-offset);
 	unlock_kernel();
-	/* most likely it's already done. CHECKME */
-	if (pos > inode->i_size)
-		inode->i_size = pos;
 	return status;
 }
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/nfs/inode.c linux/fs/nfs/inode.c
--- linux.orig/fs/nfs/inode.c	Mon Jan  7 23:41:09 2002
+++ linux/fs/nfs/inode.c	Wed Dec 26 17:15:45 2001
@@ -107,17 +107,10 @@
 	inode->i_rdev = 0;
 	/* We can't support UPDATE_ATIME(), since the server will reset it */
 	inode->i_flags |= S_NOATIME;
-	NFS_FILEID(inode) = 0;
-	NFS_FSID(inode) = 0;
-	NFS_FLAGS(inode) = 0;
 	INIT_LIST_HEAD(&inode->u.nfs_i.read);
 	INIT_LIST_HEAD(&inode->u.nfs_i.dirty);
 	INIT_LIST_HEAD(&inode->u.nfs_i.commit);
 	INIT_LIST_HEAD(&inode->u.nfs_i.writeback);
-	inode->u.nfs_i.nread = 0;
-	inode->u.nfs_i.ndirty = 0;
-	inode->u.nfs_i.ncommit = 0;
-	inode->u.nfs_i.npages = 0;
 	NFS_CACHEINV(inode);
 	NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode);
 	NFS_ATTRTIMEO_UPDATE(inode) = jiffies;
@@ -655,19 +648,6 @@
 			inode->i_op = &nfs_symlink_inode_operations;
 		else
 			init_special_inode(inode, inode->i_mode, fattr->rdev);
-		/*
-		 * Preset the size and mtime, as there's no need
-		 * to invalidate the caches.
-		 */ 
-		inode->i_size  = nfs_size_to_loff_t(fattr->size);
-		inode->i_mtime = nfs_time_to_secs(fattr->mtime);
-		inode->i_atime = nfs_time_to_secs(fattr->atime);
-		inode->i_ctime = nfs_time_to_secs(fattr->ctime);
-		NFS_CACHE_CTIME(inode) = fattr->ctime;
-		NFS_CACHE_MTIME(inode) = fattr->mtime;
-		NFS_CACHE_ISIZE(inode) = fattr->size;
-		NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode);
-		NFS_ATTRTIMEO_UPDATE(inode) = jiffies;
 		memcpy(&inode->u.nfs_i.fh, fh, sizeof(inode->u.nfs_i.fh));
 	}
 	nfs_refresh_inode(inode, fattr);
@@ -697,6 +677,9 @@
 		return 0;
 	if (memcmp(&inode->u.nfs_i.fh, fh, sizeof(inode->u.nfs_i.fh)) != 0)
 		return 0;
+	/* Force an attribute cache update if inode->i_count == 0 */
+	if (!atomic_read(&inode->i_count))
+		NFS_CACHEINV(inode);
 	return 1;
 }
 
@@ -797,7 +780,9 @@
 	if (!S_ISREG(inode->i_mode))
 		attr->ia_valid &= ~ATTR_SIZE;
 
+	filemap_fdatasync(inode->i_mapping);
 	error = nfs_wb_all(inode);
+	filemap_fdatawait(inode->i_mapping);
 	if (error)
 		goto out;
 
@@ -825,6 +810,8 @@
 		fattr.pre_ctime = NFS_CACHE_CTIME(inode);
 		fattr.valid |= NFS_ATTR_WCC;
 	}
+	/* Force an attribute cache update */
+	NFS_CACHEINV(inode);
 	error = nfs_refresh_inode(inode, &fattr);
 out:
 	return error;
@@ -966,6 +953,34 @@
 }
 
 /*
+ * nfs_fattr_obsolete - Test if attribute data is newer than cached data
+ * @inode: inode
+ * @fattr: attributes to test
+ *
+ * Avoid stuffing the attribute cache with obsolete information.
+ * We always accept updates if the attribute cache timed out, or if
+ * fattr->ctime is newer than our cached value.
+ * If fattr->ctime matches the cached value, we still accept the update
+ * if it increases the file size.
+ */
+static inline
+int nfs_fattr_obsolete(struct inode *inode, struct nfs_fattr *fattr)
+{
+	s64 cdif;
+
+	if (time_after(jiffies, NFS_READTIME(inode)+NFS_ATTRTIMEO(inode)))
+		goto out_valid;
+	if ((cdif = (s64)fattr->ctime - (s64)NFS_CACHE_CTIME(inode)) > 0)
+		goto out_valid;
+	/* Ugh... */
+	if (cdif == 0 && fattr->size > NFS_CACHE_ISIZE(inode))
+		goto out_valid;
+	return -1;
+ out_valid:
+	return 0;
+}
+
+/*
  * Many nfs protocol calls return the new file attributes after
  * an operation.  Here we update the inode to reflect the state
  * of the server's inode.
@@ -982,6 +997,7 @@
 {
 	__u64		new_size, new_mtime;
 	loff_t		new_isize;
+	time_t		new_atime;
 	int		invalid = 0;
 
 	dfprintk(VFS, "NFS: refresh_inode(%x/%ld ct=%d info=0x%x)\n",
@@ -1007,6 +1023,11 @@
 	new_size = fattr->size;
  	new_isize = nfs_size_to_loff_t(fattr->size);
 
+	new_atime = nfs_time_to_secs(fattr->atime);
+	/* Avoid races */
+	if (nfs_fattr_obsolete(inode, fattr))
+		goto out_nochange;
+
 	/*
 	 * Update the read time so we don't revalidate too often.
 	 */
@@ -1056,7 +1077,7 @@
 	NFS_CACHE_CTIME(inode) = fattr->ctime;
 	inode->i_ctime = nfs_time_to_secs(fattr->ctime);
 
-	inode->i_atime = nfs_time_to_secs(fattr->atime);
+	inode->i_atime = new_atime;
 
 	NFS_CACHE_MTIME(inode) = new_mtime;
 	inode->i_mtime = nfs_time_to_secs(new_mtime);
@@ -1093,7 +1114,10 @@
 	if (invalid)
 		nfs_zap_caches(inode);
 	return 0;
-
+ out_nochange:
+	if (new_atime - inode->i_atime > 0)
+		inode->i_atime = new_atime;
+	return 0;
  out_changed:
 	/*
 	 * Big trouble! The inode has become a different object.
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/nfs/nfs2xdr.c linux/fs/nfs/nfs2xdr.c
--- linux.orig/fs/nfs/nfs2xdr.c	Mon Jan  7 23:41:09 2002
+++ linux/fs/nfs/nfs2xdr.c	Wed Dec 26 17:15:45 2001
@@ -270,14 +270,12 @@
 
 	count = ntohl(*p++);
 	hdrlen = (u8 *) p - (u8 *) iov->iov_base;
-	recvd = req->rq_rlen - hdrlen;
-	if (p != iov[req->rq_rnr-1].iov_base) {
-		/* Unexpected reply header size. Punt.
-		 * XXX: Move iovec contents to align data on page
-		 * boundary and adjust RPC header size guess */
-		printk(KERN_WARNING "NFS: Odd RPC header size in read reply: %d\n", hdrlen);
-		return -errno_NFSERR_IO;
+	if (iov->iov_len > hdrlen) {
+		dprintk("NFS: READ header is short. iovec will be shifted.\n");
+		xdr_shift_iovec(iov, req->rq_rnr, iov->iov_len - hdrlen);
 	}
+
+	recvd = req->rq_rlen - hdrlen;
 	if (count > recvd) {
 		printk(KERN_WARNING "NFS: server cheating in read reply: "
 			"count %d > recvd %d\n", count, recvd);
@@ -448,27 +446,23 @@
 nfs_xdr_readdirres(struct rpc_rqst *req, u32 *p, struct nfs_readdirres *res)
 {
 	struct iovec		*iov = req->rq_rvec;
+	int			 hdrlen;
 	int			 status, nr;
 	u32			*end, *entry, len;
 
 	if ((status = ntohl(*p++)))
 		return -nfs_stat_to_errno(status);
-	if ((void *) p != ((u8 *) iov->iov_base+iov->iov_len)) {
-		/* Unexpected reply header size. Punt. */
-		printk(KERN_WARNING "NFS: Odd RPC header size in readdirres reply\n");
-		return -errno_NFSERR_IO;
+
+	hdrlen = (u8 *) p - (u8 *) iov->iov_base;
+	if (iov->iov_len > hdrlen) {
+		dprintk("NFS: READDIR header is short. iovec will be shifted.\n");
+		xdr_shift_iovec(iov, req->rq_rnr, iov->iov_len - hdrlen);
 	}
 
+
 	/* Get start and end address of XDR data */
 	p   = (u32 *) iov[1].iov_base;
 	end = (u32 *) ((u8 *) p + iov[1].iov_len);
-
-	/* Get start and end of dirent buffer */
-	if (res->buffer != p) {
-		printk(KERN_ERR "NFS: Bad result buffer in readdir\n");
-		return -errno_NFSERR_IO;
-	}
-
 	for (nr = 0; *p++; nr++) {
 		entry = p - 1;
 		if (p + 2 > end)
@@ -598,13 +592,21 @@
 static int
 nfs_xdr_readlinkres(struct rpc_rqst *req, u32 *p, struct nfs_readlinkres *res)
 {
+	struct iovec *iov = req->rq_rvec;
 	u32	*strlen;
 	char	*string;
+	int	hdrlen;
 	int	status;
 	unsigned int len;
 
 	if ((status = ntohl(*p++)))
 		return -nfs_stat_to_errno(status);
+	hdrlen = (u8 *) p - (u8 *) iov->iov_base;
+	if (iov->iov_len > hdrlen) {
+		dprintk("NFS: READLINK header is short. iovec will be shifted.\n");
+		xdr_shift_iovec(iov, req->rq_rnr, iov->iov_len - hdrlen);
+	}
+
 	strlen = (u32*)res->buffer;
 	/* Convert length of symlink */
 	len = ntohl(*strlen);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/nfs/read.c linux/fs/nfs/read.c
--- linux.orig/fs/nfs/read.c	Mon Jan  7 23:41:09 2002
+++ linux/fs/nfs/read.c	Wed Dec 26 17:15:45 2001
@@ -397,7 +397,7 @@
 {
 	struct nfs_read_data	*data = (struct nfs_read_data *) task->tk_calldata;
 	struct inode		*inode = data->inode;
-	int			count = data->res.count;
+	unsigned int		count = data->res.count;
 
 	dprintk("NFS: %4d nfs_readpage_result, (status %d)\n",
 		task->tk_pid, task->tk_status);
@@ -411,9 +411,15 @@
 		struct page *page = req->wb_page;
 		nfs_list_remove_request(req);
 
-		if (task->tk_status >= 0 && count >= 0) {
+		if (task->tk_status >= 0) {
+			if (count < PAGE_CACHE_SIZE) {
+				char *p = kmap(page);
+				memset(p + count, 0, PAGE_CACHE_SIZE - count);
+				kunmap(page);
+				count = 0;
+			} else
+				count -= PAGE_CACHE_SIZE;
 			SetPageUptodate(page);
-			count -= PAGE_CACHE_SIZE;
 		} else
 			SetPageError(page);
 		flush_dcache_page(page);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/nfs/write.c linux/fs/nfs/write.c
--- linux.orig/fs/nfs/write.c	Mon Jan  7 23:41:09 2002
+++ linux/fs/nfs/write.c	Wed Dec 26 17:15:45 2001
@@ -213,6 +213,7 @@
 		    unsigned int offset, unsigned int count)
 {
 	struct nfs_page	*req;
+	loff_t		end;
 	int		status;
 
 	req = nfs_update_request(file, inode, page, offset, count);
@@ -223,6 +224,10 @@
 		req->wb_cred = get_rpccred(NFS_I(inode)->mm_cred);
 	nfs_unlock_request(req);
 	nfs_strategy(inode);
+	end = ((loff_t)page->index<<PAGE_CACHE_SHIFT) + (loff_t)(offset + count);
+	if (inode->i_size < end)
+		inode->i_size = end;
+
  out:
 	return status;
 }
@@ -795,6 +800,7 @@
 	struct dentry	*dentry = file->f_dentry;
 	struct inode	*inode = dentry->d_inode;
 	struct nfs_page	*req;
+	loff_t		end;
 	int		status = 0;
 
 	dprintk("NFS:      nfs_updatepage(%s/%s %d@%Ld)\n",
@@ -826,6 +832,10 @@
 		goto done;
 
 	status = 0;
+	end = ((loff_t)page->index<<PAGE_CACHE_SHIFT) + (loff_t)(offset + count);
+	if (inode->i_size < end)
+		inode->i_size = end;
+
 	/* If we wrote past the end of the page.
 	 * Call the strategy routine so it can send out a bunch
 	 * of requests.
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/reiserfs/inode.c linux/fs/reiserfs/inode.c
--- linux.orig/fs/reiserfs/inode.c	Mon Jan  7 23:41:09 2002
+++ linux/fs/reiserfs/inode.c	Mon Jan  7 18:15:56 2002
@@ -273,7 +273,9 @@
 	pathrelse (&path);
         if (p)
             kunmap(bh_result->b_page) ;
-	if ((args & GET_BLOCK_NO_HOLE)) {
+	// We do not return -ENOENT if there is a hole but page is uptodate, because it means
+	// That there is some MMAPED data associated with it that is yet to be written to disk.
+	if ((args & GET_BLOCK_NO_HOLE) && !Page_Uptodate(bh_result->b_page) ) {
 	    return -ENOENT ;
 	}
         return 0 ;
@@ -294,9 +296,13 @@
 	    bh_result->b_dev = inode->i_dev;
 	    bh_result->b_blocknr = blocknr;
 	    bh_result->b_state |= (1UL << BH_Mapped);
-	} else if ((args & GET_BLOCK_NO_HOLE)) {
-	    ret = -ENOENT ;
-	}
+	} else
+	    // We do not return -ENOENT if there is a hole but page is uptodate, because it means
+	    // That there is some MMAPED data associated with it that is yet to be written to disk.
+	    if ((args & GET_BLOCK_NO_HOLE) && !Page_Uptodate(bh_result->b_page) ) {
+		ret = -ENOENT ;
+	    }
+
 	pathrelse (&path);
         if (p)
             kunmap(bh_result->b_page) ;
@@ -319,6 +325,16 @@
     */
     if (buffer_uptodate(bh_result)) {
         goto finished ;
+    } else 
+	/*
+	** grab_tail_page can trigger calls to reiserfs_get_block on up to date
+	** pages without any buffers.  If the page is up to date, we don't want
+	** read old data off disk.  Set the up to date bit on the buffer instead
+	** and jump to the end
+	*/
+	    if (Page_Uptodate(bh_result->b_page)) {
+		mark_buffer_uptodate(bh_result, 1);
+		goto finished ;
     }
 
     // read file tail into part of page
@@ -827,7 +843,7 @@
 	}
 	if (retval == POSITION_FOUND) {
 	    reiserfs_warning ("vs-825: reiserfs_get_block: "
-			      "%k should not be found\n", &key);
+			      "%K should not be found\n", &key);
 	    retval = -EEXIST;
 	    if (allocated_block_nr)
 	        reiserfs_free_block (&th, allocated_block_nr);
@@ -1766,6 +1782,7 @@
     int bytes_copied = 0 ;
     int copy_size ;
 
+    kmap(bh_result->b_page) ;
 start_over:
     lock_kernel() ;
     journal_begin(&th, inode->i_sb, jbegin_count) ;
@@ -1838,10 +1855,8 @@
 
     /* this is where we fill in holes in the file. */
     if (use_get_block) {
-        kmap(bh_result->b_page) ;
 	retval = reiserfs_get_block(inode, block, bh_result, 
 	                            GET_BLOCK_CREATE | GET_BLOCK_NO_ISEM) ;
-        kunmap(bh_result->b_page) ;
 	if (!retval) {
 	    if (!buffer_mapped(bh_result) || bh_result->b_blocknr == 0) {
 	        /* get_block failed to find a mapped unformatted node. */
@@ -1850,6 +1865,7 @@
 	    }
 	}
     }
+    kunmap(bh_result->b_page) ;
     return retval ;
 }
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/reiserfs/journal.c linux/fs/reiserfs/journal.c
--- linux.orig/fs/reiserfs/journal.c	Fri Dec 21 17:42:03 2001
+++ linux/fs/reiserfs/journal.c	Mon Jan  7 18:15:56 2002
@@ -1851,7 +1851,7 @@
       break ;
     }
     wake_up(&reiserfs_commit_thread_done) ;
-    interruptible_sleep_on_timeout(&reiserfs_commit_thread_wait, 5) ;
+    interruptible_sleep_on_timeout(&reiserfs_commit_thread_wait, 5 * HZ) ;
   }
   unlock_kernel() ;
   wake_up(&reiserfs_commit_thread_done) ;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/reiserfs/namei.c linux/fs/reiserfs/namei.c
--- linux.orig/fs/reiserfs/namei.c	Fri Dec 21 17:42:03 2001
+++ linux/fs/reiserfs/namei.c	Mon Jan  7 18:15:56 2002
@@ -309,9 +309,10 @@
 
     while (1) {
 	retval = search_by_entry_key (dir->i_sb, &key_to_search, path_to_entry, de);
-	if (retval == IO_ERROR)
-	    // FIXME: still has to be dealt with
-	    reiserfs_panic (dir->i_sb, "zam-7001: io error in " __FUNCTION__ "\n");
+	if (retval == IO_ERROR) {
+	    reiserfs_warning ("zam-7001: io error in " __FUNCTION__ "\n");
+	    return IO_ERROR;
+	}
 
 	/* compare names for all entries having given hash value */
 	retval = linear_search_in_dir_item (&key_to_search, de, name, namelen);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/ufs/inode.c linux/fs/ufs/inode.c
--- linux.orig/fs/ufs/inode.c	Mon Jan  7 23:41:09 2002
+++ linux/fs/ufs/inode.c	Fri Dec 21 19:17:53 2001
@@ -497,13 +497,13 @@
 	if (inode->i_ino < UFS_ROOTINO || 
 	    inode->i_ino > (uspi->s_ncg * uspi->s_ipg)) {
 		ufs_warning (sb, "ufs_read_inode", "bad inode number (%lu)\n", inode->i_ino);
-		return;
+		goto bad_inode;
 	}
 	
 	bh = bread (sb->s_dev, uspi->s_sbbase + ufs_inotofsba(inode->i_ino), sb->s_blocksize);
 	if (!bh) {
 		ufs_warning (sb, "ufs_read_inode", "unable to read inode %lu\n", inode->i_ino);
-		return;
+		goto bad_inode;
 	}
 	ufs_inode = (struct ufs_inode *) (bh->b_data + sizeof(struct ufs_inode) * ufs_inotofsbo(inode->i_ino));
 
@@ -568,6 +568,11 @@
 	brelse (bh);
 
 	UFSD(("EXIT\n"))
+	return;
+
+bad_inode:
+	make_bad_inode(inode);
+	return;
 }
 
 static int ufs_update_inode(struct inode * inode, int do_sync)
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/ufs/super.c linux/fs/ufs/super.c
--- linux.orig/fs/ufs/super.c	Mon Jan  7 23:41:09 2002
+++ linux/fs/ufs/super.c	Fri Dec 21 19:17:53 2001
@@ -442,6 +442,7 @@
 	struct ufs_super_block_second * usb2;
 	struct ufs_super_block_third * usb3;
 	struct ufs_buffer_head * ubh;	
+	struct inode *inode;
 	unsigned block_size, super_block_size;
 	unsigned flags;
 
@@ -789,7 +790,13 @@
 		    fs32_to_cpu(sb, usb3->fs_u2.fs_44.fs_maxsymlinklen);
 	
 	sb->u.ufs_sb.s_flags = flags;
-	sb->s_root = d_alloc_root(iget(sb, UFS_ROOTINO));
+
+	inode = iget(sb, UFS_ROOTINO);
+	if (!inode || is_bad_inode(inode))
+		goto failed;
+	sb->s_root = d_alloc_root(inode);
+	if (!sb->s_root)
+		goto dalloc_failed;
 
 
 	/*
@@ -802,6 +809,8 @@
 	UFSD(("EXIT\n"))
 	return(sb);
 
+dalloc_failed:
+	iput(inode);
 failed:
 	if (ubh) ubh_brelse_uspi (uspi);
 	if (uspi) kfree (uspi);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-alpha/pci.h linux/include/asm-alpha/pci.h
--- linux.orig/include/asm-alpha/pci.h	Mon Jan  7 23:41:09 2002
+++ linux/include/asm-alpha/pci.h	Fri Dec 21 18:25:01 2001
@@ -101,6 +101,20 @@
 extern void pci_unmap_single(struct pci_dev *, dma_addr_t, size_t, int);
 extern void pci_unmap_page(struct pci_dev *, dma_addr_t, size_t, int);
 
+/* pci_unmap_{single,page} is not a nop, thus... */
+#define DECLARE_PCI_UNMAP_ADDR(ADDR_NAME)	\
+	dma_addr_t ADDR_NAME;
+#define DECLARE_PCI_UNMAP_LEN(LEN_NAME)		\
+	__u32 LEN_NAME;
+#define PCI_UNMAP_ADDR(PTR, ADDR_NAME)			\
+	((PTR)->ADDR_NAME)
+#define PCI_UNMAP_ADDR_SET(PTR, ADDR_NAME, VAL)		\
+	(((PTR)->ADDR_NAME) = (VAL))
+#define PCI_UNMAP_LEN(PTR, LEN_NAME)			\
+	((PTR)->LEN_NAME)
+#define PCI_UNMAP_LEN_SET(PTR, LEN_NAME, VAL)		\
+	(((PTR)->LEN_NAME) = (VAL))
+
 /* Map a set of buffers described by scatterlist in streaming mode for
    PCI DMA.  This is the scather-gather version of the above
    pci_map_single interface.  Here the scatter gather list elements
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-arm/pci.h linux/include/asm-arm/pci.h
--- linux.orig/include/asm-arm/pci.h	Mon Jan  7 23:41:09 2002
+++ linux/include/asm-arm/pci.h	Fri Dec 21 18:25:01 2001
@@ -87,6 +87,31 @@
 	/* nothing to do */
 }
 
+/* Whether pci_unmap_{single,page} is a nop depends upon the
+ * configuration.
+ */
+#ifdef CONFIG_SA1111
+#define DECLARE_PCI_UNMAP_ADDR(ADDR_NAME)	\
+	dma_addr_t ADDR_NAME;
+#define DECLARE_PCI_UNMAP_LEN(LEN_NAME)		\
+	__u32 LEN_NAME;
+#define PCI_UNMAP_ADDR(PTR, ADDR_NAME)			\
+	((PTR)->ADDR_NAME)
+#define PCI_UNMAP_ADDR_SET(PTR, ADDR_NAME, VAL)		\
+	(((PTR)->ADDR_NAME) = (VAL))
+#define PCI_UNMAP_LEN(PTR, LEN_NAME)			\
+	((PTR)->LEN_NAME)
+#define PCI_UNMAP_LEN_SET(PTR, LEN_NAME, VAL)		\
+	(((PTR)->LEN_NAME) = (VAL))
+#else /* !(CONFIG_SA1111) */
+#define DECLARE_PCI_UNMAP_ADDR(ADDR_NAME)
+#define DECLARE_PCI_UNMAP_LEN(LEN_NAME)
+#define PCI_UNMAP_ADDR(PTR, ADDR_NAME)		(0)
+#define PCI_UNMAP_ADDR_SET(PTR, ADDR_NAME, VAL)	do { } while (0)
+#define PCI_UNMAP_LEN(PTR, LEN_NAME)		(0)
+#define PCI_UNMAP_LEN_SET(PTR, LEN_NAME, VAL)	do { } while (0)
+#endif /* CONFIG_SA1111 */
+
 /* Map a set of buffers described by scatterlist in streaming
  * mode for DMA.  This is the scather-gather version of the
  * above pci_map_single interface.  Here the scatter gather list
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-i386/page.h linux/include/asm-i386/page.h
--- linux.orig/include/asm-i386/page.h	Mon Jan  7 23:41:09 2002
+++ linux/include/asm-i386/page.h	Wed Dec 26 17:05:14 2001
@@ -80,6 +80,12 @@
 
 #define __PAGE_OFFSET		(0xC0000000)
 
+/*
+ * This much address space is reserved for vmalloc() and iomap()
+ * as well as fixmap mappings.
+ */
+#define __VMALLOC_RESERVE	(128 << 20)
+
 #ifndef __ASSEMBLY__
 
 /*
@@ -118,6 +124,9 @@
 #endif /* __ASSEMBLY__ */
 
 #define PAGE_OFFSET		((unsigned long)__PAGE_OFFSET)
+#define VMALLOC_RESERVE		((unsigned long)__VMALLOC_RESERVE)
+#define __MAXMEM		(-__PAGE_OFFSET-__VMALLOC_RESERVE)
+#define MAXMEM			((unsigned long)(-PAGE_OFFSET-VMALLOC_RESERVE))
 #define __pa(x)			((unsigned long)(x)-PAGE_OFFSET)
 #define __va(x)			((void *)((unsigned long)(x)+PAGE_OFFSET))
 #define virt_to_page(kaddr)	(mem_map + (__pa(kaddr) >> PAGE_SHIFT))
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-i386/pci.h linux/include/asm-i386/pci.h
--- linux.orig/include/asm-i386/pci.h	Mon Jan  7 23:41:09 2002
+++ linux/include/asm-i386/pci.h	Fri Dec 21 18:25:01 2001
@@ -114,6 +114,14 @@
 	/* Nothing to do */
 }
 
+/* pci_unmap_{page,single} is a nop so... */
+#define DECLARE_PCI_UNMAP_ADDR(ADDR_NAME)
+#define DECLARE_PCI_UNMAP_LEN(LEN_NAME)
+#define PCI_UNMAP_ADDR(PTR, ADDR_NAME)		(0)
+#define PCI_UNMAP_ADDR_SET(PTR, ADDR_NAME, VAL)	do { } while (0)
+#define PCI_UNMAP_LEN(PTR, LEN_NAME)		(0)
+#define PCI_UNMAP_LEN_SET(PTR, LEN_NAME, VAL)	do { } while (0)
+
 /* Map a set of buffers described by scatterlist in streaming
  * mode for DMA.  This is the scather-gather version of the
  * above pci_map_single interface.  Here the scatter gather list
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-ia64/pci.h linux/include/asm-ia64/pci.h
--- linux.orig/include/asm-ia64/pci.h	Mon Jan  7 23:41:09 2002
+++ linux/include/asm-ia64/pci.h	Fri Dec 21 18:25:01 2001
@@ -46,6 +46,20 @@
 #define pci_dma_sync_sg			platform_pci_dma_sync_sg
 #define sg_dma_address			platform_pci_dma_address
 
+/* pci_unmap_{single,page} is not a nop, thus... */
+#define DECLARE_PCI_UNMAP_ADDR(ADDR_NAME)	\
+	dma_addr_t ADDR_NAME;
+#define DECLARE_PCI_UNMAP_LEN(LEN_NAME)		\
+	__u32 LEN_NAME;
+#define PCI_UNMAP_ADDR(PTR, ADDR_NAME)			\
+	((PTR)->ADDR_NAME)
+#define PCI_UNMAP_ADDR_SET(PTR, ADDR_NAME, VAL)		\
+	(((PTR)->ADDR_NAME) = (VAL))
+#define PCI_UNMAP_LEN(PTR, LEN_NAME)			\
+	((PTR)->LEN_NAME)
+#define PCI_UNMAP_LEN_SET(PTR, LEN_NAME, VAL)		\
+	(((PTR)->LEN_NAME) = (VAL))
+
 /*
  * Return whether the given PCI device DMA address mask can be supported properly.  For
  * example, if your device can only drive the low 24-bits during PCI bus mastering, then
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-mips/pci.h linux/include/asm-mips/pci.h
--- linux.orig/include/asm-mips/pci.h	Mon Jan  7 23:41:09 2002
+++ linux/include/asm-mips/pci.h	Fri Dec 21 18:25:01 2001
@@ -114,6 +114,14 @@
 	/* Nothing to do */
 }
 
+/* pci_unmap_{page,single} is a nop so... */
+#define DECLARE_PCI_UNMAP_ADDR(ADDR_NAME)
+#define DECLARE_PCI_UNMAP_LEN(LEN_NAME)
+#define PCI_UNMAP_ADDR(PTR, ADDR_NAME)		(0)
+#define PCI_UNMAP_ADDR_SET(PTR, ADDR_NAME, VAL)	do { } while (0)
+#define PCI_UNMAP_LEN(PTR, LEN_NAME)		(0)
+#define PCI_UNMAP_LEN_SET(PTR, LEN_NAME, VAL)	do { } while (0)
+
 /*
  * Map a set of buffers described by scatterlist in streaming
  * mode for DMA.  This is the scather-gather version of the
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-mips64/pci.h linux/include/asm-mips64/pci.h
--- linux.orig/include/asm-mips64/pci.h	Mon Jan  7 23:41:09 2002
+++ linux/include/asm-mips64/pci.h	Fri Dec 21 18:25:01 2001
@@ -86,6 +86,20 @@
 extern void pci_dma_sync_sg(struct pci_dev *hwdev, struct scatterlist *sg,
                             int nelems, int direction);
 
+/* pci_unmap_{single,page} is not a nop, thus... */
+#define DECLARE_PCI_UNMAP_ADDR(ADDR_NAME)	\
+	dma_addr_t ADDR_NAME;
+#define DECLARE_PCI_UNMAP_LEN(LEN_NAME)		\
+	__u32 LEN_NAME;
+#define PCI_UNMAP_ADDR(PTR, ADDR_NAME)			\
+	((PTR)->ADDR_NAME)
+#define PCI_UNMAP_ADDR_SET(PTR, ADDR_NAME, VAL)		\
+	(((PTR)->ADDR_NAME) = (VAL))
+#define PCI_UNMAP_LEN(PTR, LEN_NAME)			\
+	((PTR)->LEN_NAME)
+#define PCI_UNMAP_LEN_SET(PTR, LEN_NAME, VAL)		\
+	(((PTR)->LEN_NAME) = (VAL))
+
 #else /* CONFIG_MAPPED_PCI_IO  */
 
 /*
@@ -123,6 +137,14 @@
 
 	/* Nothing to do */
 }
+
+/* pci_unmap_{page,single} is a nop so... */
+#define DECLARE_PCI_UNMAP_ADDR(ADDR_NAME)
+#define DECLARE_PCI_UNMAP_LEN(LEN_NAME)
+#define PCI_UNMAP_ADDR(PTR, ADDR_NAME)		(0)
+#define PCI_UNMAP_ADDR_SET(PTR, ADDR_NAME, VAL)	do { } while (0)
+#define PCI_UNMAP_LEN(PTR, LEN_NAME)		(0)
+#define PCI_UNMAP_LEN_SET(PTR, LEN_NAME, VAL)	do { } while (0)
 
 /*
  * Map a set of buffers described by scatterlist in streaming
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-parisc/pci.h linux/include/asm-parisc/pci.h
--- linux.orig/include/asm-parisc/pci.h	Mon Jan  7 23:41:09 2002
+++ linux/include/asm-parisc/pci.h	Fri Dec 21 18:25:01 2001
@@ -172,6 +172,20 @@
 #define pci_map_sg(p, sg, n, d)		hppa_dma_ops->map_sg(p, sg, n, d)
 #define pci_unmap_sg(p, sg, n, d)	hppa_dma_ops->unmap_sg(p, sg, n, d)
 
+/* pci_unmap_{single,page} is not a nop, thus... */
+#define DECLARE_PCI_UNMAP_ADDR(ADDR_NAME)	\
+	dma_addr_t ADDR_NAME;
+#define DECLARE_PCI_UNMAP_LEN(LEN_NAME)		\
+	__u32 LEN_NAME;
+#define PCI_UNMAP_ADDR(PTR, ADDR_NAME)			\
+	((PTR)->ADDR_NAME)
+#define PCI_UNMAP_ADDR_SET(PTR, ADDR_NAME, VAL)		\
+	(((PTR)->ADDR_NAME) = (VAL))
+#define PCI_UNMAP_LEN(PTR, LEN_NAME)			\
+	((PTR)->LEN_NAME)
+#define PCI_UNMAP_LEN_SET(PTR, LEN_NAME, VAL)		\
+	(((PTR)->LEN_NAME) = (VAL))
+
 /* For U2/Astro/Ike based platforms (which are fully I/O coherent)
 ** dma_sync is a NOP. Let's keep the performance path short here.
 */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-ppc/ans-lcd.h linux/include/asm-ppc/ans-lcd.h
--- linux.orig/include/asm-ppc/ans-lcd.h	Thu Jan  1 00:00:00 1970
+++ linux/include/asm-ppc/ans-lcd.h	Wed Dec 26 16:36:12 2001
@@ -0,0 +1,11 @@
+#ifndef _PPC_ANS_LCD_H
+#define _PPC_ANS_LCD_H
+
+#define ANSLCD_MINOR		156
+
+#define ANSLCD_CLEAR		0x01
+#define ANSLCD_SENDCTRL		0x02
+#define ANSLCD_SETSHORTDELAY	0x03
+#define ANSLCD_SETLONGDELAY	0x04
+
+#endif
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-ppc/dbdma.h linux/include/asm-ppc/dbdma.h
--- linux.orig/include/asm-ppc/dbdma.h	Mon Jan  7 23:41:09 2002
+++ linux/include/asm-ppc/dbdma.h	Wed Dec 26 16:36:12 2001
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.dbdma.h 1.5 05/17/01 18:14:24 cort
+ * BK Id: SCCS/s.dbdma.h 1.8 12/01/01 20:09:11 benh
  */
 /*
  * Definitions for using the Apple Descriptor-Based DMA controller
@@ -93,5 +93,13 @@
 /* Align an address for a DBDMA command structure */
 #define DBDMA_ALIGN(x)	(((unsigned)(x) + sizeof(struct dbdma_cmd) - 1) \
 			 & -sizeof(struct dbdma_cmd))
+
+/* Useful macros */
+#define DBDMA_DO_STOP(regs) do {				\
+	out_le32(&((regs)->control), (RUN|FLUSH)<<16);		\
+	while(in_le32(&((regs)->status)) & (ACTIVE|FLUSH))	\
+		;						\
+} while(0)
+
 #endif /* _ASM_DBDMA_H_ */
 #endif /* __KERNEL__ */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-ppc/feature.h linux/include/asm-ppc/feature.h
--- linux.orig/include/asm-ppc/feature.h	Mon Jan  7 23:41:09 2002
+++ linux/include/asm-ppc/feature.h	Thu Jan  1 00:00:00 1970
@@ -1,111 +0,0 @@
-/*
- * BK Id: SCCS/s.feature.h 1.13 08/19/01 22:23:04 paulus
- */
-/*
- * Definitions for accessing the Feature Control Register (FCR)
- * on Power Macintoshes and similar machines.  The FCR lets us
- * enable/disable, reset, and power up/down various peripherals.
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License.  See the file "COPYING" in the main directory of this archive
- * for more details.
- *
- * Copyright (C) 1998 Paul Mackerras &
- *                    Ben. Herrenschmidt.
- *
- * 
- */
-#ifdef __KERNEL__
-#ifndef __ASM_PPC_FEATURE_H
-#define __ASM_PPC_FEATURE_H
-
-/*
- * The FCR selector for particular features vary somewhat between
- * different machines.  So we abstract a list of features here
- * and let the feature_* routines map them to the actual bits.
- */
-enum system_feature {
-	FEATURE_null,
-	FEATURE_Serial_reset,
-	FEATURE_Serial_enable,
-	FEATURE_Serial_IO_A,
-	FEATURE_Serial_IO_B,
-	FEATURE_SWIM3_enable,
-	FEATURE_MESH_enable,
-	FEATURE_IDE0_enable,		/* Internal IDE */
-	FEATURE_IDE0_reset,		/* Internal IDE */
-	FEATURE_IOBUS_enable,		/* Internal IDE */
-	FEATURE_Mediabay_reset,
-	FEATURE_Mediabay_power,
-	FEATURE_Mediabay_PCI_enable,
-	FEATURE_IDE1_enable,		/* MediaBay IDE */
-	FEATURE_IDE1_reset,		/* MediaBay IDE */
-	FEATURE_Mediabay_floppy_enable,
-	FEATURE_BMac_reset,
-	FEATURE_BMac_IO_enable,
-	FEATURE_Modem_power,
-	FEATURE_Slow_SCC_PCLK,
-	FEATURE_Sound_power,
-	FEATURE_Sound_CLK_enable,
-	FEATURE_IDE2_enable,
-	FEATURE_IDE2_reset,
-	FEATURE_Mediabay_IDE_switch,	/* MB IDE bus switch */
-	FEATURE_Mediabay_content,	/* MB content indicator enable */
-	FEATURE_Airport_reset,		/* Is it actually a reset ? */
-	FEATURE_last,
-};
-
-/* Note about the device parameter: Each device gives it's own entry. If NULL,
-   the feature function will just do nothing and return -EINVAL.
-   The feature management will walk up the device tree until in reaches a recognized
-   chip for which features can be changed and it will then apply the necessary
-   features to that chip. If it's not found, -ENODEV is returned.
-   Note also that feature_test/set/clear are interrupt-safe provided that they are
-   called _after_ feature_init() is completed.
- */
-
-/* Test whether a particular feature is enabled. May return -ENODEV*/
-extern int	feature_test(struct device_node* device, enum system_feature f);
-
-/* Set a particular feature. Returns 0 or -ENODEV */
-extern int	feature_set(struct device_node* device, enum system_feature f);
-
-/* Clear a particular feature */
-extern int	feature_clear(struct device_node* device, enum system_feature f);
-
-/* Initialize feature stuff */
-extern void	feature_init(void);
-
-
-/*
- * Additional functions related to Core99 machines. We should extend the
- * feature mecanism to make those fit into it. For now, they are still
- * separate functions.
- */
-extern void	feature_set_gmac_power(struct device_node* device, int power);
-
-	/* use constants in KeyLargo.h for the reset parameter */
-extern void	feature_gmac_phy_reset(struct device_node* device);
-
-extern void	feature_set_usb_power(struct device_node* device, int power);
-
-extern void 	feature_set_firewire_power(struct device_node* device, int power);
-extern void 	feature_set_firewire_cable_power(struct device_node* device, int power);
-
-extern void	feature_set_modem_power(struct device_node* device, int power);
-
-extern void	feature_set_airport_power(struct device_node* device, int power);
-
-extern void	feature_core99_kick_cpu(int cpu_nr);
-
-/*
- * Sleep related functions. At term, they should be high-priority notifiers,
- * but this would require some changes to the current sleep scheme that won't
- * be done in 2.4.
- */
-extern void	feature_prepare_for_sleep(void);
-extern void	feature_wake_up(void);
-extern int	feature_can_sleep(void);
-
-#endif /* __ASM_PPC_FEATURE_H */
-#endif /* __KERNEL__ */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-ppc/heathrow.h linux/include/asm-ppc/heathrow.h
--- linux.orig/include/asm-ppc/heathrow.h	Mon Jan  7 23:41:09 2002
+++ linux/include/asm-ppc/heathrow.h	Wed Dec 26 16:36:12 2001
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.heathrow.h 1.7 05/17/01 18:14:24 cort
+ * BK Id: SCCS/s.heathrow.h 1.10 12/01/01 20:09:11 benh
  */
 /*
  * heathrow.h: definitions for using the "Heathrow" I/O controller chip.
@@ -17,7 +17,9 @@
 #define HEATHROW_CONTRAST_CNTL		0x33
 
 /* offset from ohare base for feature control register */
-#define HEATHROW_FEATURE_REG		0x38
+#define HEATHROW_MBCR			0x34	/* Media bay control */
+#define HEATHROW_FCR			0x38	/* Feature control */
+#define HEATHROW_AUX_CNTL_REG		0x3c	/* Aux control */
 
 /*
  * Bits in feature control register.
@@ -30,6 +32,7 @@
 #define HRW_BAY_FLOPPY_ENABLE	0x00000010
 #define HRW_IDE0_ENABLE		0x00000020
 #define HRW_IDE0_RESET_N	0x00000040
+#define HRW_BAY_DEV_MASK	0x0000001c
 #define HRW_BAY_RESET_N		0x00000080
 #define HRW_IOBUS_ENABLE	0x00000100	/* Internal IDE ? */
 #define HRW_SCC_ENABLE		0x00000200
@@ -45,7 +48,7 @@
 #define HRW_SWIM_CLONE_FLOPPY	0x00080000	/* ??? (0) */
 #define HRW_AUD_RUN22		0x00100000	/* ??? (1) */
 #define HRW_SCSI_LINK_MODE	0x00200000	/* Read ??? (1) */
-#define HRW_ARB_BYPASS		0x00400000	/* ??? (0 on main, 1 on gatwick) */
+#define HRW_ARB_BYPASS		0x00400000	/* Disable internal PCI arbitrer */
 #define HRW_IDE1_RESET_N	0x00800000	/* Media bay */
 #define HRW_SLOW_SCC_PCLK	0x01000000	/* ??? (0) */
 #define HRW_MODEM_POWER_N	0x02000000	/* Used by internal modem on wallstreet */
@@ -60,3 +63,7 @@
 /* Those seem to be different on paddington */
 #define PADD_MODEM_POWER_N	0x00000001	/* modem power on paddington */
 #define PADD_RESET_SCC		0x02000000	/* check this please */
+
+/* Looks like Heathrow has some sort of GPIOs as well... */
+#define HRW_GPIO_MODEM_RESET	0x6d
+
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-ppc/keylargo.h linux/include/asm-ppc/keylargo.h
--- linux.orig/include/asm-ppc/keylargo.h	Mon Jan  7 23:41:09 2002
+++ linux/include/asm-ppc/keylargo.h	Wed Dec 26 16:36:12 2001
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.keylargo.h 1.13 08/19/01 22:23:04 paulus
+ * BK Id: SCCS/s.keylargo.h 1.15 12/01/01 20:09:11 benh
  */
 /*
  * keylargo.h: definitions for using the "KeyLargo" I/O controller chip.
@@ -29,12 +29,15 @@
 #define KEYLARGO_GPIO_CNT		17
 #define KEYLARGO_GPIO_OUTPUT_ENABLE	0x04
 #define KEYLARGO_GPIO_OUTOUT_DATA	0x01
+#define KEYLARGO_GPIO_INPUT_DATA	0x02
 
 /* Specific GPIO regs */
 
-#define KL_GPIO_MODEM_RESET		(KEYLARGO_GPIO_0+0x03) /* Pangea */
+#define KL_GPIO_MODEM_RESET		(KEYLARGO_GPIO_0+0x03)
 #define KL_GPIO_MODEM_POWER		(KEYLARGO_GPIO_0+0x02) /* Pangea */
 
+#define KL_GPIO_SOUND_POWER		(KEYLARGO_GPIO_0+0x05)
+
 /* Hrm... this one is only to be used on Pismo. It seeem to also
  * control the timebase enable on other machines. Still to be
  * experimented... --BenH.
@@ -54,7 +57,9 @@
 #define KL_GPIO_RESET_CPU3		(KEYLARGO_GPIO_EXTINT_0+0x10)
 
 #define KL_GPIO_PMU_MESSAGE_IRQ		(KEYLARGO_GPIO_EXTINT_0+0x09)
-#define KL_GPIO_PMU_MESSAGE_BIT		0x02
+#define KL_GPIO_PMU_MESSAGE_BIT		KEYLARGO_GPIO_INPUT_DATA
+
+#define KL_GPIO_MEDIABAY_IRQ		(KEYLARGO_GPIO_EXTINT_0+0x0e)
 
 #define KL_GPIO_AIRPORT_0		(KEYLARGO_GPIO_EXTINT_0+0x0a)
 #define KL_GPIO_AIRPORT_1		(KEYLARGO_GPIO_EXTINT_0+0x0d)
@@ -65,11 +70,19 @@
 /*
  * Bits in feature control register
  */
-#define KL_MBCR_MB0_DEV_ENABLE		0x00001000
+#define KL_MBCR_MB0_PCI_ENABLE		0x00000800	/* exist ? */
+#define KL_MBCR_MB0_IDE_ENABLE		0x00001000
+#define KL_MBCR_MB0_FLOPPY_ENABLE	0x00002000	/* exist ? */
+#define KL_MBCR_MB0_SOUND_ENABLE	0x00004000	/* hrm... */
+#define KL_MBCR_MB0_DEV_MASK		0x00007800
 #define KL_MBCR_MB0_DEV_POWER		0x00000400
 #define KL_MBCR_MB0_DEV_RESET		0x00000200
 #define KL_MBCR_MB0_ENABLE		0x00000100
-#define KL_MBCR_MB1_DEV_ENABLE		0x10000000
+#define KL_MBCR_MB1_PCI_ENABLE		0x08000000	/* exist ? */
+#define KL_MBCR_MB1_IDE_ENABLE		0x10000000
+#define KL_MBCR_MB1_FLOPPY_ENABLE	0x20000000	/* exist ? */
+#define KL_MBCR_MB1_SOUND_ENABLE	0x40000000	/* hrm... */
+#define KL_MBCR_MB1_DEV_MASK		0x78000000
 #define KL_MBCR_MB1_DEV_POWER		0x04000000
 #define KL_MBCR_MB1_DEV_RESET		0x02000000
 #define KL_MBCR_MB1_ENABLE		0x01000000
@@ -81,27 +94,23 @@
 #define KL0_SCCA_ENABLE			0x00000010
 #define KL0_SCCB_ENABLE			0x00000020
 #define KL0_SCC_CELL_ENABLE		0x00000040
+#define KL0_IRDA_HIGH_BAND		0x00000100
+#define KL0_IRDA_SOURCE2_SEL		0x00000200
+#define KL0_IRDA_SOURCE1_SEL		0x00000400
+#define KL0_IRDA_RESET			0x00000800
+#define KL0_IRDA_DEFAULT1		0x00001000
+#define KL0_IRDA_DEFAULT0		0x00002000
+#define KL0_IRDA_FAST_CONNECT		0x00004000
+#define KL0_IRDA_ENABLE			0x00008000
+#define KL0_IRDA_CLK32_ENABLE		0x00010000
+#define KL0_IRDA_CLK19_ENABLE		0x00020000
 #define KL0_USB0_PAD_SUSPEND0		0x00040000
 #define KL0_USB0_PAD_SUSPEND1		0x00080000
 #define KL0_USB0_CELL_ENABLE		0x00100000
 #define KL0_USB1_PAD_SUSPEND0		0x00400000
 #define KL0_USB1_PAD_SUSPEND1		0x00800000
 #define KL0_USB1_CELL_ENABLE		0x01000000
-/* KL id 0x22 only */
 #define KL0_USB_REF_SUSPEND		0x10000000
-#define KL0_IRDA_ENABLE			0x00008000
-#define KL0_IRDA_CLK32_ENABLE		0x00010000
-#define KL0_IRDA_CLK19_ENABLE		0x00020000
-/* KL id 0x25 (pangea) only */
-#define KL0_USB1_PAD_SUSPEND_SEL	0x00020000
-#define KL0_USB1_REF_SUSPEND		0x00010000
-#define KL0_USB1_REF_SUSPEND_SEL	0x00008000
-#define KL0_USB1_PMI			0x00004000
-#define KL0_USB0_PAD_SUSPEND_SEL	0x00002000
-#define KL0_USB0_REF_SUSPEND		0x00001000
-#define KL0_USB0_REF_SUSPEND_SEL	0x00000800
-#define KL0_USB0_PMI			0x00000400
-
 
 #define KL0_SERIAL_ENABLE		(KL0_SCC_B_INTF_ENABLE | \
 					KL0_SCC_SLOWPCLK | \
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-ppc/machdep.h linux/include/asm-ppc/machdep.h
--- linux.orig/include/asm-ppc/machdep.h	Mon Jan  7 23:41:09 2002
+++ linux/include/asm-ppc/machdep.h	Wed Dec 26 16:36:12 2001
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.machdep.h 1.25 11/13/01 21:26:07 paulus
+ * BK Id: SCCS/s.machdep.h 1.27 12/01/01 20:09:11 benh
  */
 #ifdef __KERNEL__
 #ifndef _PPC_MACHDEP_H
@@ -16,6 +16,11 @@
 struct pci_dev;
 struct seq_file;
 
+/* We export this macro for external modules like Alsa to know if
+ * ppc_md.feature_call is implemented or not
+ */
+#define CONFIG_PPC_HAS_FEATURE_CALLS
+
 struct machdep_calls {
 	void		(*setup_arch)(void);
 	/* Optional, may be NULL. */
@@ -94,6 +99,12 @@
 
 	/* this is for modules, since _machine can be a define -- Cort */
 	int ppc_machine;
+
+	/* Motherboard/chipset features. This is a kind of general purpose
+	 * hook used to control some machine specific features (like reset
+	 * lines, chip power control, etc...).
+	 */
+	int (*feature_call)(unsigned int feature, ...);
 
 #ifdef CONFIG_SMP
 	/* functions for dealing with other cpus */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-ppc/mediabay.h linux/include/asm-ppc/mediabay.h
--- linux.orig/include/asm-ppc/mediabay.h	Mon Jan  7 23:41:09 2002
+++ linux/include/asm-ppc/mediabay.h	Wed Dec 26 16:36:12 2001
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.mediabay.h 1.5 05/17/01 18:14:25 cort
+ * BK Id: SCCS/s.mediabay.h 1.8 12/01/01 20:09:11 benh
  */
 /*
  * mediabay.h: definitions for using the media bay
@@ -12,10 +12,13 @@
 
 #ifdef __KERNEL__
 
-#define MB_FD	0		/* media bay contains floppy drive */
-#define MB_FD1	1		/* 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 */
+#define MB_FD		0	/* media bay contains floppy drive (automatic eject ?) */
+#define MB_FD1		1	/* media bay contains floppy drive (manual eject ?) */
+#define MB_SOUND	2	/* sound device ? */
+#define MB_CD		3	/* media bay contains ATA drive such as CD or ZIP */
+#define MB_PCI		5	/* media bay contains a PCI device */
+#define MB_POWER	6	/* media bay contains a Power device (???) */
+#define MB_NO		7	/* media bay contains nothing */
 
 void media_bay_init(void);
 int check_media_bay(struct device_node *which_bay, int what);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-ppc/ohare.h linux/include/asm-ppc/ohare.h
--- linux.orig/include/asm-ppc/ohare.h	Mon Jan  7 23:41:09 2002
+++ linux/include/asm-ppc/ohare.h	Wed Dec 26 16:36:12 2001
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.ohare.h 1.5 05/17/01 18:14:25 cort
+ * BK Id: SCCS/s.ohare.h 1.8 12/01/01 20:09:11 benh
  */
 /*
  * ohare.h: definitions for using the "O'Hare" I/O controller chip.
@@ -11,7 +11,8 @@
  */
 
 /* offset from ohare base for feature control register */
-#define OHARE_FEATURE_REG	0x38
+#define OHARE_MBCR	0x34
+#define OHARE_FCR	0x38
 
 /*
  * Bits in feature control register.
@@ -25,6 +26,7 @@
 #define OH_BAY_FLOPPY_ENABLE	0x10
 #define OH_IDE0_ENABLE		0x20
 #define OH_IDE0_RESET_N		0x40	/* a guess */
+#define OH_BAY_DEV_MASK		0x1c
 #define OH_BAY_RESET_N		0x80
 #define OH_IOBUS_ENABLE		0x100	/* IOBUS seems to be IDE */
 #define OH_SCC_ENABLE		0x200
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-ppc/pci.h linux/include/asm-ppc/pci.h
--- linux.orig/include/asm-ppc/pci.h	Mon Jan  7 23:41:09 2002
+++ linux/include/asm-ppc/pci.h	Fri Dec 21 18:25:01 2001
@@ -102,6 +102,13 @@
 	/* nothing to do */
 }
 
+/* pci_unmap_{page,single} is a nop so... */
+#define DECLARE_PCI_UNMAP_ADDR(ADDR_NAME)
+#define DECLARE_PCI_UNMAP_LEN(LEN_NAME)
+#define PCI_UNMAP_ADDR(PTR, ADDR_NAME)		(0)
+#define PCI_UNMAP_ADDR_SET(PTR, ADDR_NAME, VAL)	do { } while (0)
+#define PCI_UNMAP_LEN(PTR, LEN_NAME)		(0)
+#define PCI_UNMAP_LEN_SET(PTR, LEN_NAME, VAL)	do { } while (0)
 
 /*
  * pci_{map,unmap}_single_page maps a kernel page to a dma_addr_t. identical
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-ppc/pmac_feature.h linux/include/asm-ppc/pmac_feature.h
--- linux.orig/include/asm-ppc/pmac_feature.h	Thu Jan  1 00:00:00 1970
+++ linux/include/asm-ppc/pmac_feature.h	Wed Dec 26 16:36:12 2001
@@ -0,0 +1,251 @@
+/*
+ * Definition of platform feature hooks for PowerMacs
+ * 
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 1998 Paul Mackerras &
+ *                    Ben. Herrenschmidt.
+ *                    
+ *                    
+ * Note: I removed media-bay details from the feature stuff, I beleive it's
+ *       not worth it, the media-bay driver can directly use the mac-io
+ *       ASIC registers.
+ *       
+ * Implementation note: Currently, none of these functions will block.
+ * However, they may internally protect themselves with a spinlock
+ * for way too long. Be prepared for at least some of these to block
+ * in the future.
+ * 
+ * Unless specifically defined, the result code is assumed to be an
+ * error when negative, 0 is the default success result. Some functions
+ * may return additional positive result values.
+ * 
+ * To keep implementation simple, all feature calls are assumed to have
+ * the prototype parameters (struct device_node* node, int value).
+ * When either is not used, pass 0.
+ */
+
+#ifdef __KERNEL__
+#ifndef __PPC_ASM_PMAC_FEATURE_H
+#define __PPC_ASM_PMAC_FEATURE_H
+
+/*
+ * Known Mac motherboard models
+ * 
+ * Please, report any error here to benh@kernel.crashing.org, thanks !
+ */
+
+/* PowerSurge are the first generation of PCI Pmacs. This include
+ * all of the Grand-Central based machines
+ */
+#define PMAC_TYPE_PSURGE		0x10	/* PowerSurge */
+
+/* Here is the infamous serie of OHare based machines
+ */
+#define PMAC_TYPE_COMET			0x20	/* Beleived to be PowerBook 2400 */
+#define PMAC_TYPE_HOOPER		0x21	/* Beleived to be PowerBook 3400 */
+#define PMAC_TYPE_KANGA			0x22	/* PowerBook 3500 (first G3) */
+#define PMAC_TYPE_ALCHEMY		0x23	/* Alchemy motherboard base */
+#define PMAC_TYPE_GAZELLE		0x24	/* Spartacus, some 5xxx/6xxx */
+#define PMAC_TYPE_UNKNOWN_OHARE		0x2f	/* Unknown, but OHare based */
+
+/* Here are the Heathrow based machines
+ * FIXME: Differenciate wallstreet,mainstreet,wallstreetII
+ */
+#define PMAC_TYPE_GOSSAMER		0x30	/* Gossamer motherboard */
+#define PMAC_TYPE_SILK			0x31	/* Desktop PowerMac G3 */
+#define PMAC_TYPE_WALLSTREET		0x32	/* Wallstreet/Mainstreet PowerBook*/
+#define PMAC_TYPE_UNKNOWN_HEATHROW	0x3f	/* Unknown but heathrow based */
+
+/* Here are newworld machines based on Paddington (heathrow derivative)
+ */
+#define PMAC_TYPE_101_PBOOK		0x40	/* 101 PowerBook (aka Lombard) */
+#define PMAC_TYPE_ORIG_IMAC		0x41	/* First generation iMac */
+#define PMAC_TYPE_YOSEMITE		0x42	/* B&W G3 */
+#define PMAC_TYPE_YIKES			0x43	/* Yikes G4 (PCI graphics) */
+#define PMAC_TYPE_UNKNOWN_PADDINGTON	0x4f	/* Unknown but paddington based */
+
+/* Core99 machines based on UniNorth 1.0 and 1.5
+ * 
+ * Note: A single entry here may cover several actual models according
+ * to the device-tree. (Sawtooth is most tower G4s, FW_IMAC is most
+ * FireWire based iMacs, etc...). Those machines are too similar to be
+ * distinguished here, when they need to be differencied, use the
+ * device-tree "model" or "compatible" property.
+ */
+#define PMAC_TYPE_ORIG_IBOOK		0x40	/* First iBook model (no firewire) */
+#define PMAC_TYPE_SAWTOOTH		0x41	/* Desktop G4s */
+#define PMAC_TYPE_FW_IMAC		0x42	/* FireWire iMacs (except Pangea based) */
+#define PMAC_TYPE_FW_IBOOK		0x43	/* FireWire iBooks (except iBook2) */
+#define PMAC_TYPE_CUBE			0x44	/* Cube PowerMac */
+#define PMAC_TYPE_QUICKSILVER		0x45	/* QuickSilver G4s */
+#define PMAC_TYPE_PISMO			0x46	/* Pismo PowerBook */
+#define PMAC_TYPE_TITANIUM		0x47	/* Titanium PowerBook */
+#define PMAC_TYPE_TITANIUM2		0x48	/* Titanium II PowerBook */
+#define PMAC_TYPE_UNKNOWN_CORE99	0x5f
+
+/* MacRISC2 machines based on the Pangea chipset
+ */
+#define PMAC_TYPE_PANGEA_IMAC		0x100	/* Flower Power iMac */
+#define PMAC_TYPE_IBOOK2		0x101	/* iBook2 (polycarbonate) */
+#define PMAC_TYPE_UNKNOWN_PANGEA	0x10f
+
+/*
+ * Motherboard flags
+ */
+
+#define PMAC_MB_CAN_SLEEP		0x00000001
+
+/*
+ * Feature calls supported on pmac
+ * 	
+ */
+
+/*
+ * Use this inline wrapper
+ */
+struct device_node;
+
+static inline int pmac_call_feature(int selector, struct device_node* node,
+					int param, int value)
+{
+	if (!ppc_md.feature_call)
+		return -ENODEV;
+	return ppc_md.feature_call(selector, node, param, value);
+}
+
+/* PMAC_FTR_SERIAL_ENABLE	(struct device_node* node, int param, int value)
+ * enable/disable an SCC side. Pass the node corresponding to the
+ * channel side as a parameter.
+ * param is the type of port
+ * if param is ored with PMAC_SCC_FLAG_XMON, then the SCC is locked enabled
+ * for use by xmon.
+ */
+#define PMAC_FTR_SCC_ENABLE		PMAC_FTR_DEF(0)
+	#define PMAC_SCC_ASYNC		0
+	#define PMAC_SCC_IRDA		1
+	#define PMAC_SCC_I2S1		2
+	#define PMAC_SCC_FLAG_XMON	0x00001000
+
+/* PMAC_FTR_MODEM_ENABLE	(struct device_node* node, 0, int value)
+ * enable/disable the internal modem.
+ */
+#define PMAC_FTR_MODEM_ENABLE		PMAC_FTR_DEF(1)
+
+/* PMAC_FTR_SWIM3_ENABLE	(struct device_node* node, 0,int value)
+ * enable/disable the swim3 (floppy) cell of a mac-io ASIC
+ */
+#define PMAC_FTR_SWIM3_ENABLE		PMAC_FTR_DEF(2)
+
+/* PMAC_FTR_MESH_ENABLE		(struct device_node* node, 0, int value)
+ * enable/disable the mesh (scsi) cell of a mac-io ASIC
+ */
+#define PMAC_FTR_MESH_ENABLE		PMAC_FTR_DEF(3)
+
+/* PMAC_FTR_IDE_ENABLE		(struct device_node* node, int busID, int value)
+ * enable/disable an IDE port of a mac-io ASIC
+ * pass the busID parameter
+ */
+#define PMAC_FTR_IDE_ENABLE		PMAC_FTR_DEF(4)
+
+/* PMAC_FTR_IDE_RESET		(struct device_node* node, int busID, int value)
+ * assert(1)/release(0) an IDE reset line (mac-io IDE only)
+ */
+#define PMAC_FTR_IDE_RESET		PMAC_FTR_DEF(5)
+
+/* PMAC_FTR_BMAC_ENABLE		(struct device_node* node, 0, int value)
+ * enable/disable the bmac (ethernet) cell of a mac-io ASIC, also drive
+ * it's reset line
+ */
+#define PMAC_FTR_BMAC_ENABLE		PMAC_FTR_DEF(6)
+
+/* PMAC_FTR_GMAC_ENABLE		(struct device_node* node, 0, int value)
+ * enable/disable the gmac (ethernet) cell of an uninorth ASIC. This
+ * control the cell's clock.
+ */
+#define PMAC_FTR_GMAC_ENABLE		PMAC_FTR_DEF(7)
+
+/* PMAC_FTR_GMAC_PHY_RESET	(struct device_node* node, 0, 0)
+ * Perform a HW reset of the PHY connected to a gmac controller.
+ * Pass the gmac device node, not the PHY node.
+ */
+#define PMAC_FTR_GMAC_PHY_RESET		PMAC_FTR_DEF(8)
+
+/* PMAC_FTR_SOUND_CHIP_ENABLE	(struct device_node* node, 0, int value)
+ * enable/disable the sound chip, whatever it is and provided it can
+ * acually be controlled
+ */
+#define PMAC_FTR_SOUND_CHIP_ENABLE	PMAC_FTR_DEF(9)
+
+/* -- add various tweaks related to sound routing -- */
+ 
+/* PMAC_FTR_AIRPORT_ENABLE	(struct device_node* node, 0, int value)
+ * enable/disable the airport card
+ */
+#define PMAC_FTR_AIRPORT_ENABLE		PMAC_FTR_DEF(10)
+
+/* PMAC_FTR_RESET_CPU		(NULL, int cpu_nr, 0)
+ * toggle the reset line of a CPU on an uninorth-based SMP machine
+ */
+#define PMAC_FTR_RESET_CPU		PMAC_FTR_DEF(11)
+
+/* PMAC_FTR_USB_ENABLE		(struct device_node* node, 0, int value)
+ * enable/disable an USB cell, along with the power of the USB "pad"
+ * on keylargo based machines
+ */
+#define PMAC_FTR_USB_ENABLE		PMAC_FTR_DEF(12)
+
+/* PMAC_FTR_1394_ENABLE		(struct device_node* node, 0, int value)
+ * enable/disable the firewire cell of an uninorth ASIC.
+ */
+#define PMAC_FTR_1394_ENABLE		PMAC_FTR_DEF(13)
+
+/* PMAC_FTR_1394_CABLE_POWER	(struct device_node* node, 0, int value)
+ * enable/disable the firewire cable power supply of the uninorth
+ * firewire cell
+ */
+#define PMAC_FTR_1394_CABLE_POWER	PMAC_FTR_DEF(14)
+
+/* PMAC_FTR_SLEEP_STATE		(struct device_node* node, 0, int value)
+ * set the sleep state of the motherboard.
+ * Pass -1 as value to query for sleep capability
+ */
+#define PMAC_FTR_SLEEP_STATE		PMAC_FTR_DEF(15)
+
+/* PMAC_FTR_GET_MB_INFO		(NULL, selector, 0)
+ * 
+ * returns some motherboard infos.
+ * selector: 0  - model id
+ *           1  - model flags (capabilities)
+ *           2  - model name (cast to const char *)
+ */
+#define PMAC_FTR_GET_MB_INFO		PMAC_FTR_DEF(16)
+#define   PMAC_MB_INFO_MODEL	0
+#define   PMAC_MB_INFO_FLAGS	1
+#define   PMAC_MB_INFO_NAME	2
+
+/* PMAC_FTR_READ_GPIO		(NULL, int index, 0)
+ *
+ * read a GPIO from a mac-io controller of type KeyLargo or Pangea.
+ * the value returned is a byte (positive), or a negative error code
+ */
+#define PMAC_FTR_READ_GPIO		PMAC_FTR_DEF(17)
+
+/* PMAC_FTR_WRITE_GPIO		(NULL, int index, int value)
+ *
+ * write a GPIO of a mac-io controller of type KeyLargo or Pangea.
+ */
+#define PMAC_FTR_WRITE_GPIO		PMAC_FTR_DEF(18)
+
+
+/* Don't use those directly, they are for the sake of pmac_setup.c */
+extern int pmac_do_feature_call(unsigned int selector, ...);
+extern void pmac_feature_init(void);
+extern void pmac_feature_late_init(void);
+
+#define PMAC_FTR_DEF(x) ((_MACH_Pmac << 16) | (x))
+
+#endif /* __PPC_ASM_PMAC_FEATURE_H */
+#endif /* __KERNEL__ */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-ppc/processor.h linux/include/asm-ppc/processor.h
--- linux.orig/include/asm-ppc/processor.h	Mon Jan  7 23:41:09 2002
+++ linux/include/asm-ppc/processor.h	Wed Dec 26 16:36:12 2001
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.processor.h 1.31 10/05/01 16:26:22 paulus
+ * BK Id: SCCS/s.processor.h 1.33 12/01/01 20:09:11 benh
  */
 #ifdef __KERNEL__
 #ifndef __ASM_PPC_PROCESSOR_H
@@ -209,8 +209,11 @@
 #define	  HID0_DFCA	(1<<6)		/* Data Cache Flush Assist */
 #define   HID0_BTIC	(1<<5)		/* Branch Target Instruction Cache Enable */
 #define   HID0_ABE	(1<<3)		/* Address Broadcast Enable */
+#define   HID0_FOLD	(1<<3)		/* Branch Folding enable - 7450 */
 #define	  HID0_BHTE	(1<<2)		/* Branch History Table Enable */
 #define	  HID0_BTCD	(1<<1)		/* Branch target cache disable */
+#define	  HID0_NOPDST	(1<<1)		/* No-op dst, dstt, etc. instr. */
+#define	  HID0_NOPTI	(1<<0)		/* No-op dcbt and dcbst instr. */
 #define	SPRN_HID1	0x3F1	/* Hardware Implementation Register 1 */
 #define	SPRN_IABR	0x3F2	/* Instruction Address Breakpoint Register */
 #define	SPRN_IAC1	0x3F4	/* Instruction Address Compare 1 */
@@ -261,6 +264,14 @@
 #define L2CR_L2DF		0x00004000	/* L2 differential clock */
 #define L2CR_L2BYP		0x00002000	/* L2 DLL bypass */
 #define L2CR_L2IP		0x00000001	/* L2 GI in progress */
+#define SPRN_L2CR2      0x3f8
+#define	SPRN_L3CR	0x3FA	/* Level 3 Cache Control Regsiter (7450) */
+#define L3CR_L3E		0x80000000	/* L3 enable */
+#define SPRN_MSSCR0	0x3f6	/* Memory Subsystem Control Register 0 */
+#define SPRN_MSSSR0	0x3f7	/* Memory Subsystem Status Register 1 */
+#define SPRN_ICTRL	0x3f3	/* Instruction Cache & Interrupt control reg */
+#define SPRN_LDSTCR	0x3f8	/* Load/Store control register */
+#define SPRN_LDSTDB	0x3f4	/* */
 #define	SPRN_LR		0x008	/* Link Register */
 #define	SPRN_MMCR0	0x3B8	/* Monitor Mode Control Register 0 */
 #define	SPRN_MMCR1	0x3BC	/* Monitor Mode Control Register 1 */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-ppc/prom.h linux/include/asm-ppc/prom.h
--- linux.orig/include/asm-ppc/prom.h	Mon Jan  7 23:41:09 2002
+++ linux/include/asm-ppc/prom.h	Wed Dec 26 16:36:12 2001
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.prom.h 1.19 08/17/01 15:23:17 paulus
+ * BK Id: SCCS/s.prom.h 1.21 12/01/01 20:09:11 benh
  */
 /*
  * Definitions for talking to the Open Firmware PROM on
@@ -85,6 +85,10 @@
 extern void prom_get_irq_senses(unsigned char *, int, int);
 extern int prom_n_addr_cells(struct device_node* np);
 extern int prom_n_size_cells(struct device_node* np);
+
+extern struct resource*
+request_OF_resource(struct device_node* node, int index, const char* name_postfix);
+extern int release_OF_resource(struct device_node* node, int index);
 
 extern void print_properties(struct device_node *node);
 extern int call_rtas(const char *service, int nargs, int nret,
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-s390/idals.h linux/include/asm-s390/idals.h
--- linux.orig/include/asm-s390/idals.h	Mon Jan  7 23:41:09 2002
+++ linux/include/asm-s390/idals.h	Fri Dec 21 16:25:31 2001
@@ -10,7 +10,7 @@
 #include <linux/config.h>
 #include <asm/irq.h>
 
-#define IDA_SIZE_LOG 12 /* 11 for 2k , 12 for 4k */
+#define IDA_SIZE_LOG 11 /* 11 for 2k , 12 for 4k */
 #define IDA_BLOCK_SIZE (1L<<IDA_SIZE_LOG)
 
 static inline addr_t *
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-s390/lowcore.h linux/include/asm-s390/lowcore.h
--- linux.orig/include/asm-s390/lowcore.h	Mon Jan  7 23:41:09 2002
+++ linux/include/asm-s390/lowcore.h	Fri Dec 21 16:25:31 2001
@@ -45,6 +45,9 @@
 #define __LC_CPUID                      0xC60
 #define __LC_CPUADDR                    0xC68
 #define __LC_IPLDEV                     0xC7C
+
+#define __LC_JIFFY_TIMER		0xC80
+
 #define __LC_PANIC_MAGIC                0xE00
 
 #define __LC_PFAULT_INTPARM             0x080
@@ -161,7 +164,7 @@
 	/* entry.S sensitive area end */
 
         /* SMP info area: defined by DJB */
-        __u64        jiffy_timer_cc;           /* 0xc80 */
+        __u64        jiffy_timer;              /* 0xc80 */
 	atomic_t     ext_call_fast;            /* 0xc88 */
         __u8         pad11[0xe00-0xc8c];       /* 0xc8c */
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-s390/processor.h linux/include/asm-s390/processor.h
--- linux.orig/include/asm-s390/processor.h	Mon Jan  7 23:41:09 2002
+++ linux/include/asm-s390/processor.h	Fri Dec 21 16:25:31 2001
@@ -74,8 +74,6 @@
 
 struct thread_struct
  {
-
-        struct pt_regs *regs;         /* the user registers can be found on*/
 	s390_fp_regs fp_regs;
         __u32   ar2;                   /* kernel access register 2         */
         __u32   ar4;                   /* kernel access register 4         */
@@ -95,8 +93,7 @@
 
 typedef struct thread_struct thread_struct;
 
-#define INIT_THREAD { (struct pt_regs *) 0,                       \
-                    { 0,{{0},{0},{0},{0},{0},{0},{0},{0},{0},{0}, \
+#define INIT_THREAD {{0,{{0},{0},{0},{0},{0},{0},{0},{0},{0},{0}, \
 			    {0},{0},{0},{0},{0},{0}}},            \
                      0, 0,                                        \
                     sizeof(init_stack) + (__u32) &init_stack,     \
@@ -126,16 +123,25 @@
 #define release_segments(mm)            do { } while (0)
 
 /*
- * Return saved PC of a blocked thread. used in kernel/sched
+ * Return saved PC of a blocked thread. used in kernel/sched.
+ * resume in entry.S does not create a new stack frame, it
+ * just stores the registers %r6-%r15 to the frame given by
+ * schedule. We want to return the address of the caller of
+ * schedule, so we have to walk the backchain one time to
+ * find the frame schedule() store its return address.
  */
 extern inline unsigned long thread_saved_pc(struct thread_struct *t)
 {
-        return (t->regs) ? ((unsigned long)t->regs->psw.addr) : 0;
+	unsigned long bc;
+	bc = *((unsigned long *) t->ksp);
+	return *((unsigned long *) (bc+56));
 }
 
 unsigned long get_wchan(struct task_struct *p);
-#define KSTK_EIP(tsk)   ((tsk)->thread.regs->psw.addr)
-#define KSTK_ESP(tsk)   ((tsk)->thread.ksp)
+#define __KSTK_PTREGS(tsk) \
+	((struct pt_regs *)((unsigned long) tsk+THREAD_SIZE) - 1)
+#define KSTK_EIP(tsk)	(__KSTK_PTREGS(tsk)->psw.addr)
+#define KSTK_ESP(tsk)	(__KSTK_PTREGS(tsk)->gprs[15])
 
 /* Allocation and freeing of basic task resources. */
 /*
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-s390/ptrace.h linux/include/asm-s390/ptrace.h
--- linux.orig/include/asm-s390/ptrace.h	Mon Jan  7 23:41:09 2002
+++ linux/include/asm-s390/ptrace.h	Fri Dec 21 16:25:31 2001
@@ -118,7 +118,7 @@
 {
         __u32   mask;
         __u32   addr;
-} psw_t __attribute__ ((aligned(8)));
+} __attribute__ ((aligned(8))) psw_t;
 
 #ifdef __KERNEL__
 #define FIX_PSW(addr) ((unsigned long)(addr)|0x80000000UL)
@@ -150,8 +150,8 @@
 #define FPC_VALID_MASK          0xF8F8FF03
 
 /*
- * The first entries in pt_regs, gdb_pt_regs and user_regs_struct
- * are common for all three structures. The s390_regs structure
+ * The first entries in pt_regs and user_regs_struct
+ * are common for the two structures. The s390_regs structure
  * covers the common parts. It simplifies copying the common part
  * between the three structures.
  */
@@ -178,30 +178,12 @@
 };
 
 /*
- * The gdb_pt_regs struct is used instead of the pt_regs structure
- * if kernel remote debugging is used.
- */
-#if CONFIG_REMOTE_DEBUG
-struct gdb_pt_regs
-{
-	psw_t psw;
-	__u32 gprs[NUM_GPRS];
-	__u32 acrs[NUM_ACRS];
-	__u32 orig_gpr2;
-	__u32 trap;
-	__u32 crs[16];
-	s390_fp_regs fp_regs;
-	__u32 old_ilc;
-};
-#endif
-
-/*
  * Now for the program event recording (trace) definitions.
  */
 typedef struct
 {
 	__u32 cr[3];
-} per_cr_words  __attribute__((packed));
+} per_cr_words;
 
 #define PER_EM_MASK 0xE8000000
 
@@ -223,14 +205,14 @@
 	unsigned                       : 21;
 	addr_t   starting_addr;
 	addr_t   ending_addr;
-} per_cr_bits  __attribute__((packed));
+} per_cr_bits;
 
 typedef struct
 {
 	__u16          perc_atmid;          /* 0x096 */
 	__u32          address;             /* 0x098 */
 	__u8           access_id;           /* 0x0a1 */
-} per_lowcore_words  __attribute__((packed));
+} per_lowcore_words;
 
 typedef struct
 {
@@ -249,14 +231,14 @@
 	addr_t   address;                     /* 0x098 */
 	unsigned                         : 4; /* 0x0a1 */
 	unsigned access_id               : 4;
-} per_lowcore_bits __attribute__((packed));
+} per_lowcore_bits;
 
 typedef struct
 {
 	union {
 		per_cr_words   words;
 		per_cr_bits    bits;
-	} control_regs  __attribute__((packed));
+	} control_regs;
 	/*
 	 * Use these flags instead of setting em_instruction_fetch
 	 * directly they are used so that single stepping can be
@@ -275,7 +257,7 @@
 		per_lowcore_words words;
 		per_lowcore_bits  bits;
 	} lowcore; 
-} per_struct __attribute__((packed));
+} per_struct;
 
 typedef struct
 {
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-s390/s390-gdbregs.h linux/include/asm-s390/s390-gdbregs.h
--- linux.orig/include/asm-s390/s390-gdbregs.h	Mon Jan  7 23:41:09 2002
+++ linux/include/asm-s390/s390-gdbregs.h	Thu Jan  1 00:00:00 1970
@@ -1,85 +0,0 @@
-/*
- *  include/asm-s390/s390-gdbregs.h
- *
- *  S390 version
- *    Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
- *    Author(s): Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com)
- *
- *  used both by the linux kernel for remote debugging & gdb 
- */
-
-#ifndef _S390_GDBREGS_H
-#define _S390_GDBREGS_H
-
-#ifdef __KERNEL__
-#include <asm/s390-regs-common.h>
-#else
-#include <s390/s390-regs-common.h>
-#endif
-#define S390_MAX_INSTR_SIZE 6
-#define NUM_REGS      (2+NUM_GPRS+NUM_ACRS+NUM_CRS+1+NUM_FPRS)
-#define FIRST_ACR     (2+NUM_GPRS)
-#define LAST_ACR      (FIRST_ACR+NUM_ACRS-1)
-#define FIRST_CR      (FIRST_ACR+NUM_ACRS)
-#define LAST_CR       (FIRST_CR+NUM_CRS-1)
-
-#define PSWM_REGNUM    0
-#define PC_REGNUM      1
-#define	GP0_REGNUM     2		    /* GPR register 0 */ 
-#define GP_LAST_REGNUM (GP0_REGNUM+NUM_GPRS-1)
-#define RETADDR_REGNUM (GP0_REGNUM+14)                   /* Usually return address */
-#define SP_REGNUM      (GP0_REGNUM+15)	    /* Contains address of top of stack */
-#define FP_REGNUM     SP_REGNUM /* needed in findvar.c still */
-#define FRAME_REGNUM  (GP0_REGNUM+11)
-#define FPC_REGNUM    (GP0_REGNUM+NUM_GPRS+NUM_ACRS+NUM_CRS)
-#define FP0_REGNUM    (FPC_REGNUM+1) /* FPR (Floating point) register 0 */
-#define FPLAST_REGNUM (FP0_REGNUM+NUM_FPRS-1)	/* Last floating point register */
-
-/* The top of this structure is as similar as possible to a pt_regs structure to */
-/* simplify code */
-typedef struct
-{
-	S390_REGS_COMMON
-	__u32         crs[NUM_CRS];
-	s390_fp_regs  fp_regs;
-} s390_gdb_regs __attribute__((packed));
-
-#define REGISTER_NAMES                                           \
-{                                                                \
-"pswm","pswa",                                                   \
-"gpr0","gpr1","gpr2","gpr3","gpr4","gpr5","gpr6","gpr7",         \
-"gpr8","gpr9","gpr10","gpr11","gpr12","gpr13","gpr14","gpr15",   \
-"acr0","acr1","acr2","acr3","acr4","acr5","acr6","acr7",         \
-"acr8","acr9","acr10","acr11","acr12","acr13","acr14","acr15",   \
-"cr0","cr1","cr2","cr3","cr4","cr5","cr6","cr7",                 \
-"cr8","cr9","cr10","cr11","cr12","cr13","cr14","cr15",           \
-"fpc",                                                           \
-"fpr0","fpr1","fpr2","fpr3","fpr4","fpr5","fpr6","fpr7",         \
-"fpr8","fpr9","fpr10","fpr11","fpr12","fpr13","fpr14","fpr15"    \
-}
-
-/* Index within `registers' of the first byte of the space for
-   register N.  */
-
-#define FP0_OFFSET ((PSW_MASK_SIZE+PSW_ADDR_SIZE)+ \
-(GPR_SIZE*NUM_GPRS)+(ACR_SIZE+NUM_ACRS)+ \
-(CR_SIZE*NUM_CRS)+(FPC_SIZE+FPC_PAD_SIZE))
-
-#define REGISTER_BYTES    \
-((FP0_OFFSET)+(FPR_SIZE*NUM_FPRS))
-
-#define REGISTER_BYTE(N)  ((N) < FP0_REGNUM ? (N)*4:(FP0_OFFSET+((N)-FP0_REGNUM)*FPR_SIZE))
-
-#endif
-
-
-
-
-
-
-
-
-
-
-
-
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-s390/scatterlist.h linux/include/asm-s390/scatterlist.h
--- linux.orig/include/asm-s390/scatterlist.h	Mon Jan  7 23:41:09 2002
+++ linux/include/asm-s390/scatterlist.h	Fri Dec 21 16:25:31 2001
@@ -1,8 +1,16 @@
-#ifndef _ASMS390X_SCATTERLIST_H
-#define _ASMS390X_SCATTERLIST_H
+#ifndef _ASMS390_SCATTERLIST_H
+#define _ASMS390_SCATTERLIST_H
 
 struct scatterlist {
-    char *  address;    /* Location data is to be transferred to */
+    /* This will disappear in 2.5.x */
+    char *address;
+
+    /* These two are only valid if ADDRESS member of this
+     * struct is NULL.
+     */
+    struct page *page;
+    unsigned int offset;
+
     unsigned int length;
 };
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-s390/setup.h linux/include/asm-s390/setup.h
--- linux.orig/include/asm-s390/setup.h	Mon Jan  7 23:41:09 2002
+++ linux/include/asm-s390/setup.h	Fri Dec 21 16:25:31 2001
@@ -13,7 +13,7 @@
 #define RAMDISK_ORIGIN		0x800000
 #define RAMDISK_SIZE		0x800000
 
-#ifndef __ASSEMBLER__
+#ifndef __ASSEMBLY__
 
 #define IPL_DEVICE        (*(unsigned long *)  (0x10404))
 #define INITRD_START      (*(unsigned long *)  (0x1040C))
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-s390/siginfo.h linux/include/asm-s390/siginfo.h
--- linux.orig/include/asm-s390/siginfo.h	Mon Jan  7 23:41:09 2002
+++ linux/include/asm-s390/siginfo.h	Fri Dec 21 16:25:31 2001
@@ -111,7 +111,7 @@
 #define SI_USER		0	/* sent by kill, sigsend, raise */
 #define SI_KERNEL	0x80	/* sent by the kernel from somewhere */
 #define SI_QUEUE	-1	/* sent by sigqueue */
-#define SI_TIMER	-2	/* sent by timer expiration */
+#define SI_TIMER __SI_CODE(__SI_TIMER,-2) /* sent by timer expiration */
 #define SI_MESGQ	-3	/* sent by real time mesq state change */
 #define SI_ASYNCIO	-4	/* sent by AIO completion */
 #define SI_SIGIO	-5	/* sent by queued SIGIO */
@@ -122,71 +122,71 @@
 /*
  * SIGILL si_codes
  */
-#define ILL_ILLOPC	1	/* illegal opcode */
-#define ILL_ILLOPN	2	/* illegal operand */
-#define ILL_ILLADR	3	/* illegal addressing mode */
-#define ILL_ILLTRP	4	/* illegal trap */
-#define ILL_PRVOPC	5	/* privileged opcode */
-#define ILL_PRVREG	6	/* privileged register */
-#define ILL_COPROC	7	/* coprocessor error */
-#define ILL_BADSTK	8	/* internal stack error */
+#define ILL_ILLOPC	(__SI_FAULT|1)	/* illegal opcode */
+#define ILL_ILLOPN	(__SI_FAULT|2)	/* illegal operand */
+#define ILL_ILLADR	(__SI_FAULT|3)	/* illegal addressing mode */
+#define ILL_ILLTRP	(__SI_FAULT|4)	/* illegal trap */
+#define ILL_PRVOPC	(__SI_FAULT|5)	/* privileged opcode */
+#define ILL_PRVREG	(__SI_FAULT|6)	/* privileged register */
+#define ILL_COPROC	(__SI_FAULT|7)	/* coprocessor error */
+#define ILL_BADSTK	(__SI_FAULT|8)	/* internal stack error */
 #define NSIGILL		8
 
 /*
  * SIGFPE si_codes
  */
-#define FPE_INTDIV	1	/* integer divide by zero */
-#define FPE_INTOVF	2	/* integer overflow */
-#define FPE_FLTDIV	3	/* floating point divide by zero */
-#define FPE_FLTOVF	4	/* floating point overflow */
-#define FPE_FLTUND	5	/* floating point underflow */
-#define FPE_FLTRES	6	/* floating point inexact result */
-#define FPE_FLTINV	7	/* floating point invalid operation */
-#define FPE_FLTSUB	8	/* subscript out of range */
+#define FPE_INTDIV	(__SI_FAULT|1)	/* integer divide by zero */
+#define FPE_INTOVF	(__SI_FAULT|2)	/* integer overflow */
+#define FPE_FLTDIV	(__SI_FAULT|3)	/* floating point divide by zero */
+#define FPE_FLTOVF	(__SI_FAULT|4)	/* floating point overflow */
+#define FPE_FLTUND	(__SI_FAULT|5)	/* floating point underflow */
+#define FPE_FLTRES	(__SI_FAULT|6)	/* floating point inexact result */
+#define FPE_FLTINV	(__SI_FAULT|7)	/* floating point invalid operation */
+#define FPE_FLTSUB	(__SI_FAULT|8)	/* subscript out of range */
 #define NSIGFPE		8
 
 /*
  * SIGSEGV si_codes
  */
-#define SEGV_MAPERR	1	/* address not mapped to object */
-#define SEGV_ACCERR	2	/* invalid permissions for mapped object */
+#define SEGV_MAPERR	(__SI_FAULT|1)	/* address not mapped to object */
+#define SEGV_ACCERR	(__SI_FAULT|2)	/* invalid permissions for mapped object */
 #define NSIGSEGV	2
 
 /*
  * SIGBUS si_codes
  */
-#define BUS_ADRALN	1	/* invalid address alignment */
-#define BUS_ADRERR	2	/* non-existant physical address */
-#define BUS_OBJERR	3	/* object specific hardware error */
+#define BUS_ADRALN	(__SI_FAULT|1)	/* invalid address alignment */
+#define BUS_ADRERR	(__SI_FAULT|2)	/* non-existant physical address */
+#define BUS_OBJERR	(__SI_FAULT|3)	/* object specific hardware error */
 #define NSIGBUS		3
 
 /*
  * SIGTRAP si_codes
  */
-#define TRAP_BRKPT	1	/* process breakpoint */
-#define TRAP_TRACE	2	/* process trace trap */
+#define TRAP_BRKPT	(__SI_FAULT|1)	/* process breakpoint */
+#define TRAP_TRACE	(__SI_FAULT|2)	/* process trace trap */
 #define NSIGTRAP	2
 
 /*
  * SIGCHLD si_codes
  */
-#define CLD_EXITED	1	/* child has exited */
-#define CLD_KILLED	2	/* child was killed */
-#define CLD_DUMPED	3	/* child terminated abnormally */
-#define CLD_TRAPPED	4	/* traced child has trapped */
-#define CLD_STOPPED	5	/* child has stopped */
-#define CLD_CONTINUED	6	/* stopped child has continued */
+#define CLD_EXITED	(__SI_CHLD|1)	/* child has exited */
+#define CLD_KILLED	(__SI_CHLD|2)	/* child was killed */
+#define CLD_DUMPED	(__SI_CHLD|3)	/* child terminated abnormally */
+#define CLD_TRAPPED	(__SI_CHLD|4)	/* traced child has trapped */
+#define CLD_STOPPED	(__SI_CHLD|5)	/* child has stopped */
+#define CLD_CONTINUED	(__SI_CHLD|6)	/* stopped child has continued */
 #define NSIGCHLD
 
 /*
  * SIGPOLL si_codes
  */
-#define POLL_IN		1	/* data input available */
-#define POLL_OUT	2	/* output buffers available */
-#define POLL_MSG	3	/* input message available */
-#define POLL_ERR	4	/* i/o error */
-#define POLL_PRI	5	/* high priority input available */
-#define POLL_HUP	6	/* device disconnected */
+#define POLL_IN		(__SI_POLL|1)	/* data input available */
+#define POLL_OUT	(__SI_POLL|2)	/* output buffers available */
+#define POLL_MSG	(__SI_POLL|3)	/* input message available */
+#define POLL_ERR	(__SI_POLL|4)	/* i/o error */
+#define POLL_PRI	(__SI_POLL|5)	/* high priority input available */
+#define POLL_HUP	(__SI_POLL|6)	/* device disconnected */
 #define NSIGPOLL	6
 
 /*
@@ -224,7 +224,7 @@
 #ifdef __KERNEL__
 #include <linux/string.h>
 
-extern inline void copy_siginfo(siginfo_t *to, siginfo_t *from)
+static inline void copy_siginfo(siginfo_t *to, siginfo_t *from)
 {
 	if (from->si_code < 0)
 		memcpy(to, from, sizeof(siginfo_t));
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-s390/timex.h linux/include/asm-s390/timex.h
--- linux.orig/include/asm-s390/timex.h	Mon Jan  7 23:41:09 2002
+++ linux/include/asm-s390/timex.h	Fri Dec 21 16:25:31 2001
@@ -17,13 +17,16 @@
 	(1000000/CLOCK_TICK_FACTOR) / (CLOCK_TICK_RATE/CLOCK_TICK_FACTOR)) \
 		<< (SHIFT_SCALE-SHIFT_HZ)) / HZ)
 
-typedef unsigned long cycles_t;
+typedef unsigned long long cycles_t;
 
 extern cycles_t cacheflush_time;
 
 static inline cycles_t get_cycles(void)
 {
-        return 0;
+	cycles_t cycles;
+
+	__asm__("stck  %0" : "=m" (cycles) : : "cc");
+	return cycles >> 2;
 }
 
 #endif
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-s390/uaccess.h linux/include/asm-s390/uaccess.h
--- linux.orig/include/asm-s390/uaccess.h	Mon Jan  7 23:41:09 2002
+++ linux/include/asm-s390/uaccess.h	Fri Dec 21 16:25:31 2001
@@ -379,34 +379,11 @@
  * access register are set up, that 4 points to secondary (user) , 2 to primary (kernel)
  */
 
-asmlinkage void __copy_from_user_fixup(void /* special calling convention */);
-asmlinkage void __copy_to_user_fixup(void /* special calling convention */);
-
-extern inline unsigned long
-__copy_to_user_asm(void* to, const void* from,  long n)
-{
-
-        __asm__ __volatile__ (  "   lr    2,%2\n"
-                                "   lr    4,%1\n"
-                                "   lr    3,%0\n"
-                                "   lr    5,3\n"
-                                "   sacf  512\n"
-                                "0: mvcle 4,2,0\n"
-                                "   jo    0b\n"
-                                "   sacf  0\n"
-                                "   lr    %0,3\n"
-				".section __ex_table,\"a\"\n"
-				"   .align 4\n"
-				"   .long  0b,__copy_to_user_fixup\n"
-				".previous"
-                                : "+&d" (n) : "d" (to), "d" (from)
-                                : "cc", "2", "3", "4", "5" );
-        return n;
-}
+extern long __copy_to_user_asm(const void *from, long n, const void *to);
 
 #define __copy_to_user(to, from, n)                             \
 ({                                                              \
-        __copy_to_user_asm(to,from,n);                          \
+        __copy_to_user_asm(from, n, to);                        \
 })
 
 #define copy_to_user(to, from, n)                               \
@@ -414,38 +391,18 @@
         long err = 0;                                           \
         __typeof__(n) __n = (n);                                \
         if (__access_ok(to,__n)) {                              \
-                err = __copy_to_user_asm(to,from,__n);          \
+                err = __copy_to_user_asm(from, __n, to);        \
         }                                                       \
         else                                                    \
                 err = __n;                                      \
         err;                                                    \
 })
 
-extern inline unsigned long
-__copy_from_user_asm(void* to, const void* from,  long n)
-{
-        __asm__ __volatile__ (  "   lr    2,%1\n"
-                                "   lr    4,%2\n"
-                                "   lr    3,%0\n"
-                                "   lr    5,3\n"
-                                "   sacf  512\n"
-                                "0: mvcle 2,4,0\n"
-                                "   jo    0b\n"
-                                "   sacf  0\n"
-                                "   lr    %0,5\n"
-				".section __ex_table,\"a\"\n"
-				"   .align 4\n"
-				"   .long  0b,__copy_from_user_fixup\n"
-				".previous"
-                                : "+&d" (n) : "d" (to), "d" (from)
-                                : "cc", "2", "3", "4", "5" );
-        return n;
-}
-
+extern long __copy_from_user_asm(void *to, long n, const void *from);
 
 #define __copy_from_user(to, from, n)                           \
 ({                                                              \
-        __copy_from_user_asm(to,from,n);                        \
+        __copy_from_user_asm(to, n, from);                      \
 })
 
 #define copy_from_user(to, from, n)                             \
@@ -453,7 +410,7 @@
         long err = 0;                                           \
         __typeof__(n) __n = (n);                                \
         if (__access_ok(from,__n)) {                            \
-                err = __copy_from_user_asm(to,from,__n);        \
+                err = __copy_from_user_asm(to, __n, from);      \
         }                                                       \
         else                                                    \
                 err = __n;                                      \
@@ -550,38 +507,12 @@
  * Zero Userspace
  */
 
-static inline unsigned long
-__clear_user(void *to, unsigned long n)
-{
-        __asm__ __volatile__ (  "   sacf  512\n"
-                                "   lr    4,%1\n"
-                                "   lr    5,%0\n"
-                                "   sr    2,2\n"
-                                "   sr    3,3\n"
-                                "0: mvcle 4,2,0\n"
-                                "   jo    0b\n"
-                                "   sacf  0\n"
-                                "1: lr    %0,3\n"
-                                ".section .fixup,\"ax\"\n"
-                                "2: lhi   5,-4096\n"
-                                "   n     5,0x90\n"
-                                "   sr    5,4\n"
-                                "   mvcle 4,2,0\n"
-                                "   sacf  0\n"
-                                "   basr  4,0\n"
-                                "   l     4,3f-.(4)\n"
-                                "   br    4\n"
-                                "3: .long 1b\n"
-                                ".previous\n"
-				".section __ex_table,\"a\"\n"
-				"   .align 4\n"
-				"   .long  0b,2b\n"
-				".previous"
-                                : "+&a" (n)
-                                : "a"   (to)
-                                : "cc", "2", "3", "4", "5" );
-        return n;
-}
+extern long __clear_user_asm(void *to, long n);
+
+#define __clear_user(to, n)                                     \
+({                                                              \
+        __clear_user_asm(to, n);                                \
+})
 
 static inline unsigned long
 clear_user(void *to, unsigned long n)
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-s390x/lowcore.h linux/include/asm-s390x/lowcore.h
--- linux.orig/include/asm-s390x/lowcore.h	Mon Jan  7 23:41:09 2002
+++ linux/include/asm-s390x/lowcore.h	Fri Dec 21 16:25:31 2001
@@ -45,6 +45,8 @@
 #define __LC_CPUADDR                    0xD98
 #define __LC_IPLDEV                     0xDB8
 
+#define __LC_JIFFY_TIMER		0xDC0
+
 #define __LC_PANIC_MAGIC                0xE00
 
 #define __LC_AREGS_SAVE_AREA            0x1340
@@ -158,7 +160,7 @@
 	/* entry.S sensitive area end */
 
         /* SMP info area: defined by DJB */
-        __u64        jiffy_timer_cc;           /* 0xdc0 */
+        __u64        jiffy_timer;              /* 0xdc0 */
 	__u64        ext_call_fast;            /* 0xdc8 */
         __u8         pad12[0xe00-0xdd0];       /* 0xdd0 */
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-s390x/processor.h linux/include/asm-s390x/processor.h
--- linux.orig/include/asm-s390x/processor.h	Mon Jan  7 23:41:09 2002
+++ linux/include/asm-s390x/processor.h	Fri Dec 21 16:25:31 2001
@@ -80,7 +80,6 @@
 
 struct thread_struct
  {
-        struct pt_regs *regs;         /* the user registers can be found on*/
 	s390_fp_regs fp_regs;
         __u32   ar2;                   /* kernel access register 2         */
         __u32   ar4;                   /* kernel access register 4         */
@@ -99,8 +98,7 @@
 
 typedef struct thread_struct thread_struct;
 
-#define INIT_THREAD { (struct pt_regs *) 0,                       \
-                    { 0,{{0},{0},{0},{0},{0},{0},{0},{0},{0},{0}, \
+#define INIT_THREAD {{0,{{0},{0},{0},{0},{0},{0},{0},{0},{0},{0}, \
 			    {0},{0},{0},{0},{0},{0}}},            \
                      0, 0,                                        \
                     sizeof(init_stack) + (addr_t) &init_stack,    \
@@ -137,15 +135,24 @@
 
 /*
  * Return saved PC of a blocked thread. used in kernel/sched
+ * resume in entry.S does not create a new stack frame, it
+ * just stores the registers %r6-%r15 to the frame given by
+ * schedule. We want to return the address of the caller of
+ * schedule, so we have to walk the backchain one time to
+ * find the frame schedule() store its return address.
  */
 extern inline unsigned long thread_saved_pc(struct thread_struct *t)
 {
-        return (t->regs) ? ((unsigned long)t->regs->psw.addr) : 0;
+	unsigned long bc;
+	bc = *((unsigned long *) t->ksp);
+	return *((unsigned long *) (bc+112));
 }
 
 unsigned long get_wchan(struct task_struct *p);
-#define KSTK_EIP(tsk)   ((tsk)->thread.regs->psw.addr)
-#define KSTK_ESP(tsk)   ((tsk)->thread.ksp)
+#define __KSTK_PTREGS(tsk) \
+	((struct pt_regs *)((unsigned long) tsk+THREAD_SIZE) - 1)
+#define KSTK_EIP(tsk)	(__KSTK_PTREGS(tsk)->psw.addr)
+#define KSTK_ESP(tsk)	(__KSTK_PTREGS(tsk)->gprs[15])
 
 /* Allocation and freeing of basic task resources. */
 /*
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-s390x/ptrace.h linux/include/asm-s390x/ptrace.h
--- linux.orig/include/asm-s390x/ptrace.h	Mon Jan  7 23:41:09 2002
+++ linux/include/asm-s390x/ptrace.h	Fri Dec 21 16:25:31 2001
@@ -98,7 +98,7 @@
 {
         __u64   mask;
         __u64   addr;
-} psw_t __attribute__ ((aligned(8)));
+} __attribute__ ((aligned(8))) psw_t;
 
 #ifdef __KERNEL__
 #define FIX_PSW(addr) ((unsigned long)(addr))
@@ -130,8 +130,8 @@
 #define FPC_VALID_MASK          0xF8F8FF03
 
 /*
- * The first entries in pt_regs, gdb_pt_regs and user_regs_struct
- * are common for all three structures. The s390_regs structure
+ * The first entries in pt_regs and user_regs_struct
+ * are common for the two structures. The s390_regs structure
  * covers the common parts. It simplifies copying the common part
  * between the three structures.
  */
@@ -158,29 +158,12 @@
 } __attribute__ ((packed));
 
 /*
- * The gdb_pt_regs struct is used instead of the pt_regs structure
- * if kernel remote debugging is used.
- */
-#if CONFIG_REMOTE_DEBUG
-struct gdb_pt_regs
-{
-	psw_t psw;
-	__u64 gprs[NUM_GPRS];
-	__u32 acrs[NUM_ACRS];
-	__u64 orig_gpr2;
-	__u32 trap;
-	__u32 crs[16];
-	s390_fp_regs fp_regs;
-};
-#endif
-
-/*
  * Now for the program event recording (trace) definitions.
  */
 typedef struct
 {
 	__u64 cr[3];
-} per_cr_words  __attribute__((packed));
+} per_cr_words;
 
 #define PER_EM_MASK 0x00000000E8000000UL
 
@@ -203,14 +186,14 @@
 	unsigned                       : 21;
 	addr_t   starting_addr;
 	addr_t   ending_addr;
-} per_cr_bits  __attribute__((packed));
+} per_cr_bits;
 
 typedef struct
 {
 	__u16          perc_atmid;
 	addr_t         address;
 	__u8           access_id;
-} per_lowcore_words  __attribute__((packed));
+} per_lowcore_words;
 
 typedef struct
 {
@@ -230,14 +213,14 @@
 	addr_t   address;                     /* 0x098 */
 	unsigned                         : 4; /* 0x0a1 */
 	unsigned access_id               : 4;
-} per_lowcore_bits __attribute__((packed));
+} per_lowcore_bits;
 
 typedef struct
 {
 	union {
 		per_cr_words   words;
 		per_cr_bits    bits;
-	} control_regs  __attribute__((packed));
+	} control_regs;
 	/*
 	 * Use these flags instead of setting em_instruction_fetch
 	 * directly they are used so that single stepping can be
@@ -256,7 +239,7 @@
 		per_lowcore_words words;
 		per_lowcore_bits  bits;
 	} lowcore; 
-} per_struct __attribute__((packed));
+} per_struct;
 
 typedef struct
 {
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-s390x/s390-gdbregs.h linux/include/asm-s390x/s390-gdbregs.h
--- linux.orig/include/asm-s390x/s390-gdbregs.h	Mon Jan  7 23:41:09 2002
+++ linux/include/asm-s390x/s390-gdbregs.h	Thu Jan  1 00:00:00 1970
@@ -1,89 +0,0 @@
-/*
- *  include/asm-s390/s390-gdbregs.h
- *
- *  S390 version
- *    Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
- *    Author(s): Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com)
- *
- *  used both by the linux kernel for remote debugging & gdb 
- */
-
-#ifndef _S390_GDBREGS_H
-#define _S390_GDBREGS_H
-
-#ifdef __KERNEL__
-#include <asm/s390-regs-common.h>
-#else
-#include <s390x/s390-regs-common.h>
-#endif
-#define S390_MAX_INSTR_SIZE 6
-#define NUM_REGS      (2+NUM_GPRS+NUM_ACRS+NUM_CRS+1+NUM_FPRS)
-#define FIRST_ACR     (2+NUM_GPRS)
-#define LAST_ACR      (FIRST_ACR+NUM_ACRS-1)
-#define FIRST_CR      (FIRST_ACR+NUM_ACRS)
-#define LAST_CR       (FIRST_CR+NUM_CRS-1)
-
-#define PSWM_REGNUM    0
-#define PC_REGNUM      1
-#define	GP0_REGNUM     2		    /* GPR register 0 */ 
-#define GP_LAST_REGNUM (GP0_REGNUM+NUM_GPRS-1)
-#define RETADDR_REGNUM (GP0_REGNUM+14)                   /* Usually return address */
-#define SP_REGNUM      (GP0_REGNUM+15)	    /* Contains address of top of stack */
-#define FP_REGNUM     SP_REGNUM /* needed in findvar.c still */
-#define FRAME_REGNUM  (GP0_REGNUM+11)
-#define FPC_REGNUM    (GP0_REGNUM+NUM_GPRS+NUM_ACRS+NUM_CRS)
-#define FP0_REGNUM    (FPC_REGNUM+1) /* FPR (Floating point) register 0 */
-#define FPLAST_REGNUM (FP0_REGNUM+NUM_FPRS-1)	/* Last floating point register */
-
-/* The top of this structure is as similar as possible to a pt_regs structure to */
-/* simplify code */
-typedef struct
-{
-	S390_REGS_COMMON
-	__u32         crs[NUM_CRS];
-	s390_fp_regs  fp_regs;
-} s390_gdb_regs __attribute__((packed));
-
-#define REGISTER_NAMES                                           \
-{                                                                \
-"pswm","pswa",                                                   \
-"gpr0","gpr1","gpr2","gpr3","gpr4","gpr5","gpr6","gpr7",         \
-"gpr8","gpr9","gpr10","gpr11","gpr12","gpr13","gpr14","gpr15",   \
-"acr0","acr1","acr2","acr3","acr4","acr5","acr6","acr7",         \
-"acr8","acr9","acr10","acr11","acr12","acr13","acr14","acr15",   \
-"cr0","cr1","cr2","cr3","cr4","cr5","cr6","cr7",                 \
-"cr8","cr9","cr10","cr11","cr12","cr13","cr14","cr15",           \
-"fpc",                                                           \
-"fpr0","fpr1","fpr2","fpr3","fpr4","fpr5","fpr6","fpr7",         \
-"fpr8","fpr9","fpr10","fpr11","fpr12","fpr13","fpr14","fpr15"    \
-}
-
-/* Index within `registers' of the first byte of the space for
-   register N.  */
-
-#define ACR0_OFFSET ((PSW_MASK_SIZE+PSW_ADDR_SIZE)+(GPR_SIZE*NUM_GPRS))
-#define CR0_OFFSET (ACR0_OFFSET+(ACR_SIZE+NUM_ACRS))
-#define FPC_OFFSET (CR0_OFFSET+(CR_SIZE*NUM_CRS))
-#define FP0_OFFSET (FPC_OFFSET+(FPC_SIZE+FPC_PAD_SIZE))
-
-#define REGISTER_BYTES    \
-((FP0_OFFSET)+(FPR_SIZE*NUM_FPRS))
-
-#define REGISTER_BYTE(N)  ((N)<=GP_LAST_REGNUM ? (N)*8: \
-(N) <= LAST_ACR ? (ACR0_OFFSET+(((N)-FIRST_ACR)*ACR_SIZE)): \
-(N) <= LAST_CR ? (CR0_OFFSET+(((N)-FIRST_CR)*CR_SIZE)): \
-(N) == FPC_REGNUM ? FPC_OFFSET:(FP0_OFFSET+(((N)-FP0_REGNUM)*FPR_SIZE)))
-
-#endif
-
-
-
-
-
-
-
-
-
-
-
-
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-s390x/scatterlist.h linux/include/asm-s390x/scatterlist.h
--- linux.orig/include/asm-s390x/scatterlist.h	Mon Jan  7 23:41:09 2002
+++ linux/include/asm-s390x/scatterlist.h	Fri Dec 21 16:25:31 2001
@@ -2,7 +2,15 @@
 #define _ASMS390X_SCATTERLIST_H
 
 struct scatterlist {
-    char *  address;    /* Location data is to be transferred to */
+    /* This will disappear in 2.5.x */
+    char *address;
+
+    /* These two are only valid if ADDRESS member of this
+     * struct is NULL.
+     */
+    struct page *page;
+    unsigned int offset;
+
     unsigned int length;
 };
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-s390x/setup.h linux/include/asm-s390x/setup.h
--- linux.orig/include/asm-s390x/setup.h	Mon Jan  7 23:41:09 2002
+++ linux/include/asm-s390x/setup.h	Fri Dec 21 16:25:31 2001
@@ -13,7 +13,7 @@
 #define RAMDISK_ORIGIN		0x800000
 #define RAMDISK_SIZE		0x800000
 
-#ifndef __ASSEMBLER__
+#ifndef __ASSEMBLY__
 
 #define IPL_DEVICE        (*(unsigned long *)  (0x10400))
 #define INITRD_START      (*(unsigned long *)  (0x10408))
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-s390x/siginfo.h linux/include/asm-s390x/siginfo.h
--- linux.orig/include/asm-s390x/siginfo.h	Mon Jan  7 23:41:09 2002
+++ linux/include/asm-s390x/siginfo.h	Fri Dec 21 16:25:31 2001
@@ -111,7 +111,7 @@
 #define SI_USER		0	/* sent by kill, sigsend, raise */
 #define SI_KERNEL	0x80	/* sent by the kernel from somewhere */
 #define SI_QUEUE	-1	/* sent by sigqueue */
-#define SI_TIMER	-2	/* sent by timer expiration */
+#define SI_TIMER __SI_CODE(__SI_TIMER,-2) /* sent by timer expiration */
 #define SI_MESGQ	-3	/* sent by real time mesq state change */
 #define SI_ASYNCIO	-4	/* sent by AIO completion */
 #define SI_SIGIO	-5	/* sent by queued SIGIO */
@@ -122,71 +122,71 @@
 /*
  * SIGILL si_codes
  */
-#define ILL_ILLOPC	1	/* illegal opcode */
-#define ILL_ILLOPN	2	/* illegal operand */
-#define ILL_ILLADR	3	/* illegal addressing mode */
-#define ILL_ILLTRP	4	/* illegal trap */
-#define ILL_PRVOPC	5	/* privileged opcode */
-#define ILL_PRVREG	6	/* privileged register */
-#define ILL_COPROC	7	/* coprocessor error */
-#define ILL_BADSTK	8	/* internal stack error */
+#define ILL_ILLOPC	(__SI_FAULT|1)	/* illegal opcode */
+#define ILL_ILLOPN	(__SI_FAULT|2)	/* illegal operand */
+#define ILL_ILLADR	(__SI_FAULT|3)	/* illegal addressing mode */
+#define ILL_ILLTRP	(__SI_FAULT|4)	/* illegal trap */
+#define ILL_PRVOPC	(__SI_FAULT|5)	/* privileged opcode */
+#define ILL_PRVREG	(__SI_FAULT|6)	/* privileged register */
+#define ILL_COPROC	(__SI_FAULT|7)	/* coprocessor error */
+#define ILL_BADSTK	(__SI_FAULT|8)	/* internal stack error */
 #define NSIGILL		8
 
 /*
  * SIGFPE si_codes
  */
-#define FPE_INTDIV	1	/* integer divide by zero */
-#define FPE_INTOVF	2	/* integer overflow */
-#define FPE_FLTDIV	3	/* floating point divide by zero */
-#define FPE_FLTOVF	4	/* floating point overflow */
-#define FPE_FLTUND	5	/* floating point underflow */
-#define FPE_FLTRES	6	/* floating point inexact result */
-#define FPE_FLTINV	7	/* floating point invalid operation */
-#define FPE_FLTSUB	8	/* subscript out of range */
+#define FPE_INTDIV	(__SI_FAULT|1)	/* integer divide by zero */
+#define FPE_INTOVF	(__SI_FAULT|2)	/* integer overflow */
+#define FPE_FLTDIV	(__SI_FAULT|3)	/* floating point divide by zero */
+#define FPE_FLTOVF	(__SI_FAULT|4)	/* floating point overflow */
+#define FPE_FLTUND	(__SI_FAULT|5)	/* floating point underflow */
+#define FPE_FLTRES	(__SI_FAULT|6)	/* floating point inexact result */
+#define FPE_FLTINV	(__SI_FAULT|7)	/* floating point invalid operation */
+#define FPE_FLTSUB	(__SI_FAULT|8)	/* subscript out of range */
 #define NSIGFPE		8
 
 /*
  * SIGSEGV si_codes
  */
-#define SEGV_MAPERR	1	/* address not mapped to object */
-#define SEGV_ACCERR	2	/* invalid permissions for mapped object */
+#define SEGV_MAPERR	(__SI_FAULT|1)	/* address not mapped to object */
+#define SEGV_ACCERR	(__SI_FAULT|2)	/* invalid permissions for mapped object */
 #define NSIGSEGV	2
 
 /*
  * SIGBUS si_codes
  */
-#define BUS_ADRALN	1	/* invalid address alignment */
-#define BUS_ADRERR	2	/* non-existant physical address */
-#define BUS_OBJERR	3	/* object specific hardware error */
+#define BUS_ADRALN	(__SI_FAULT|1)	/* invalid address alignment */
+#define BUS_ADRERR	(__SI_FAULT|2)	/* non-existant physical address */
+#define BUS_OBJERR	(__SI_FAULT|3)	/* object specific hardware error */
 #define NSIGBUS		3
 
 /*
  * SIGTRAP si_codes
  */
-#define TRAP_BRKPT	1	/* process breakpoint */
-#define TRAP_TRACE	2	/* process trace trap */
+#define TRAP_BRKPT	(__SI_FAULT|1)	/* process breakpoint */
+#define TRAP_TRACE	(__SI_FAULT|2)	/* process trace trap */
 #define NSIGTRAP	2
 
 /*
  * SIGCHLD si_codes
  */
-#define CLD_EXITED	1	/* child has exited */
-#define CLD_KILLED	2	/* child was killed */
-#define CLD_DUMPED	3	/* child terminated abnormally */
-#define CLD_TRAPPED	4	/* traced child has trapped */
-#define CLD_STOPPED	5	/* child has stopped */
-#define CLD_CONTINUED	6	/* stopped child has continued */
+#define CLD_EXITED	(__SI_CHLD|1)	/* child has exited */
+#define CLD_KILLED	(__SI_CHLD|2)	/* child was killed */
+#define CLD_DUMPED	(__SI_CHLD|3)	/* child terminated abnormally */
+#define CLD_TRAPPED	(__SI_CHLD|4)	/* traced child has trapped */
+#define CLD_STOPPED	(__SI_CHLD|5)	/* child has stopped */
+#define CLD_CONTINUED	(__SI_CHLD|6)	/* stopped child has continued */
 #define NSIGCHLD
 
 /*
  * SIGPOLL si_codes
  */
-#define POLL_IN		1	/* data input available */
-#define POLL_OUT	2	/* output buffers available */
-#define POLL_MSG	3	/* input message available */
-#define POLL_ERR	4	/* i/o error */
-#define POLL_PRI	5	/* high priority input available */
-#define POLL_HUP	6	/* device disconnected */
+#define POLL_IN		(__SI_POLL|1)	/* data input available */
+#define POLL_OUT	(__SI_POLL|2)	/* output buffers available */
+#define POLL_MSG	(__SI_POLL|3)	/* input message available */
+#define POLL_ERR	(__SI_POLL|4)	/* i/o error */
+#define POLL_PRI	(__SI_POLL|5)	/* high priority input available */
+#define POLL_HUP	(__SI_POLL|6)	/* device disconnected */
 #define NSIGPOLL	6
 
 /*
@@ -224,7 +224,7 @@
 #ifdef __KERNEL__
 #include <linux/string.h>
 
-extern inline void copy_siginfo(siginfo_t *to, siginfo_t *from)
+static inline void copy_siginfo(siginfo_t *to, siginfo_t *from)
 {
 	if (from->si_code < 0)
 		memcpy(to, from, sizeof(siginfo_t));
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-s390x/timex.h linux/include/asm-s390x/timex.h
--- linux.orig/include/asm-s390x/timex.h	Mon Jan  7 23:41:09 2002
+++ linux/include/asm-s390x/timex.h	Fri Dec 21 16:25:31 2001
@@ -17,13 +17,16 @@
 	(1000000/CLOCK_TICK_FACTOR) / (CLOCK_TICK_RATE/CLOCK_TICK_FACTOR)) \
 		<< (SHIFT_SCALE-SHIFT_HZ)) / HZ)
 
-typedef unsigned long cycles_t;
+typedef unsigned long long cycles_t;
 
 extern cycles_t cacheflush_time;
 
 static inline cycles_t get_cycles(void)
 {
-        return 0;
+	cycles_t cycles;
+
+	__asm__("stck  %0" : "=m" (cycles) : : "cc");
+	return cycles >> 2;
 }
 
 #endif
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-s390x/uaccess.h linux/include/asm-s390x/uaccess.h
--- linux.orig/include/asm-s390x/uaccess.h	Mon Jan  7 23:41:09 2002
+++ linux/include/asm-s390x/uaccess.h	Fri Dec 21 16:25:31 2001
@@ -336,34 +336,11 @@
  * access register are set up, that 4 points to secondary (user) , 2 to primary (kernel)
  */
 
-asmlinkage void __copy_from_user_fixup(void /* special calling convention */);
-asmlinkage void __copy_to_user_fixup(void /* special calling convention */);
-
-extern inline unsigned long
-__copy_to_user_asm(void* to, const void* from,  long n)
-{
-
-        __asm__ __volatile__ (  "   lgr   2,%2\n"
-                                "   lgr   4,%1\n"
-                                "   lgr   3,%0\n"
-                                "   lgr   5,3\n"
-                                "   sacf  512\n"
-                                "0: mvcle 4,2,0\n"
-                                "   jo    0b\n"
-                                "   sacf  0\n"
-                                "   lgr   %0,3\n"
-				".section __ex_table,\"a\"\n"
-				"   .align 8\n"
-				"   .quad  0b,__copy_to_user_fixup\n"
-				".previous"
-                                : "+&d" (n) : "d" (to), "d" (from)
-                                : "cc", "1", "2", "3", "4", "5" );
-        return n;
-}
+extern long __copy_to_user_asm(const void *from, long n, void *to);
 
 #define __copy_to_user(to, from, n)                             \
 ({                                                              \
-        __copy_to_user_asm(to,from,n);                          \
+        __copy_to_user_asm(from, n, to);                        \
 })
 
 #define copy_to_user(to, from, n)                               \
@@ -371,38 +348,18 @@
         long err = 0;                                           \
         __typeof__(n) __n = (n);                                \
         if (__access_ok(to,__n)) {                              \
-                err = __copy_to_user_asm(to,from,__n);          \
+                err = __copy_to_user_asm(from, __n, to);        \
         }                                                       \
         else                                                    \
                 err = __n;                                      \
         err;                                                    \
 })
 
-extern inline unsigned long
-__copy_from_user_asm(void* to, const void* from,  long n)
-{
-        __asm__ __volatile__ (  "   lgr   2,%1\n"
-                                "   lgr   4,%2\n"
-                                "   lgr   3,%0\n"
-                                "   lgr   5,3\n"
-                                "   sacf  512\n"
-                                "0: mvcle 2,4,0\n"
-                                "   jo    0b\n"
-                                "   sacf  0\n"
-                                "   lgr   %0,5\n"
-				".section __ex_table,\"a\"\n"
-				"   .align 8\n"
-				"   .quad  0b,__copy_from_user_fixup\n"
-				".previous"
-                                : "+&d" (n) : "d" (to), "d" (from)
-                                : "cc", "1", "2", "3", "4", "5" );
-        return n;
-}
-
+extern long __copy_from_user_asm(void *to, long n, const void *from);
 
 #define __copy_from_user(to, from, n)                           \
 ({                                                              \
-        __copy_from_user_asm(to,from,n);                        \
+        __copy_from_user_asm(to, n, from);                      \
 })
 
 #define copy_from_user(to, from, n)                             \
@@ -410,7 +367,7 @@
         long err = 0;                                           \
         __typeof__(n) __n = (n);                                \
         if (__access_ok(from,__n)) {                            \
-                err = __copy_from_user_asm(to,from,__n);        \
+                err = __copy_from_user_asm(to, __n, from);      \
         }                                                       \
         else                                                    \
                 err = __n;                                      \
@@ -520,27 +477,12 @@
  * Zero Userspace
  */
 
-static inline unsigned long
-__clear_user(void *to, unsigned long n)
-{
-        __asm__ __volatile__ (  "   sacf  512\n"
-                                "   lgr   4,%1\n"
-                                "   lgr   5,%0\n"
-                                "   sgr   2,2\n"
-                                "   sgr   3,3\n"
-                                "0: mvcle 4,2,0\n"
-                                "   jo    0b\n"
-                                "1: sacf  0\n"
-                                "   lgr   %0,5\n"
-				".section __ex_table,\"a\"\n"
-				"   .align 8\n"
-				"   .quad  0b,__copy_to_user_fixup\n"
-				".previous"
-                                : "+&a" (n)
-                                : "a"   (to)
-                                : "cc", "1", "2", "3", "4", "5" );
-        return n;
-}
+extern long __clear_user_asm(void *to, long n);
+
+#define __clear_user(to, n)                                     \
+({                                                              \
+        __clear_user_asm(to, n);                                \
+})
 
 static inline unsigned long
 clear_user(void *to, unsigned long n)
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-sh/pci.h linux/include/asm-sh/pci.h
--- linux.orig/include/asm-sh/pci.h	Mon Jan  7 23:41:09 2002
+++ linux/include/asm-sh/pci.h	Fri Dec 21 18:25:01 2001
@@ -89,6 +89,31 @@
 	return virt_to_bus(ptr);
 }
 
+/* pci_unmap_{single,page} being a nop depends upon the
+ * configuration.
+ */
+#ifdef CONFIG_SH_PCIDMA_NONCOHERENT
+#define DECLARE_PCI_UNMAP_ADDR(ADDR_NAME)	\
+	dma_addr_t ADDR_NAME;
+#define DECLARE_PCI_UNMAP_LEN(LEN_NAME)		\
+	__u32 LEN_NAME;
+#define PCI_UNMAP_ADDR(PTR, ADDR_NAME)			\
+	((PTR)->ADDR_NAME)
+#define PCI_UNMAP_ADDR_SET(PTR, ADDR_NAME, VAL)		\
+	(((PTR)->ADDR_NAME) = (VAL))
+#define PCI_UNMAP_LEN(PTR, LEN_NAME)			\
+	((PTR)->LEN_NAME)
+#define PCI_UNMAP_LEN_SET(PTR, LEN_NAME, VAL)		\
+	(((PTR)->LEN_NAME) = (VAL))
+#else
+#define DECLARE_PCI_UNMAP_ADDR(ADDR_NAME)
+#define DECLARE_PCI_UNMAP_LEN(LEN_NAME)
+#define PCI_UNMAP_ADDR(PTR, ADDR_NAME)		(0)
+#define PCI_UNMAP_ADDR_SET(PTR, ADDR_NAME, VAL)	do { } while (0)
+#define PCI_UNMAP_LEN(PTR, LEN_NAME)		(0)
+#define PCI_UNMAP_LEN_SET(PTR, LEN_NAME, VAL)	do { } while (0)
+#endif
+
 /* Unmap a single streaming mode DMA translation.  The dma_addr and size
  * must match what was provided for in a previous pci_map_single call.  All
  * other usages are undefined.
@@ -195,6 +220,11 @@
 {
 	return 1;
 }
+
+/* Not supporting more than 32-bit PCI bus addresses now, but
+ * must satisfy references to this function.  Change if needed.
+ */
+#define pci_dac_dma_supported(pci_dev, mask) (0)
 
 /* Return the index of the PCI controller for device PDEV. */
 #define pci_controller_num(PDEV)	(0)
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-sh/stat.h linux/include/asm-sh/stat.h
--- linux.orig/include/asm-sh/stat.h	Mon Jan  7 23:41:09 2002
+++ linux/include/asm-sh/stat.h	Fri Dec 21 18:00:38 2001
@@ -42,8 +42,16 @@
  * insane amounts of padding around dev_t's.
  */
 struct stat64 {
+#if defined(__BIG_ENDIAN__)
+	unsigned char   __pad0b[6];
 	unsigned short	st_dev;
-	unsigned char	__pad0[10];
+#elif defined(__LITTLE_ENDIAN__)
+	unsigned short	st_dev;
+	unsigned char	__pad0b[6];
+#else
+#error Must know endian to build stat64 structure!
+#endif
+	unsigned char	__pad0[4];
 
 	unsigned long	st_ino;
 	unsigned int	st_mode;
@@ -52,14 +60,25 @@
 	unsigned long	st_uid;
 	unsigned long	st_gid;
 
+#if defined(__BIG_ENDIAN__)
+	unsigned char	__pad3b[6];
+	unsigned short	st_rdev;
+#else /* Must be little */
 	unsigned short	st_rdev;
-	unsigned char	__pad3[10];
+	unsigned char	__pad3b[6];
+#endif
+	unsigned char	__pad3[4];
 
 	long long	st_size;
 	unsigned long	st_blksize;
 
+#if defined(__BIG_ENDIAN__)
+	unsigned long	__pad4;		/* Future possible st_blocks hi bits */
+	unsigned long	st_blocks;	/* Number 512-byte blocks allocated. */
+#else /* Must be little */
 	unsigned long	st_blocks;	/* Number 512-byte blocks allocated. */
-	unsigned long	__pad4;		/* future possible st_blocks high bits */
+	unsigned long	__pad4;		/* Future possible st_blocks hi bits */
+#endif
 
 	unsigned long	st_atime;
 	unsigned long	__pad5;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-sh/uaccess.h linux/include/asm-sh/uaccess.h
--- linux.orig/include/asm-sh/uaccess.h	Mon Jan  7 23:41:09 2002
+++ linux/include/asm-sh/uaccess.h	Fri Dec 21 18:00:38 2001
@@ -216,6 +216,7 @@
 	: "r" (val), "m" (__m(addr)), "i" (-EFAULT) \
         : "memory"); })
 #else
+#define __put_user_u64(val,addr,retval) \
 ({ \
 __asm__ __volatile__( \
 	"1:\n\t" \
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-sparc/pci.h linux/include/asm-sparc/pci.h
--- linux.orig/include/asm-sparc/pci.h	Mon Jan  7 23:41:09 2002
+++ linux/include/asm-sparc/pci.h	Fri Dec 21 18:25:01 2001
@@ -63,6 +63,20 @@
  */
 extern void pci_unmap_single(struct pci_dev *hwdev, dma_addr_t dma_addr, size_t size, int direction);
 
+/* pci_unmap_{single,page} is not a nop, thus... */
+#define DECLARE_PCI_UNMAP_ADDR(ADDR_NAME)	\
+	dma_addr_t ADDR_NAME;
+#define DECLARE_PCI_UNMAP_LEN(LEN_NAME)		\
+	__u32 LEN_NAME;
+#define PCI_UNMAP_ADDR(PTR, ADDR_NAME)			\
+	((PTR)->ADDR_NAME)
+#define PCI_UNMAP_ADDR_SET(PTR, ADDR_NAME, VAL)		\
+	(((PTR)->ADDR_NAME) = (VAL))
+#define PCI_UNMAP_LEN(PTR, LEN_NAME)			\
+	((PTR)->LEN_NAME)
+#define PCI_UNMAP_LEN_SET(PTR, LEN_NAME, VAL)		\
+	(((PTR)->LEN_NAME) = (VAL))
+
 /* Map a set of buffers described by scatterlist in streaming
  * mode for DMA.  This is the scather-gather version of the
  * above pci_map_single interface.  Here the scatter gather list
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-sparc64/pci.h linux/include/asm-sparc64/pci.h
--- linux.orig/include/asm-sparc64/pci.h	Mon Jan  7 23:41:09 2002
+++ linux/include/asm-sparc64/pci.h	Fri Dec 21 18:25:01 2001
@@ -77,6 +77,20 @@
 	pci_map_single(dev, (page_address(page) + (off)), size, dir)
 #define pci_unmap_page(dev,addr,sz,dir) pci_unmap_single(dev,addr,sz,dir)
 
+/* pci_unmap_{single,page} is not a nop, thus... */
+#define DECLARE_PCI_UNMAP_ADDR(ADDR_NAME)	\
+	dma_addr_t ADDR_NAME;
+#define DECLARE_PCI_UNMAP_LEN(LEN_NAME)		\
+	__u32 LEN_NAME;
+#define PCI_UNMAP_ADDR(PTR, ADDR_NAME)			\
+	((PTR)->ADDR_NAME)
+#define PCI_UNMAP_ADDR_SET(PTR, ADDR_NAME, VAL)		\
+	(((PTR)->ADDR_NAME) = (VAL))
+#define PCI_UNMAP_LEN(PTR, LEN_NAME)			\
+	((PTR)->LEN_NAME)
+#define PCI_UNMAP_LEN_SET(PTR, LEN_NAME, VAL)		\
+	(((PTR)->LEN_NAME) = (VAL))
+
 /* Map a set of buffers described by scatterlist in streaming
  * mode for DMA.  This is the scather-gather version of the
  * above pci_map_single interface.  Here the scatter gather list
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/linux/intermezzo_fs.h linux/include/linux/intermezzo_fs.h
--- linux.orig/include/linux/intermezzo_fs.h	Tue Nov 13 17:20:56 2001
+++ linux/include/linux/intermezzo_fs.h	Mon Jan  7 14:09:06 2002
@@ -135,6 +135,7 @@
         int fset_permit_count;
         int fset_permit_cookie;
         int fset_chunkbits; 
+        int fset_data; /* replaces the dentry d_data field for fsetroots */
 	struct  kml_fsdata *fset_kmldata;
 	loff_t  fset_file_maxio;  /* writing more than this causes a close */ 
 };
@@ -201,9 +202,6 @@
 void presto_frob_dop(struct dentry *de) ;
 char * presto_path(struct dentry *dentry, struct dentry *root,
                    char *buffer, int buflen);
-void presto_set_dd(struct dentry *);
-void presto_init_ddata_cache(void);
-void presto_cleanup_ddata_cache(void);
 extern struct dentry_operations presto_dentry_ops;
 
 
@@ -231,14 +229,6 @@
 };
 
 
-struct presto_dentry_data { 
-        int dd_count; /* how mnay dentries are using this dentry */
-        struct presto_file_set *dd_fset;
-        loff_t dd_kml_offset;
-        int dd_flags;
-
-}; 
-
 struct presto_file_data { 
         int fd_do_lml;
         loff_t fd_lml_offset;
@@ -259,7 +249,6 @@
         __u64 pv_ctime;
         __u64 pv_size;
 };
-inline struct presto_dentry_data *presto_d2d(struct dentry *);
 int presto_walk(const char *name, struct nameidata *nd);
 int presto_clear_fsetroot(char *path);
 int presto_clear_all_fsetroots(char *path);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/linux/kernel.h linux/include/linux/kernel.h
--- linux.orig/include/linux/kernel.h	Mon Jan  7 23:41:09 2002
+++ linux/include/linux/kernel.h	Wed Dec 26 15:25:28 2001
@@ -36,6 +36,13 @@
 #define	KERN_INFO	"<6>"	/* informational			*/
 #define	KERN_DEBUG	"<7>"	/* debug-level messages			*/
 
+extern int console_printk[];
+
+#define console_loglevel (console_printk[0])
+#define default_message_loglevel (console_printk[1])
+#define minimum_console_loglevel (console_printk[2])
+#define default_console_loglevel (console_printk[3])
+
 # define NORET_TYPE    /**/
 # define ATTRIB_NORET  __attribute__((noreturn))
 # define NORET_AND     noreturn,
@@ -81,8 +88,6 @@
 
 asmlinkage int printk(const char * fmt, ...)
 	__attribute__ ((format (printf, 1, 2)));
-
-extern int console_loglevel;
 
 static inline void console_silent(void)
 {
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/linux/netdevice.h linux/include/linux/netdevice.h
--- linux.orig/include/linux/netdevice.h	Mon Jan  7 23:41:09 2002
+++ linux/include/linux/netdevice.h	Fri Dec 21 18:56:21 2001
@@ -663,6 +663,8 @@
 	NETIF_MSG_TX_DONE	= 0x0400,
 	NETIF_MSG_RX_STATUS	= 0x0800,
 	NETIF_MSG_PKTDATA	= 0x1000,
+	NETIF_MSG_HW		= 0x2000,
+	NETIF_MSG_WOL		= 0x4000,
 };
 
 #define netif_msg_drv(p)	((p)->msg_enable & NETIF_MSG_DRV)
@@ -678,6 +680,8 @@
 #define netif_msg_tx_done(p)	((p)->msg_enable & NETIF_MSG_TX_DONE)
 #define netif_msg_rx_status(p)	((p)->msg_enable & NETIF_MSG_RX_STATUS)
 #define netif_msg_pktdata(p)	((p)->msg_enable & NETIF_MSG_PKTDATA)
+#define netif_msg_hw(p)		((p)->msg_enable & NETIF_MSG_HW)
+#define netif_msg_wol(p)	((p)->msg_enable & NETIF_MSG_WOL)
 
 /* These functions live elsewhere (drivers/net/net_init.c, but related) */
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/linux/pci_ids.h linux/include/linux/pci_ids.h
--- linux.orig/include/linux/pci_ids.h	Mon Jan  7 23:41:09 2002
+++ linux/include/linux/pci_ids.h	Wed Dec 26 18:20:25 2001
@@ -355,6 +355,7 @@
 
 #define PCI_VENDOR_ID_WD		0x101c
 #define PCI_DEVICE_ID_WD_7197		0x3296
+#define PCI_DEVICE_ID_WD_90C		0xc24a
 
 #define PCI_VENDOR_ID_AMI		0x101e
 #define PCI_DEVICE_ID_AMI_MEGARAID3	0x1960
@@ -646,6 +647,12 @@
 #define PCI_DEVICE_ID_APPLE_KL_USB	0x0019
 #define PCI_DEVICE_ID_APPLE_UNI_N_AGP	0x0020
 #define PCI_DEVICE_ID_APPLE_UNI_N_GMAC	0x0021
+#define PCI_DEVICE_ID_APPLE_KEYLARGO	0x0022
+#define PCI_DEVICE_ID_APPLE_UNI_N_GMACP	0x0024
+#define PCI_DEVICE_ID_APPLE_KEYLARGO_P	0x0025
+#define PCI_DEVICE_ID_APPLE_KL_USB_P	0x0026
+#define PCI_DEVICE_ID_APPLE_UNI_N_AGP_P	0x0027
+#define PCI_DEVICE_ID_APPLE_UNI_N_AGP15	0x002d
 #define PCI_DEVICE_ID_APPLE_UNI_N_FW2	0x0030
 
 #define PCI_VENDOR_ID_YAMAHA		0x1073
@@ -855,11 +862,17 @@
 #define PCI_DEVICE_ID_NVIDIA_QUADRO		0x0103
 #define PCI_DEVICE_ID_NVIDIA_GEFORCE2_MX	0x0110
 #define PCI_DEVICE_ID_NVIDIA_GEFORCE2_MX2	0x0111
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE2_GO	0x0112
 #define PCI_DEVICE_ID_NVIDIA_QUADRO2_MXR	0x0113
 #define PCI_DEVICE_ID_NVIDIA_GEFORCE2_GTS	0x0150
 #define PCI_DEVICE_ID_NVIDIA_GEFORCE2_GTS2	0x0151
 #define PCI_DEVICE_ID_NVIDIA_GEFORCE2_ULTRA	0x0152
 #define PCI_DEVICE_ID_NVIDIA_QUADRO2_PRO	0x0153
+#define PCI_DEVICE_ID_NVIDIA_IGEFORCE2		0x01a0
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE3		0x0200
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE3_1		0x0201
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE3_2		0x0202
+#define PCI_DEVICE_ID_NVIDIA_QUADRO_DDC		0x0203
 
 #define PCI_VENDOR_ID_IMS		0x10e0
 #define PCI_DEVICE_ID_IMS_8849		0x8849
@@ -946,7 +959,9 @@
 #define PCI_DEVICE_ID_VIA_8233_7	0x3065
 #define PCI_DEVICE_ID_VIA_82C686_6	0x3068
 #define PCI_DEVICE_ID_VIA_8233_0	0x3074
+#define PCI_DEVICE_ID_VIA_8622		0x3102
 #define PCI_DEVICE_ID_VIA_8233C_0	0x3109
+#define PCI_DEVICE_ID_VIA_8361		0x3112
 #define PCI_DEVICE_ID_VIA_8633_0	0x3091
 #define PCI_DEVICE_ID_VIA_8367_0	0x3099
 #define PCI_DEVICE_ID_VIA_86C100A	0x6100
@@ -1109,6 +1124,8 @@
 #define PCI_DEVICE_ID_ARTOP_ATP850UF	0x0005
 #define PCI_DEVICE_ID_ARTOP_ATP860	0x0006
 #define PCI_DEVICE_ID_ARTOP_ATP860R	0x0007
+#define PCI_DEVICE_ID_ARTOP_ATP865	0x0008
+#define PCI_DEVICE_ID_ARTOP_ATP865R	0x0009
 #define PCI_DEVICE_ID_ARTOP_AEC7610	0x8002
 #define PCI_DEVICE_ID_ARTOP_AEC7612UW	0x8010
 #define PCI_DEVICE_ID_ARTOP_AEC7612U	0x8020
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/linux/pmu.h linux/include/linux/pmu.h
--- linux.orig/include/linux/pmu.h	Mon Jan  7 23:41:09 2002
+++ linux/include/linux/pmu.h	Wed Dec 26 16:36:12 2001
@@ -168,19 +168,41 @@
 
 /* priority levels in notifiers */
 #define SLEEP_LEVEL_VIDEO	100	/* Video driver (first wake) */
-#define SLEEP_LEVEL_SOUND	90	/* Sound driver */
-#define SLEEP_LEVEL_MEDIABAY	80	/* Media bay driver */
-#define SLEEP_LEVEL_BLOCK	70	/* IDE, SCSI */
-#define SLEEP_LEVEL_NET		60	/* bmac */
-#define SLEEP_LEVEL_ADB		50	/* ADB */
-#define SLEEP_LEVEL_MISC	30	/* Anything */
-#define SLEEP_LEVEL_LAST	0	/* Reserved for apm_emu */
+#define SLEEP_LEVEL_MEDIABAY	90	/* Media bay driver */
+#define SLEEP_LEVEL_BLOCK	80	/* IDE, SCSI */
+#define SLEEP_LEVEL_NET		70	/* bmac, gmac */
+#define SLEEP_LEVEL_MISC	60	/* Anything else */
+#define SLEEP_LEVEL_USERLAND	55	/* Reserved for apm_emu */
+#define SLEEP_LEVEL_ADB		50	/* ADB (async) */
+#define SLEEP_LEVEL_SOUND	40	/* Sound driver (blocking) */
 
 /* special register notifier functions */
 int pmu_register_sleep_notifier(struct pmu_sleep_notifier* notifier);
 int pmu_unregister_sleep_notifier(struct pmu_sleep_notifier* notifier);
 
-#endif /* CONFIG_PMAC_PBOOK */
+#define PMU_MAX_BATTERIES	2
+
+/* values for pmu_power_flags */
+#define PMU_PWR_AC_PRESENT	0x00000001
+
+/* values for pmu_battery_info.flags */
+#define PMU_BATT_PRESENT	0x00000001
+#define PMU_BATT_CHARGING	0x00000002
 
+struct pmu_battery_info
+{
+	unsigned int	flags;
+	unsigned int	charge;		/* current charge */
+	unsigned int	max_charge;	/* maximum charge */
+	signed int	current;	/* current, positive if charging */
+	unsigned int	voltage;	/* voltage */
+	unsigned int	time_remaining;	/* remaining time */
+};
+
+extern int pmu_battery_count;
+extern struct pmu_battery_info pmu_batteries[PMU_MAX_BATTERIES];
+extern unsigned int pmu_power_flags;
+
+#endif /* CONFIG_PMAC_PBOOK */
 
 #endif	/* __KERNEL__ */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/linux/reiserfs_fs_sb.h linux/include/linux/reiserfs_fs_sb.h
--- linux.orig/include/linux/reiserfs_fs_sb.h	Fri Dec 21 17:42:03 2001
+++ linux/include/linux/reiserfs_fs_sb.h	Mon Jan  7 18:15:56 2002
@@ -407,7 +407,7 @@
 				/* To be obsoleted soon by per buffer seals.. -Hans */
     atomic_t s_generation_counter; // increased by one every time the
     // tree gets re-balanced
-    unsigned int s_properties;    /* File system properties. Currently holds
+    unsigned long s_properties;    /* File system properties. Currently holds
 				     on-disk FS format */
     
     /* session statistics */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/linux/sonypi.h linux/include/linux/sonypi.h
--- linux.orig/include/linux/sonypi.h	Thu Oct 11 18:17:22 2001
+++ linux/include/linux/sonypi.h	Mon Jan  7 13:03:04 2002
@@ -75,9 +75,21 @@
 #define SONYPI_EVENT_LID_OPENED			37
 
 
-/* brightness etc. ioctls */
-#define SONYPI_IOCGBRT	_IOR('v', 0, __u8)
-#define SONYPI_IOCSBRT	_IOW('v', 0, __u8)
+/* get/set brightness */
+#define SONYPI_IOCGBRT		_IOR('v', 0, __u8)
+#define SONYPI_IOCSBRT		_IOW('v', 0, __u8)
+
+/* get battery full capacity/remaining capacity */
+#define SONYPI_IOCGBAT1CAP	_IOR('v', 2, __u16)
+#define SONYPI_IOCGBAT1REM	_IOR('v', 3, __u16)
+#define SONYPI_IOCGBAT2CAP	_IOR('v', 4, __u16)
+#define SONYPI_IOCGBAT2REM	_IOR('v', 5, __u16)
+
+/* get battery flags: battery1/battery2/ac adapter present */
+#define SONYPI_BFLAGS_B1	0x01
+#define SONYPI_BFLAGS_B2	0x02
+#define SONYPI_BFLAGS_AC	0x04
+#define SONYPI_IOCGBATFLAGS	_IOR('v', 7, __u8)
 
 #ifdef __KERNEL__
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/linux/sunrpc/clnt.h linux/include/linux/sunrpc/clnt.h
--- linux.orig/include/linux/sunrpc/clnt.h	Thu Nov 22 19:47:20 2001
+++ linux/include/linux/sunrpc/clnt.h	Mon Jan  7 17:36:23 2002
@@ -136,7 +136,6 @@
 	xprt_set_timeout(&clnt->cl_timeout, retr, incr);
 }
 
-extern void rpciod_tcp_dispatcher(void);
 extern void rpciod_wake_up(void);
 
 /*
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/linux/threads.h linux/include/linux/threads.h
--- linux.orig/include/linux/threads.h	Mon Jan  7 23:41:09 2002
+++ linux/include/linux/threads.h	Wed Dec 26 15:25:28 2001
@@ -5,7 +5,7 @@
 
 /*
  * The default limit for the nr of threads is now in
- * /proc/sys/kernel/max-threads.
+ * /proc/sys/kernel/threads-max.
  */
  
 #ifdef CONFIG_SMP
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/linux/usb.h linux/include/linux/usb.h
--- linux.orig/include/linux/usb.h	Mon Jan  7 23:41:09 2002
+++ linux/include/linux/usb.h	Wed Dec 26 14:28:36 2001
@@ -470,40 +470,84 @@
 	iso_packet_descriptor_t iso_frame_desc[0];
 } urb_t, *purb_t;
 
-#define FILL_CONTROL_URB(a,aa,b,c,d,e,f,g) \
+/**
+ * FILL_CONTROL_URB - macro to help initialize a control urb
+ * @URB: pointer to the urb to initialize.
+ * @DEV: pointer to the struct usb_device for this urb.
+ * @PIPE: the endpoint pipe
+ * @SETUP_PACKET: pointer to the setup_packet buffer
+ * @TRANSFER_BUFFER: pointer to the transfer buffer
+ * @BUFFER_LENGTH: length of the transfer buffer
+ * @COMPLETE: pointer to the usb_complete_t function
+ * @CONTEXT: what to set the urb context to.
+ *
+ * Initializes a control urb with the proper information needed to submit
+ * it to a device.  This macro is depreciated, the usb_fill_control_urb()
+ * function should be used instead.
+ */
+#define FILL_CONTROL_URB(URB,DEV,PIPE,SETUP_PACKET,TRANSFER_BUFFER,BUFFER_LENGTH,COMPLETE,CONTEXT) \
     do {\
-	spin_lock_init(&(a)->lock);\
-	(a)->dev=aa;\
-	(a)->pipe=b;\
-	(a)->setup_packet=c;\
-	(a)->transfer_buffer=d;\
-	(a)->transfer_buffer_length=e;\
-	(a)->complete=f;\
-	(a)->context=g;\
+	spin_lock_init(&(URB)->lock);\
+	(URB)->dev=DEV;\
+	(URB)->pipe=PIPE;\
+	(URB)->setup_packet=SETUP_PACKET;\
+	(URB)->transfer_buffer=TRANSFER_BUFFER;\
+	(URB)->transfer_buffer_length=BUFFER_LENGTH;\
+	(URB)->complete=COMPLETE;\
+	(URB)->context=CONTEXT;\
     } while (0)
 
-#define FILL_BULK_URB(a,aa,b,c,d,e,f) \
+/**
+ * FILL_BULK_URB - macro to help initialize a bulk urb
+ * @URB: pointer to the urb to initialize.
+ * @DEV: pointer to the struct usb_device for this urb.
+ * @PIPE: the endpoint pipe
+ * @TRANSFER_BUFFER: pointer to the transfer buffer
+ * @BUFFER_LENGTH: length of the transfer buffer
+ * @COMPLETE: pointer to the usb_complete_t function
+ * @CONTEXT: what to set the urb context to.
+ *
+ * Initializes a bulk urb with the proper information needed to submit it
+ * to a device.  This macro is depreciated, the usb_fill_bulk_urb()
+ * function should be used instead.
+ */
+#define FILL_BULK_URB(URB,DEV,PIPE,TRANSFER_BUFFER,BUFFER_LENGTH,COMPLETE,CONTEXT) \
     do {\
-	spin_lock_init(&(a)->lock);\
-	(a)->dev=aa;\
-	(a)->pipe=b;\
-	(a)->transfer_buffer=c;\
-	(a)->transfer_buffer_length=d;\
-	(a)->complete=e;\
-	(a)->context=f;\
+	spin_lock_init(&(URB)->lock);\
+	(URB)->dev=DEV;\
+	(URB)->pipe=PIPE;\
+	(URB)->transfer_buffer=TRANSFER_BUFFER;\
+	(URB)->transfer_buffer_length=BUFFER_LENGTH;\
+	(URB)->complete=COMPLETE;\
+	(URB)->context=CONTEXT;\
     } while (0)
     
-#define FILL_INT_URB(a,aa,b,c,d,e,f,g) \
+/**
+ * FILL_INT_URB - macro to help initialize a interrupt urb
+ * @URB: pointer to the urb to initialize.
+ * @DEV: pointer to the struct usb_device for this urb.
+ * @PIPE: the endpoint pipe
+ * @TRANSFER_BUFFER: pointer to the transfer buffer
+ * @BUFFER_LENGTH: length of the transfer buffer
+ * @COMPLETE: pointer to the usb_complete_t function
+ * @CONTEXT: what to set the urb context to.
+ * @INTERVAL: what to set the urb interval to.
+ *
+ * Initializes a interrupt urb with the proper information needed to submit
+ * it to a device.  This macro is depreciated, the usb_fill_int_urb()
+ * function should be used instead.
+ */
+#define FILL_INT_URB(URB,DEV,PIPE,TRANSFER_BUFFER,BUFFER_LENGTH,COMPLETE,CONTEXT,INTERVAL) \
     do {\
-	spin_lock_init(&(a)->lock);\
-	(a)->dev=aa;\
-	(a)->pipe=b;\
-	(a)->transfer_buffer=c;\
-	(a)->transfer_buffer_length=d;\
-	(a)->complete=e;\
-	(a)->context=f;\
-	(a)->interval=g;\
-	(a)->start_frame=-1;\
+	spin_lock_init(&(URB)->lock);\
+	(URB)->dev=DEV;\
+	(URB)->pipe=PIPE;\
+	(URB)->transfer_buffer=TRANSFER_BUFFER;\
+	(URB)->transfer_buffer_length=BUFFER_LENGTH;\
+	(URB)->complete=COMPLETE;\
+	(URB)->context=CONTEXT;\
+	(URB)->interval=INTERVAL;\
+	(URB)->start_frame=-1;\
     } while (0)
 
 #define FILL_CONTROL_URB_TO(a,aa,b,c,d,e,f,g,h) \
@@ -530,7 +574,105 @@
 	(a)->context=f;\
 	(a)->timeout=g;\
     } while (0)
+ 
+/**
+ * usb_fill_control_urb - initializes a control urb
+ * @urb: pointer to the urb to initialize.
+ * @dev: pointer to the struct usb_device for this urb.
+ * @pipe: the endpoint pipe
+ * @setup_packet: pointer to the setup_packet buffer
+ * @transfer_buffer: pointer to the transfer buffer
+ * @buffer_length: length of the transfer buffer
+ * @complete: pointer to the usb_complete_t function
+ * @context: what to set the urb context to.
+ *
+ * Initializes a control urb with the proper information needed to submit
+ * it to a device.
+ */
+static inline void usb_fill_control_urb (struct urb *urb,
+					 struct usb_device *dev,
+					 unsigned int pipe,
+					 unsigned char *setup_packet,
+					 void *transfer_buffer,
+					 int buffer_length,
+					 usb_complete_t complete,
+					 void *context)
+{
+	spin_lock_init(&urb->lock);
+	urb->dev = dev;
+	urb->pipe = pipe;
+	urb->setup_packet = setup_packet;
+	urb->transfer_buffer = transfer_buffer;
+	urb->transfer_buffer_length = buffer_length;
+	urb->complete = complete;
+	urb->context = context;
+}
+
+/**
+ * usb_fill_bulk_urb - macro to help initialize a bulk urb
+ * @urb: pointer to the urb to initialize.
+ * @dev: pointer to the struct usb_device for this urb.
+ * @pipe: the endpoint pipe
+ * @transfer_buffer: pointer to the transfer buffer
+ * @buffer_length: length of the transfer buffer
+ * @complete: pointer to the usb_complete_t function
+ * @context: what to set the urb context to.
+ *
+ * Initializes a bulk urb with the proper information needed to submit it
+ * to a device.
+ */
+static inline void usb_fill_bulk_urb (struct urb *urb,
+				      struct usb_device *dev,
+				      unsigned int pipe,
+				      void *transfer_buffer,
+				      int buffer_length,
+				      usb_complete_t complete,
+				      void *context)
+				      
+{
+	spin_lock_init(&urb->lock);
+	urb->dev = dev;
+	urb->pipe = pipe;
+	urb->transfer_buffer = transfer_buffer;
+	urb->transfer_buffer_length = buffer_length;
+	urb->complete = complete;
+	urb->context = context;
+}
     
+/**
+ * usb_fill_int_urb - macro to help initialize a interrupt urb
+ * @urb: pointer to the urb to initialize.
+ * @dev: pointer to the struct usb_device for this urb.
+ * @pipe: the endpoint pipe
+ * @transfer_buffer: pointer to the transfer buffer
+ * @buffer_length: length of the transfer buffer
+ * @complete: pointer to the usb_complete_t function
+ * @context: what to set the urb context to.
+ * @interval: what to set the urb interval to.
+ *
+ * Initializes a interrupt urb with the proper information needed to submit
+ * it to a device.
+ */
+static inline void usb_fill_int_urb (struct urb *urb,
+				     struct usb_device *dev,
+				     unsigned int pipe,
+				     void *transfer_buffer,
+				     int buffer_length,
+				     usb_complete_t complete,
+				     void *context,
+				     int interval)
+{
+	spin_lock_init(&urb->lock);
+	urb->dev = dev;
+	urb->pipe = pipe;
+	urb->transfer_buffer = transfer_buffer;
+	urb->transfer_buffer_length = buffer_length;
+	urb->complete = complete;
+	urb->context = context;
+	urb->interval = interval;
+	urb->start_frame = -1;
+}
+
 purb_t usb_alloc_urb(int iso_packets);
 void usb_free_urb (purb_t purb);
 int usb_submit_urb(purb_t purb);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/kernel/exit.c linux/kernel/exit.c
--- linux.orig/kernel/exit.c	Mon Jan  7 23:41:09 2002
+++ linux/kernel/exit.c	Wed Dec 26 16:16:25 2001
@@ -150,21 +150,34 @@
 }
 
 /*
- * When we die, we re-parent all our children to
+ * When we die, we re-parent all our children.
+ * Try to give them to another thread in our process
+ * group, and if no such member exists, give it to
  * the global child reaper process (ie "init")
  */
 static inline void forget_original_parent(struct task_struct * father)
 {
-	struct task_struct * p;
+	struct task_struct * p, *reaper;
 
 	read_lock(&tasklist_lock);
 
+	/* Next in our thread group */
+	reaper = next_thread(father);
+	if (reaper == father)
+		reaper = child_reaper;
+
 	for_each_task(p) {
 		if (p->p_opptr == father) {
 			/* We dont want people slaying init */
 			p->exit_signal = SIGCHLD;
 			p->self_exec_id++;
-			p->p_opptr = child_reaper;
+
+			/* Make sure we're not reparenting to ourselves */
+			if (p == reaper)
+				p->p_opptr = child_reaper;
+			else
+				p->p_opptr = reaper;
+
 			if (p->pdeath_signal) send_sig(p->pdeath_signal, p, 0);
 		}
 	}
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/kernel/ksyms.c linux/kernel/ksyms.c
--- linux.orig/kernel/ksyms.c	Mon Jan  7 23:41:09 2002
+++ linux/kernel/ksyms.c	Fri Dec 21 16:25:31 2001
@@ -354,9 +354,7 @@
 EXPORT_SYMBOL(del_timer);
 EXPORT_SYMBOL(request_irq);
 EXPORT_SYMBOL(free_irq);
-#if !defined(CONFIG_ARCH_S390)
-EXPORT_SYMBOL(irq_stat);	/* No separate irq_stat for s390, it is part of PSA */
-#endif
+EXPORT_SYMBOL(irq_stat);
 
 /* waitqueue handling */
 EXPORT_SYMBOL(add_wait_queue);
@@ -461,6 +459,7 @@
 EXPORT_SYMBOL(bdevname);
 EXPORT_SYMBOL(cdevname);
 EXPORT_SYMBOL(simple_strtoul);
+EXPORT_SYMBOL(simple_strtoull);
 EXPORT_SYMBOL(system_utsname);	/* UTS data */
 EXPORT_SYMBOL(uts_sem);		/* UTS semaphore */
 #ifndef __mips__
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/kernel/printk.c linux/kernel/printk.c
--- linux.orig/kernel/printk.c	Mon Jan  7 23:41:09 2002
+++ linux/kernel/printk.c	Wed Dec 26 15:25:28 2001
@@ -16,6 +16,7 @@
  *	01Mar01 Andrew Morton <andrewm@uow.edu.au>
  */
 
+#include <linux/kernel.h>
 #include <linux/mm.h>
 #include <linux/tty.h>
 #include <linux/tty_driver.h>
@@ -30,6 +31,8 @@
 
 #ifdef CONFIG_MULTIQUAD
 #define LOG_BUF_LEN	(65536)
+#elif defined(CONFIG_ARCH_S390)
+#define LOG_BUF_LEN	(131072)
 #elif defined(CONFIG_SMP)
 #define LOG_BUF_LEN	(32768)
 #else	
@@ -51,11 +54,12 @@
 
 DECLARE_WAIT_QUEUE_HEAD(log_wait);
 
-/* Keep together for sysctl support */
-int console_loglevel = DEFAULT_CONSOLE_LOGLEVEL;
-int default_message_loglevel = DEFAULT_MESSAGE_LOGLEVEL;
-int minimum_console_loglevel = MINIMUM_CONSOLE_LOGLEVEL;
-int default_console_loglevel = DEFAULT_CONSOLE_LOGLEVEL;
+int console_printk[4] = {
+	DEFAULT_CONSOLE_LOGLEVEL,	/* console_loglevel */
+	DEFAULT_MESSAGE_LOGLEVEL,	/* default_message_loglevel */
+	MINIMUM_CONSOLE_LOGLEVEL,	/* minimum_console_loglevel */
+	DEFAULT_CONSOLE_LOGLEVEL,	/* default_console_loglevel */
+};
 
 int oops_in_progress;
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/mm/filemap.c linux/mm/filemap.c
--- linux.orig/mm/filemap.c	Mon Jan  7 23:41:09 2002
+++ linux/mm/filemap.c	Sun Dec 23 16:14:58 2001
@@ -941,7 +941,6 @@
 	spin_unlock(&pagecache_lock);
 	if (!page) {
 		struct page *newpage = alloc_page(gfp_mask);
-		page = NULL;
 		if (newpage) {
 			spin_lock(&pagecache_lock);
 			page = __find_lock_page_helper(mapping, index, *hash);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/mm/vmscan.c linux/mm/vmscan.c
--- linux.orig/mm/vmscan.c	Fri Dec 21 17:42:05 2001
+++ linux/mm/vmscan.c	Mon Jan  7 23:39:22 2002
@@ -537,7 +537,7 @@
 
 	spin_lock(&pagemap_lru_lock);
 	entry = active_list.prev;
-	while (nr_pages-- && entry != &active_list) {
+	while (nr_pages && entry != &active_list) {
 		struct page * page;
 
 		page = list_entry(entry, struct page, lru);
@@ -547,6 +547,8 @@
 			list_add(&page->lru, &active_list);
 			continue;
 		}
+
+		nr_pages--;
 
 		del_page_from_active_list(page);
 		add_page_to_inactive_list(page);
