diff -ubw -Naur linux-2.6.8.orig/arch/ppc/Kconfig linux-2.6.8/arch/ppc/Kconfig
--- linux-2.6.8.orig/arch/ppc/Kconfig	2004-08-14 07:38:08.000000000 +0200
+++ linux-2.6.8/arch/ppc/Kconfig	2004-08-14 18:23:39.941585920 +0200
@@ -985,6 +985,8 @@
 
 source "drivers/zorro/Kconfig"
 
+source kernel/power/Kconfig
+
 endmenu
 
 menu "Bus options"
diff -ubw -Naur linux-2.6.8.orig/arch/ppc/Kconfig.orig linux-2.6.8/arch/ppc/Kconfig.orig
--- linux-2.6.8.orig/arch/ppc/Kconfig.orig	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.8/arch/ppc/Kconfig.orig	2004-08-14 07:38:08.000000000 +0200
@@ -0,0 +1,1356 @@
+# For a description of the syntax of this configuration file,
+# see Documentation/kbuild/kconfig-language.txt.
+#
+
+mainmenu "Linux/PowerPC Kernel Configuration"
+
+config MMU
+	bool
+	default y
+
+config UID16
+	bool
+
+config RWSEM_GENERIC_SPINLOCK
+	bool
+
+config RWSEM_XCHGADD_ALGORITHM
+	bool
+	default y
+
+config HAVE_DEC_LOCK
+	bool
+	default y
+
+config PPC
+	bool
+	default y
+
+config PPC32
+	bool
+	default y
+
+# All PPCs use generic nvram driver through ppc_md
+config GENERIC_NVRAM
+	bool
+	default y
+
+source "init/Kconfig"
+
+menu "Processor"
+
+choice
+	prompt "Processor Type"
+	default 6xx
+
+config 6xx
+	bool "6xx/7xx/74xx/52xx/8260"
+	help
+	  There are four types of PowerPC chips supported.  The more common
+	  types (601, 603, 604, 740, 750, 7400), the Motorola embedded
+	  versions (821, 823, 850, 855, 860, 52xx, 8260), the IBM embedded
+	  versions (403 and 405) and the high end 64 bit Power processors 
+	  (POWER 3, POWER4, and IBM 970 also known as G5)
+	  Unless you are building a kernel for one of the embedded processor
+	  systems, 64 bit IBM RS/6000 or an Apple G5, choose 6xx.
+	  Note that the kernel runs in 32-bit mode even on 64-bit chips.
+	  Also note that because the 52xx & 82xx family has a 603e core,
+	  specific support for that chipset is asked later on.
+
+config 40x
+	bool "40x"
+
+config 44x
+	bool "44x"
+
+config POWER3
+	bool "POWER3"
+
+config POWER4
+	bool "POWER4 and 970 (G5)"
+
+config 8xx
+	bool "8xx"
+
+config E500
+	bool "e500"
+
+endchoice
+
+config BOOKE
+	bool
+	depends on E500
+	default y
+
+config FSL_BOOKE
+	bool
+	depends on E500
+	default y
+
+config PTE_64BIT
+	bool
+	depends on 44x
+	default y
+
+config ALTIVEC
+	bool "AltiVec Support"
+	depends on 6xx || POWER4
+	depends on !8260
+	---help---
+	  This option enables kernel support for the Altivec extensions to the
+	  PowerPC processor. The kernel currently supports saving and restoring
+	  altivec registers, and turning on the 'altivec enable' bit so user
+	  processes can execute altivec instructions.
+
+	  This option is only usefully if you have a processor that supports
+	  altivec (G4, otherwise known as 74xx series), but does not have
+	  any affect on a non-altivec cpu (it does, however add code to the
+	  kernel).
+
+	  If in doubt, say Y here.
+
+config SPE
+	bool "SPE Support"
+	depends on E500
+	---help---
+	  This option enables kernel support for the Signal Processing
+	  Extensions (SPE) to the PowerPC processor. The kernel currently
+	  supports saving and restoring SPE registers, and turning on the
+	  'spe enable' bit so user processes can execute SPE instructions.
+
+	  This option is only usefully if you have a processor that supports
+	  SPE (e500, otherwise known as 85xx series), but does not have any
+	  affect on a non-spe cpu (it does, however add code to the kernel).
+
+	  If in doubt, say Y here.
+
+config TAU
+	bool "Thermal Management Support"
+	depends on 6xx && !8260
+	help
+	  G3 and G4 processors have an on-chip temperature sensor called the
+	  'Thermal Assist Unit (TAU)', which, in theory, can measure the on-die
+	  temperature within 2-4 degrees Celsius. This option shows the current
+	  on-die temperature in /proc/cpuinfo if the cpu supports it.
+
+	  Unfortunately, on some chip revisions, this sensor is very inaccurate
+	  and in some cases, does not work at all, so don't assume the cpu
+	  temp is actually what /proc/cpuinfo says it is.
+
+config TAU_INT
+	bool "Interrupt driven TAU driver (DANGEROUS)"
+	depends on TAU
+	---help---
+	  The TAU supports an interrupt driven mode which causes an interrupt
+	  whenever the temperature goes out of range. This is the fastest way
+	  to get notified the temp has exceeded a range. With this option off,
+	  a timer is used to re-check the temperature periodically.
+
+	  However, on some cpus it appears that the TAU interrupt hardware
+	  is buggy and can cause a situation which would lead unexplained hard
+	  lockups.
+
+	  Unless you are extending the TAU driver, or enjoy kernel/hardware
+	  debugging, leave this option off.
+
+config TAU_AVERAGE
+	bool "Average high and low temp"
+	depends on TAU
+	---help---
+	  The TAU hardware can compare the temperature to an upper and lower
+	  bound.  The default behavior is to show both the upper and lower
+	  bound in /proc/cpuinfo. If the range is large, the temperature is
+	  either changing a lot, or the TAU hardware is broken (likely on some
+	  G4's). If the range is small (around 4 degrees), the temperature is
+	  relatively stable.  If you say Y here, a single temperature value,
+	  halfway between the upper and lower bounds, will be reported in
+	  /proc/cpuinfo.
+
+	  If in doubt, say N here.
+
+config MATH_EMULATION
+	bool "Math emulation"
+	depends on 4xx || 8xx || E500
+	---help---
+	  Some PowerPC chips designed for embedded applications do not have
+	  a floating-point unit and therefore do not implement the
+	  floating-point instructions in the PowerPC instruction set.  If you
+	  say Y here, the kernel will include code to emulate a floating-point
+	  unit, which will allow programs that use floating-point
+	  instructions to run.
+
+	  If you have an Apple machine or an IBM RS/6000 or pSeries machine,
+	  or any machine with a 6xx, 7xx or 7xxx series processor, say N
+	  here.  Saying Y here will not hurt performance (on any machine) but
+	  will increase the size of the kernel.
+
+config CPU_FREQ
+	bool "CPU Frequency scaling"
+	help
+	  Clock scaling allows you to change the clock speed of CPUs on the
+	  fly. This is a nice method to save battery power on notebooks,
+	  because the lower the clock speed, the less power the CPU consumes.
+
+	  For more information, take a look at <file:Documentation/cpu-freq> or
+	  at <http://www.brodo.de/cpufreq/>
+
+	  If in doubt, say N.
+
+source "drivers/cpufreq/Kconfig"
+
+config CPU_FREQ_PMAC
+	bool "Support for Apple PowerBooks"
+	depends on CPU_FREQ && ADB_PMU
+	help
+	  This adds support for frequency switching on Apple PowerBooks,
+	  this currently includes some models of iBook & Titanium
+	  PowerBook.
+
+config CPU_FREQ_TABLE
+	bool
+	depends on CPU_FREQ_PMAC
+	default y
+
+config PPC601_SYNC_FIX
+	bool "Workarounds for PPC601 bugs"
+	depends on 6xx && (PPC_PREP || PPC_PMAC)
+	help
+	  Some versions of the PPC601 (the first PowerPC chip) have bugs which
+	  mean that extra synchronization instructions are required near
+	  certain instructions, typically those that make major changes to the
+	  CPU state.  These extra instructions reduce performance slightly.
+	  If you say N here, these extra instructions will not be included,
+	  resulting in a kernel which will run faster but may not run at all
+	  on some systems with the PPC601 chip.
+
+	  If in doubt, say Y here.
+
+source arch/ppc/platforms/4xx/Kconfig
+source arch/ppc/platforms/85xx/Kconfig
+
+config PPC64BRIDGE
+	bool
+	depends on POWER3 || POWER4
+	default y
+
+config PPC_STD_MMU
+	bool
+	depends on 6xx || POWER3 || POWER4
+	default y
+
+config NOT_COHERENT_CACHE
+	bool
+	depends on 4xx || 8xx
+	default y
+
+endmenu
+
+menu "Platform options"
+
+choice
+	prompt "8xx Machine Type"
+	depends on 8xx
+	default RPXLITE
+
+config RPXLITE
+	bool "RPX-Lite"
+	---help---
+	  Single-board computers based around the PowerPC MPC8xx chips and
+	  intended for embedded applications.  The following types are
+	  supported:
+
+	  RPX-Lite:
+	  Embedded Planet RPX Lite. PC104 form-factor SBC based on the MPC823.
+
+	  RPX-Classic:
+	  Embedded Planet RPX Classic Low-fat. Credit-card-size SBC based on
+	  the MPC 860
+
+	  BSE-IP:
+	  Bright Star Engineering ip-Engine.
+
+	  TQM823L:
+	  TQM850L:
+	  TQM855L:
+	  TQM860L:
+	  MPC8xx based family of mini modules, half credit card size,
+	  up to 64 MB of RAM, 8 MB Flash, (Fast) Ethernet, 2 x serial ports,
+	  2 x CAN bus interface, ...
+	  Manufacturer: TQ Components, www.tq-group.de
+	  Date of Release: October (?) 1999
+	  End of Life: not yet :-)
+	  URL:
+	  - module: <http://www.denx.de/PDF/TQM8xxLHWM201.pdf>
+	  - starter kit: <http://www.denx.de/PDF/STK8xxLHWM201.pdf>
+	  - images: <http://www.denx.de/embedded-ppc-en.html>
+
+	  FPS850L:
+	  FingerPrint Sensor System (based on TQM850L)
+	  Manufacturer: IKENDI AG, <http://www.ikendi.com/>
+	  Date of Release: November 1999
+	  End of life: end 2000 ?
+	  URL: see TQM850L
+
+	  SPD823TS:
+	  MPC823 based board used in the "Tele Server" product
+	  Manufacturer: Speech Design, <http://www.speech-design.de/>
+	  Date of Release: Mid 2000 (?)
+	  End of life: -
+	  URL: <http://www.speech-design.de/>
+	  select "English", then "Teleteam Solutions", then "TeleServer"
+
+	  IVMS8:
+	  MPC860 based board used in the "Integrated Voice Mail System",
+	  Small Version (8 voice channels)
+	  Manufacturer: Speech Design, <http://www.speech-design.de/>
+	  Date of Release: December 2000 (?)
+	  End of life: -
+	  URL: <http://www.speech-design.de/>
+
+	  IVML24:
+	  MPC860 based board used in the "Integrated Voice Mail System",
+	  Large Version (24 voice channels)
+	  Manufacturer: Speech Design, <http://www.speech-design.de/>
+	  Date of Release: March 2001  (?)
+	  End of life: -
+	  URL: <http://www.speech-design.de/>
+
+	  SM850:
+	  Service Module (based on TQM850L)
+	  Manufacturer: Dependable Computer Systems, <http://www.decomsys.com/>
+	  Date of Release: end 2000 (?)
+	  End of life: mid 2001 (?)
+	  URL: <http://www.tz-mikroelektronik.de/ServiceModule/index.html>
+
+	  HERMES:
+	  Hermes-Pro ISDN/LAN router with integrated 8 x hub
+	  Manufacturer: Multidata Gesellschaft für Datentechnik und Informatik
+	  <http://www.multidata.de/>
+	  Date of Release: 2000 (?)
+	  End of life: -
+	  URL: <http://www.multidata.de/english/products/hpro.htm>
+
+	  IP860:
+	  VMEBus IP (Industry Pack) carrier board with MPC860
+	  Manufacturer: MicroSys GmbH, <http://www.microsys.de/>
+	  Date of Release: ?
+	  End of life: -
+	  URL: <http://www.microsys.de/html/ip860.html>
+
+	  PCU_E:
+	  PCU = Peripheral Controller Unit, Extended
+	  Manufacturer: Siemens AG, ICN (Information and Communication Networks)
+	  	<http://www.siemens.de/page/1,3771,224315-1-999_2_226207-0,00.html>
+	  Date of Release: April 2001
+	  End of life: August 2001
+	  URL: n. a.
+
+config RPXCLASSIC
+	bool "RPX-Classic"
+	help
+	  The RPX-Classic is a single-board computer based on the Motorola
+	  MPC860.  It features 16MB of DRAM and a variable amount of flash,
+	  I2C EEPROM, thermal monitoring, a PCMCIA slot, a DIP switch and two
+	  LEDs.  Variants with Ethernet ports exist.  Say Y here to support it
+	  directly.
+
+config BSEIP
+	bool "BSE-IP"
+	help
+	  Say Y here to support the Bright Star Engineering ipEngine SBC.
+	  This is a credit-card-sized device featuring a MPC823 processor,
+	  26MB DRAM, 4MB flash, Ethernet, a 16K-gate FPGA, USB, an LCD/video
+	  controller, and two RS232 ports.
+
+config FADS
+	bool "FADS"
+
+config TQM823L
+	bool "TQM823L"
+	help
+	  Say Y here to support the TQM823L, one of an MPC8xx-based family of
+	  mini SBCs (half credit-card size) from TQ Components first released
+	  in late 1999.  Technical references are at
+	  <http://www.denx.de/PDF/TQM8xxLHWM201.pdf>, and
+	  <http://www.denx.de/PDF/STK8xxLHWM201.pdf>, and an image at
+	  <http://www.denx.de/embedded-ppc-en.html>.
+
+config TQM850L
+	bool "TQM850L"
+	help
+	  Say Y here to support the TQM850L, one of an MPC8xx-based family of
+	  mini SBCs (half credit-card size) from TQ Components first released
+	  in late 1999.  Technical references are at
+	  <http://www.denx.de/PDF/TQM8xxLHWM201.pdf>, and
+	  <http://www.denx.de/PDF/STK8xxLHWM201.pdf>, and an image at
+	  <http://www.denx.de/embedded-ppc-en.html>.
+
+config TQM855L
+	bool "TQM855L"
+	help
+	  Say Y here to support the TQM855L, one of an MPC8xx-based family of
+	  mini SBCs (half credit-card size) from TQ Components first released
+	  in late 1999.  Technical references are at
+	  <http://www.denx.de/PDF/TQM8xxLHWM201.pdf>, and
+	  <http://www.denx.de/PDF/STK8xxLHWM201.pdf>, and an image at
+	  <http://www.denx.de/embedded-ppc-en.html>.
+
+config TQM860L
+	bool "TQM860L"
+	help
+	  Say Y here to support the TQM860L, one of an MPC8xx-based family of
+	  mini SBCs (half credit-card size) from TQ Components first released
+	  in late 1999.  Technical references are at
+	  <http://www.denx.de/PDF/TQM8xxLHWM201.pdf>, and
+	  <http://www.denx.de/PDF/STK8xxLHWM201.pdf>, and an image at
+	  <http://www.denx.de/embedded-ppc-en.html>.
+
+config FPS850L
+	bool "FPS850L"
+
+config SPD823TS
+	bool "SPD823TS"
+	help
+	  Say Y here to support the Speech Design 823 Tele-Server from Speech
+	  Design, released in 2000.  The manufacturer's website is at
+	  <http://www.speech-design.de/>.
+
+config IVMS8
+	bool "IVMS8"
+	help
+	  Say Y here to support the Integrated Voice-Mail Small 8-channel SBC
+	  from Speech Design, released March 2001.  The manufacturer's website
+	  is at <http://www.speech-design.de/>.
+
+config IVML24
+	bool "IVML24"
+	help
+	  Say Y here to support the Integrated Voice-Mail Large 24-channel SBC
+	  from Speech Design, released March 2001.  The manufacturer's website
+	  is at <http://www.speech-design.de/>.
+
+config SM850
+	bool "SM850"
+	help
+	  Say Y here to support the Service Module 850 from Dependable
+	  Computer Systems, an SBC based on the TQM850L module by TQ
+	  Components.  This board is no longer in production.  The
+	  manufacturer's website is at <http://www.decomsys.com/>.
+
+config HERMES_PRO
+	bool "HERMES"
+
+config IP860
+	bool "IP860"
+
+config LWMON
+	bool "LWMON"
+
+config PCU_E
+	bool "PCU_E"
+
+config CCM
+	bool "CCM"
+
+config LANTEC
+	bool "LANTEC"
+
+config MBX
+	bool "MBX"
+	help
+	  MBX is a line of Motorola single-board computer based around the
+	  MPC821 and MPC860 processors, and intended for embedded-controller
+	  applications.  Say Y here to support these boards directly.
+
+config WINCEPT
+	bool "WinCept"
+	help
+	  The Wincept 100/110 is a Motorola single-board computer based on the
+	  MPC821 PowerPC, introduced in 1998 and designed to be used in
+	  thin-client machines.  Say Y to support it directly.
+
+endchoice
+
+choice
+	prompt "Machine Type"
+	depends on 6xx || POWER3 || POWER4
+	default PPC_MULTIPLATFORM
+	---help---
+	  Linux currently supports several different kinds of PowerPC-based
+	  machines: Apple Power Macintoshes and clones (such as the Motorola
+	  Starmax series), PReP (PowerPC Reference Platform) machines (such
+	  as the Motorola PowerStacks, Motorola cPCI/VME embedded systems,
+	  and some IBM RS/6000 systems), CHRP (Common Hardware Reference
+	  Platform) machines (including all of the recent IBM RS/6000 and
+	  pSeries machines), and several embedded PowerPC systems containing
+	  4xx, 6xx, 7xx, 8xx, 74xx, and 82xx processors.  Currently, the
+	  default option is to build a kernel which works on the first three.
+
+	  Select CHRP/PowerMac/PReP if configuring for an IBM RS/6000 or
+	  pSeries machine, a Power Macintosh (including iMacs, iBooks and
+	  Powerbooks), or a PReP machine.
+
+	  Select Gemini if configuring for a Synergy Microsystems' Gemini
+	  series Single Board Computer.  More information is available at:
+	  <http://www.synergymicro.com/PressRel/97_10_15.html>.
+
+	  Select APUS if configuring for a PowerUP Amiga.  More information is
+	  available at: <http://linux-apus.sourceforge.net/>.
+
+config PPC_MULTIPLATFORM
+	bool "CHRP/PowerMac/PReP"
+
+config APUS
+	bool "Amiga-APUS"
+	help
+	  Select APUS if configuring for a PowerUP Amiga.
+	  More information is available at:
+	  <http://linux-apus.sourceforge.net/>.
+
+config WILLOW
+	bool "Cogent-Willow"
+
+config PCORE
+	bool "Force-PowerCore"
+
+config POWERPMC250
+	bool "Force-PowerPMC250"
+
+config EV64260
+	bool "Galileo-EV-64260-BP"
+
+config SPRUCE
+	bool "IBM-Spruce"
+
+config LOPEC
+	bool "Motorola-LoPEC"
+
+config MCPN765
+	bool "Motorola-MCPN765"
+
+config MVME5100
+	bool "Motorola-MVME5100"
+
+config PPLUS
+	bool "Motorola-PowerPlus"
+
+config PRPMC750
+	bool "Motorola-PrPMC750"
+
+config PRPMC800
+	bool "Motorola-PrPMC800"
+
+config SANDPOINT
+	bool "Motorola-Sandpoint"
+	help
+	  Select SANDPOINT if configuring for a Motorola Sandpoint X3
+	  (any flavor).
+
+config ADIR
+	bool "SBS-Adirondack"
+
+config K2
+	bool "SBS-K2"
+
+config PAL4
+	bool "SBS-Palomar4"
+
+config GEMINI
+	bool "Synergy-Gemini"
+	help
+	  Select Gemini if configuring for a Synergy Microsystems' Gemini
+	  series Single Board Computer.  More information is available at:
+	  <http://www.synergymicro.com/PressRel/97_10_15.html>.
+
+config EST8260
+	bool "EST8260"
+	---help---
+	  The EST8260 is a single-board computer manufactured by Wind River
+	  Systems, Inc. (formerly Embedded Support Tools Corp.) and based on
+	  the MPC8260.  Wind River Systems has a website at
+	  <http://www.windriver.com/>, but the EST8260 cannot be found on it
+	  and has probably been discontinued or rebadged.
+
+config SBC82xx
+	bool "SBC82xx"
+	---help---
+	  SBC PowerQUICC II, single-board computer with MPC82xx CPU
+	  Manufacturer: Wind River Systems, Inc.
+	  Date of Release: May 2003
+	  End of Life: -
+	  URL: <http://www.windriver.com/>
+
+config SBS8260
+	bool "SBS8260"
+
+config RPX8260
+	bool "RPXSUPER"
+
+config TQM8260
+	bool "TQM8260"
+	---help---
+	  MPC8260 based module, little larger than credit card,
+	  up to 128 MB global + 64 MB local RAM, 32 MB Flash,
+	  32 kB EEPROM, 256 kB L@ Cache, 10baseT + 100baseT Ethernet,
+	  2 x serial ports, ...
+	  Manufacturer: TQ Components, www.tq-group.de
+	  Date of Release: June 2001
+	  End of Life: not yet :-)
+	  URL: <http://www.denx.de/PDF/TQM82xx_SPEC_Rev005.pdf>
+
+config ADS8272
+	bool "ADS8272"
+
+config LITE5200
+	bool "Freescale LITE5200 / (IceCube)"
+	select PPC_MPC52xx
+	help
+	  Support for the LITE5200 dev board for the MPC5200 from Freescale.
+	  This is for the LITE5200 version 2.0 board. Don't know if it changes
+	  much but it's only been tested on this board version. I think this
+	  board is also known as IceCube.
+
+endchoice
+
+config PQ2ADS
+	bool
+	depends on ADS8272
+	default y
+
+config TQM8xxL
+	bool
+	depends on 8xx && (TQM823L || TQM850L || FPS850L || TQM855L || TQM860L || SM850)
+	default y
+
+config EMBEDDEDBOOT
+	bool
+	depends on 8xx || 8260
+	default y
+	
+config PPC_MPC52xx
+	bool
+
+config 8260
+	bool "CPM2 Support" if WILLOW
+	depends on 6xx
+	default y if TQM8260 || RPX8260 || EST8260 || SBS8260 || SBC82xx
+	help
+	  The MPC8260 is a typical embedded CPU made by Motorola.  Selecting
+	  this option means that you wish to build a kernel for a machine with
+	  an 8260 class CPU.
+
+config 8272
+	bool
+	depends on 6xx
+	default y if ADS8272
+	select 8260
+	help
+	  The MPC8272 CPM has a different internal dpram setup than other CPM2
+	  devices
+
+config CPM2
+	bool
+	depends on 8260 || MPC8560
+	default y
+	help
+	  The CPM2 (Communications Processor Module) is a coprocessor on
+	  embedded CPUs made by Motorola.  Selecting this option means that
+	  you wish to build a kernel for a machine with a CPM2 coprocessor
+	  on it (826x, 827x, 8560).
+
+config PPC_CHRP
+	bool
+	depends on PPC_MULTIPLATFORM
+	default y
+
+config PPC_PMAC
+	bool
+	depends on PPC_MULTIPLATFORM
+	default y
+
+config PPC_PMAC64
+	bool
+	depends on PPC_PMAC && POWER4
+	default y
+
+config PPC_PREP
+	bool
+	depends on PPC_MULTIPLATFORM
+	default y
+
+config PPC_OF
+	bool
+	depends on PPC_PMAC || PPC_CHRP
+	default y
+
+config PPC_GEN550
+	bool
+	depends on SANDPOINT || MCPN765 || SPRUCE || PPLUS || PCORE || PRPMC750 || K2 || PRPMC800
+	default y
+
+config FORCE
+	bool
+	depends on 6xx && (PCORE || POWERPMC250)
+	default y
+
+config GT64260
+	bool
+	depends on EV64260
+	default y
+
+config NONMONARCH_SUPPORT
+	bool "Enable Non-Monarch Support"
+	depends on PRPMC800
+
+config HARRIER
+	bool
+	depends on PRPMC800
+	default y
+
+config EPIC_SERIAL_MODE
+	bool
+	depends on 6xx && (LOPEC || SANDPOINT)
+	default y
+
+config MPC10X_BRIDGE
+	bool
+	depends on PCORE || POWERPMC250 || LOPEC || SANDPOINT
+	default y
+
+config FSL_OCP
+	bool
+	depends on MPC10X_BRIDGE || PPC_MPC52xx
+	default y
+
+config MPC10X_OPENPIC
+	bool
+	depends on POWERPMC250 || LOPEC || SANDPOINT
+	default y
+
+config MPC10X_STORE_GATHERING
+	bool "Enable MPC10x store gathering"
+	depends on MPC10X_BRIDGE
+
+config CPC710_DATA_GATHERING
+	bool "Enable CPC710 data gathering"
+	depends on K2
+
+config HARRIER_STORE_GATHERING
+	bool "Enable Harrier store gathering"
+	depends on HARRIER
+
+config MVME5100_IPMC761_PRESENT
+	bool "MVME5100 configured with an IPMC761"
+	depends on MVME5100
+
+config SPRUCE_BAUD_33M
+	bool "Spruce baud clock support"
+	depends on SPRUCE
+
+config PC_KEYBOARD
+	bool "PC PS/2 style Keyboard"
+	depends on 4xx || CPM2
+
+config SERIAL_CONSOLE
+	bool
+	depends on 8xx
+	default y
+
+config SERIAL_CONSOLE_BAUD
+	int
+	depends on EV64260
+	default "115200"
+
+config PPCBUG_NVRAM
+	bool "Enable reading PPCBUG NVRAM during boot" if PPLUS || LOPEC
+	default y if PPC_PREP
+
+config SMP
+	bool "Symmetric multi-processing support"
+	---help---
+	  This enables support for systems with more than one CPU. If you have
+	  a system with only one CPU, say N. If you have a system with more
+	  than one CPU, say Y.  Note that the kernel does not currently
+	  support SMP machines with 603/603e/603ev or PPC750 ("G3") processors
+	  since they have inadequate hardware support for multiprocessor
+	  operation.
+
+	  If you say N here, the kernel will run on single and multiprocessor
+	  machines, but will use only one CPU of a multiprocessor machine. If
+	  you say Y here, the kernel will run on single-processor machines.
+	  On a single-processor machine, the kernel will run faster if you say
+	  N here.
+
+	  If you don't know what to do here, say N.
+
+config IRQ_ALL_CPUS
+	bool "Distribute interrupts on all CPUs by default"
+	depends on SMP
+	help
+	  This option gives the kernel permission to distribute IRQs across
+	  multiple CPUs.  Saying N here will route all IRQs to the first
+	  CPU.  Generally saying Y is safe, although some problems have been
+	  reported with SMP Power Macintoshes with this option enabled.
+
+config NR_CPUS
+	int "Maximum number of CPUs (2-32)"
+	range 2 32
+	depends on SMP
+	default "4"
+
+config PREEMPT
+	bool "Preemptible Kernel"
+	help
+	  This option reduces the latency of the kernel when reacting to
+	  real-time or interactive events by allowing a low priority process to
+	  be preempted even if it is in kernel mode executing a system call.
+
+	  Say Y here if you are building a kernel for a desktop, embedded
+	  or real-time system.  Say N if you are unsure.
+
+config HIGHMEM
+	bool "High memory support"
+
+config KERNEL_ELF
+	bool
+	default y
+
+source "fs/Kconfig.binfmt"
+
+config PROC_DEVICETREE
+	bool "Support for Open Firmware device tree in /proc"
+	depends on PPC_OF && PROC_FS
+	help
+	  This option adds a device-tree directory under /proc which contains
+	  an image of the device tree that the kernel copies from Open
+	  Firmware. If unsure, say Y here.
+
+config PREP_RESIDUAL
+	bool "Support for PReP Residual Data"
+	depends on PPC_PREP
+	help
+	  Some PReP systems have residual data passed to the kernel by the
+	  firmware.  This allows detection of memory size, devices present and
+	  other useful pieces of information.  Sometimes this information is
+	  not present or incorrect.
+
+	  Unless you expect to boot on a PReP system, there is no need to
+	  select Y.
+
+config PROC_PREPRESIDUAL
+	bool "Support for reading of PReP Residual Data in /proc"
+	depends on PREP_RESIDUAL && PROC_FS
+	help
+	  Enabling this option will create a /proc/residual file which allows
+	  you to get at the residual data on PReP systems.  You will need a tool
+	  (lsresidual) to parse it.  If you aren't on a PReP system, you don't
+	  want this.
+
+config CMDLINE_BOOL
+	bool "Default bootloader kernel arguments"
+
+config CMDLINE
+	string "Initial kernel command string"
+	depends on CMDLINE_BOOL
+	default "console=ttyS0,9600 console=tty0 root=/dev/sda2"
+	help
+	  On some platforms, there is currently no way for the boot loader to
+	  pass arguments to the kernel. For these platforms, you can supply
+	  some command-line options at build time by entering them here.  In
+	  most cases you will need to specify the root device here.
+
+config AMIGA
+	bool
+	depends on APUS
+	default y
+	help
+	  This option enables support for the Amiga series of computers.
+
+config ZORRO
+	bool
+	depends on APUS
+	default y
+	help
+	  This enables support for the Zorro bus in the Amiga. If you have
+	  expansion cards in your Amiga that conform to the Amiga
+	  AutoConfig(tm) specification, say Y, otherwise N. Note that even
+	  expansion cards that do not fit in the Zorro slots but fit in e.g.
+	  the CPU slot may fall in this category, so you have to say Y to let
+	  Linux use these.
+
+config ABSTRACT_CONSOLE
+	bool
+	depends on APUS
+	default y
+
+config APUS_FAST_EXCEPT
+	bool
+	depends on APUS
+	default y
+
+config AMIGA_PCMCIA
+	bool "Amiga 1200/600 PCMCIA support"
+	depends on APUS && EXPERIMENTAL
+	help
+	  Include support in the kernel for pcmcia on Amiga 1200 and Amiga
+	  600. If you intend to use pcmcia cards say Y; otherwise say N.
+
+config AMIGA_BUILTIN_SERIAL
+	tristate "Amiga builtin serial support"
+	depends on APUS
+	help
+	  If you want to use your Amiga's built-in serial port in Linux,
+	  answer Y.
+
+	  To compile this driver as a module, choose M here.
+
+config GVPIOEXT
+	tristate "GVP IO-Extender support"
+	depends on APUS
+	help
+	  If you want to use a GVP IO-Extender serial card in Linux, say Y.
+	  Otherwise, say N.
+
+config GVPIOEXT_LP
+	tristate "GVP IO-Extender parallel printer support"
+	depends on GVPIOEXT
+	help
+	  Say Y to enable driving a printer from the parallel port on your
+	  GVP IO-Extender card, N otherwise.
+
+config GVPIOEXT_PLIP
+	tristate "GVP IO-Extender PLIP support"
+	depends on GVPIOEXT
+	help
+	  Say Y to enable doing IP over the parallel port on your GVP
+	  IO-Extender card, N otherwise.
+
+config MULTIFACE_III_TTY
+	tristate "Multiface Card III serial support"
+	depends on APUS
+	help
+	  If you want to use a Multiface III card's serial port in Linux,
+	  answer Y.
+
+	  To compile this driver as a module, choose M here.
+
+config A2232
+	tristate "Commodore A2232 serial support (EXPERIMENTAL)"
+	depends on EXPERIMENTAL && APUS
+	---help---
+	  This option supports the 2232 7-port serial card shipped with the
+	  Amiga 2000 and other Zorro-bus machines, dating from 1989.  At
+	  a max of 19,200 bps, the ports are served by a 6551 ACIA UART chip
+	  each, plus a 8520 CIA, and a master 6502 CPU and buffer as well. The
+	  ports were connected with 8 pin DIN connectors on the card bracket,
+	  for which 8 pin to DB25 adapters were supplied. The card also had
+	  jumpers internally to toggle various pinning configurations.
+
+	  This driver can be built as a module; but then "generic_serial"
+	  will also be built as a module. This has to be loaded before
+	  "ser_a2232". If you want to do this, answer M here.
+
+config WHIPPET_SERIAL
+	tristate "Hisoft Whippet PCMCIA serial support"
+	depends on AMIGA_PCMCIA
+	help
+	  HiSoft has a web page at <http://www.hisoft.co.uk/>, but there
+	  is no listing for the Whippet in their Amiga section.
+
+config APNE
+	tristate "PCMCIA NE2000 support"
+	depends on AMIGA_PCMCIA
+	help
+	  If you have a PCMCIA NE2000 compatible adapter, say Y.  Otherwise,
+	  say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called apne.
+
+config SERIAL_CONSOLE
+	bool "Support for serial port console"
+	depends on APUS && (AMIGA_BUILTIN_SERIAL=y || GVPIOEXT=y || MULTIFACE_III_TTY=y)
+
+config HEARTBEAT
+	bool "Use power LED as a heartbeat"
+	depends on APUS
+	help
+	  Use the power-on LED on your machine as a load meter.  The exact
+	  behavior is platform-dependent, but normally the flash frequency is
+	  a hyperbolic function of the 5-minute load average.
+
+config PROC_HARDWARE
+	bool "/proc/hardware support"
+	depends on APUS
+
+source "drivers/zorro/Kconfig"
+
+endmenu
+
+menu "Bus options"
+
+config ISA
+	bool "Support for ISA-bus hardware"
+	depends on PPC_PREP || PPC_CHRP
+	help
+	  Find out whether you have ISA slots on your motherboard.  ISA is the
+	  name of a bus system, i.e. the way the CPU talks to the other stuff
+	  inside your box.  If you have an Apple machine, say N here; if you
+	  have an IBM RS/6000 or pSeries machine or a PReP machine, say Y.  If
+	  you have an embedded board, consult your board documentation.
+
+config GENERIC_ISA_DMA
+	bool
+	depends on POWER3 || POWER4 || 6xx && !CPM2
+	default y
+
+config EISA
+	bool
+	help
+	  The Extended Industry Standard Architecture (EISA) bus is a bus
+	  architecture used on some older intel-based PCs.
+
+config SBUS
+	bool
+
+# Yes MCA RS/6000s exist but Linux-PPC does not currently support any
+config MCA
+	bool
+
+config PCI
+	bool "PCI support" if 40x || CPM2 || 85xx
+	default y if !40x && !CPM2 && !8xx && !APUS && !85xx
+	default PCI_PERMEDIA if !4xx && !CPM2 && !8xx && APUS
+	default PCI_QSPAN if !4xx && !CPM2 && 8xx
+	help
+	  Find out whether your system includes a PCI bus. PCI is the name of
+	  a bus system, i.e. the way the CPU talks to the other stuff inside
+	  your box.  If you say Y here, the kernel will include drivers and
+	  infrastructure code to support PCI bus devices.
+
+config PCI_DOMAINS
+	bool
+	default PCI
+
+config PCI_QSPAN
+	bool "QSpan PCI"
+	depends on !4xx && !CPM2 && 8xx
+	help
+	  Say Y here if you have a system based on a Motorola 8xx-series
+	  embedded processor with a QSPAN PCI interface, otherwise say N.
+
+config PCI_8260
+	bool
+	depends on PCI && 8260 && !8272
+	default y
+	
+config 8260_PCI9
+	bool "  Enable workaround for MPC826x erratum PCI 9"
+	depends on PCI_8260
+	default y
+
+choice 
+	prompt "  IDMA channel for PCI 9 workaround"
+	depends on 8260_PCI9
+
+config 8260_PCI9_IDMA1
+	bool "IDMA1"
+
+config 8260_PCI9_IDMA2
+	bool "IDMA2"
+
+config 8260_PCI9_IDMA3
+	bool "IDMA3"
+
+config 8260_PCI9_IDMA4
+	bool "IDMA4"
+
+endchoice
+
+config PCI_PERMEDIA
+	bool "PCI for Permedia2"
+	depends on !4xx && !8xx && APUS
+
+source "drivers/pci/Kconfig"
+
+source "drivers/pcmcia/Kconfig"
+
+endmenu
+
+menu "Advanced setup"
+
+config ADVANCED_OPTIONS
+	bool "Prompt for advanced kernel configuration options"
+	help
+	  This option will enable prompting for a variety of advanced kernel
+	  configuration options.  These options can cause the kernel to not
+	  work if they are set incorrectly, but can be used to optimize certain
+	  aspects of kernel memory management.
+
+	  Unless you know what you are doing, say N here.
+
+comment "Default settings for advanced configuration options are used"
+	depends on !ADVANCED_OPTIONS
+
+config HIGHMEM_START_BOOL
+	bool "Set high memory pool address"
+	depends on ADVANCED_OPTIONS && HIGHMEM
+	help
+	  This option allows you to set the base address of the kernel virtual
+	  area used to map high memory pages.  This can be useful in
+	  optimizing the layout of kernel virtual memory.
+
+	  Say N here unless you know what you are doing.
+
+config HIGHMEM_START
+	hex "Virtual start address of high memory pool" if HIGHMEM_START_BOOL
+	default "0xfe000000"
+
+config LOWMEM_SIZE_BOOL
+	bool "Set maximum low memory"
+	depends on ADVANCED_OPTIONS
+	help
+	  This option allows you to set the maximum amount of memory which
+	  will be used as "low memory", that is, memory which the kernel can
+	  access directly, without having to set up a kernel virtual mapping.
+	  This can be useful in optimizing the layout of kernel virtual
+	  memory.
+
+	  Say N here unless you know what you are doing.
+
+config LOWMEM_SIZE
+	hex "Maximum low memory size (in bytes)" if LOWMEM_SIZE_BOOL
+	default "0x30000000"
+
+config KERNEL_START_BOOL
+	bool "Set custom kernel base address"
+	depends on ADVANCED_OPTIONS
+	help
+	  This option allows you to set the kernel virtual address at which
+	  the kernel will map low memory (the kernel image will be linked at
+	  this address).  This can be useful in optimizing the virtual memory
+	  layout of the system.
+
+	  Say N here unless you know what you are doing.
+
+config KERNEL_START
+	hex "Virtual address of kernel base" if KERNEL_START_BOOL
+	default "0xc0000000"
+
+config TASK_SIZE_BOOL
+	bool "Set custom user task size"
+	depends on ADVANCED_OPTIONS
+	help
+	  This option allows you to set the amount of virtual address space
+	  allocated to user tasks.  This can be useful in optimizing the
+	  virtual memory layout of the system.
+
+	  Say N here unless you know what you are doing.
+
+config TASK_SIZE
+	hex "Size of user task space" if TASK_SIZE_BOOL
+	default "0x80000000"
+
+config CONSISTENT_START_BOOL
+	bool "Set custom consistent memory pool address"
+	depends on ADVANCED_OPTIONS && NOT_COHERENT_CACHE
+	help
+	  This option allows you to set the base virtual address
+	  of the the consistent memory pool.  This pool of virtual
+	  memory is used to make consistent memory allocations.
+
+config CONSISTENT_START
+	hex "Base virtual address of consistent memory pool" if CONSISTENT_START_BOOL
+	default "0xff100000" if NOT_COHERENT_CACHE
+
+config CONSISTENT_SIZE_BOOL
+	bool "Set custom consistent memory pool size"
+	depends on ADVANCED_OPTIONS && NOT_COHERENT_CACHE
+	help
+	  This option allows you to set the size of the the
+	  consistent memory pool.  This pool of virtual memory
+	  is used to make consistent memory allocations.
+
+config CONSISTENT_SIZE
+	hex "Size of consistent memory pool" if CONSISTENT_SIZE_BOOL
+	default "0x00200000" if NOT_COHERENT_CACHE
+
+config BOOT_LOAD_BOOL
+	bool "Set the boot link/load address"
+	depends on ADVANCED_OPTIONS && !PPC_MULTIPLATFORM
+	help
+	  This option allows you to set the initial load address of the zImage
+	  or zImage.initrd file.  This can be useful if you are on a board
+	  which has a small amount of memory.
+
+	  Say N here unless you know what you are doing.
+
+config BOOT_LOAD
+	hex "Link/load address for booting" if BOOT_LOAD_BOOL
+	default "0x00400000" if 40x || 8xx || 8260
+	default "0x01000000" if 44x
+	default "0x00800000"
+
+config PIN_TLB
+	bool "Pinned Kernel TLBs (860 ONLY)"
+	depends on ADVANCED_OPTIONS && 8xx
+endmenu
+
+source "drivers/Kconfig"
+
+source "fs/Kconfig"
+
+source "arch/ppc/8xx_io/Kconfig"
+
+source "arch/ppc/8260_io/Kconfig"
+
+
+menu "IBM 40x options"
+	depends on 40x
+
+config SERIAL_SICC
+	bool "SICC Serial port"
+	depends on STB03xxx
+
+config UART1_DFLT_CONSOLE
+	bool
+	depends on SERIAL_SICC && UART0_TTYS1
+	default y
+
+config SERIAL_SICC_CONSOLE
+	bool
+	depends on SERIAL_SICC && UART0_TTYS1
+	default y
+
+endmenu
+
+source "lib/Kconfig"
+
+source "arch/ppc/oprofile/Kconfig"
+
+menu "Kernel hacking"
+
+config DEBUG_KERNEL
+	bool "Kernel debugging"
+
+config DEBUG_SLAB
+	bool "Debug memory allocations"
+	depends on DEBUG_KERNEL
+
+config MAGIC_SYSRQ
+	bool "Magic SysRq key"
+	depends on DEBUG_KERNEL
+	help
+	  If you say Y here, you will have some control over the system even
+	  if the system crashes for example during kernel debugging (e.g., you
+	  will be able to flush the buffer cache to disk, reboot the system
+	  immediately or dump some status information). This is accomplished
+	  by pressing various keys while holding SysRq (Alt+PrintScreen). It
+	  also works on a serial console (on PC hardware at least), if you
+	  send a BREAK and then within 5 seconds a command keypress. The
+	  keys are documented in <file:Documentation/sysrq.txt>. Don't say Y
+	  unless you really know what this hack does.
+
+config DEBUG_SPINLOCK
+	bool "Spinlock debugging"
+	depends on DEBUG_KERNEL
+	help
+	  Say Y here and to CONFIG_SMP to include code to check for missing
+	  spinlock initialization and some other common spinlock errors.
+
+config DEBUG_HIGHMEM
+	bool "Highmem debugging"
+	depends on DEBUG_KERNEL && HIGHMEM
+	help
+	  This options enables additional error checking for high memory
+	  systems.  Disable for production systems.
+
+config DEBUG_SPINLOCK_SLEEP
+	bool "Sleep-inside-spinlock checking"
+	depends on DEBUG_KERNEL
+	help
+	  If you say Y here, various routines which may sleep will become very
+	  noisy if they are called with a spinlock held.
+
+config KGDB
+	bool "Include kgdb kernel debugger"
+	depends on DEBUG_KERNEL && (BROKEN || PPC_GEN550 || 4xx)
+	select DEBUG_INFO
+	help
+	  Include in-kernel hooks for kgdb, the Linux kernel source level
+	  debugger.  See <http://kgdb.sourceforge.net/> for more information.
+	  Unless you are intending to debug the kernel, say N here.
+
+choice
+	prompt "Serial Port"
+	depends on KGDB
+	default KGDB_TTYS1
+
+config KGDB_TTYS0
+	bool "ttyS0"
+
+config KGDB_TTYS1
+	bool "ttyS1"
+
+config KGDB_TTYS2
+	bool "ttyS2"
+
+config KGDB_TTYS3
+	bool "ttyS3"
+
+endchoice
+
+config KGDB_CONSOLE
+	bool "Enable serial console thru kgdb port"
+	depends on KGDB && 8xx || CPM2
+	help
+	  If you enable this, all serial console messages will be sent
+	  over the gdb stub.
+	  If unsure, say N.
+
+config XMON
+	bool "Include xmon kernel debugger"
+	depends on DEBUG_KERNEL
+	help
+	  Include in-kernel hooks for the xmon kernel monitor/debugger.
+	  Unless you are intending to debug the kernel, say N here.
+
+config BDI_SWITCH
+	bool "Include BDI-2000 user context switcher"
+	depends on DEBUG_KERNEL
+	help
+	  Include in-kernel support for the Abatron BDI2000 debugger.
+	  Unless you are intending to debug the kernel with one of these
+	  machines, say N here.
+
+config DEBUG_INFO
+	bool "Compile the kernel with debug info"
+	depends on DEBUG_KERNEL
+	help
+          If you say Y here the resulting kernel image will include
+	  debugging info resulting in a larger kernel image.
+	  Say Y here only if you plan to use some sort of debugger to
+	  debug the kernel.
+	  If you don't debug the kernel, you can say N.
+
+config BOOTX_TEXT
+	bool "Support for early boot text console (BootX or OpenFirmware only)"
+	depends PPC_OF
+	help
+	  Say Y here to see progress messages from the boot firmware in text
+	  mode. Requires either BootX or Open Firmware.
+
+config SERIAL_TEXT_DEBUG
+	bool "Support for early boot texts over serial port"
+	depends on 4xx || GT64260 || LOPEC || PPLUS || PRPMC800 || PPC_GEN550 || PPC_MPC52xx
+
+config PPC_OCP
+	bool
+	depends on IBM_OCP || FSL_OCP
+	default y
+
+endmenu
+
+source "security/Kconfig"
+
+source "crypto/Kconfig"
diff -ubw -Naur linux-2.6.8.orig/arch/ppc/kernel/Makefile linux-2.6.8/arch/ppc/kernel/Makefile
--- linux-2.6.8.orig/arch/ppc/kernel/Makefile	2004-08-14 07:37:40.000000000 +0200
+++ linux-2.6.8/arch/ppc/kernel/Makefile	2004-08-14 18:23:39.943585616 +0200
@@ -16,6 +16,7 @@
 					semaphore.o syscalls.o setup.o \
 					cputable.o ppc_htab.o
 obj-$(CONFIG_6xx)		+= l2cr.o cpu_setup_6xx.o
+obj-$(CONFIG_PM_DISK)		+= pmdisk.o
 obj-$(CONFIG_POWER4)		+= cpu_setup_power4.o
 obj-$(CONFIG_MODULES)		+= module.o ppc_ksyms.o
 obj-$(CONFIG_NOT_COHERENT_CACHE)	+= dma-mapping.o
diff -ubw -Naur linux-2.6.8.orig/arch/ppc/kernel/Makefile.orig linux-2.6.8/arch/ppc/kernel/Makefile.orig
--- linux-2.6.8.orig/arch/ppc/kernel/Makefile.orig	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.8/arch/ppc/kernel/Makefile.orig	2004-08-14 07:37:40.000000000 +0200
@@ -0,0 +1,31 @@
+#
+# Makefile for the linux kernel.
+#
+
+extra-$(CONFIG_PPC_STD_MMU)	:= head.o
+extra-$(CONFIG_40x)		:= head_4xx.o
+extra-$(CONFIG_44x)		:= head_44x.o
+extra-$(CONFIG_E500)		:= head_e500.o
+extra-$(CONFIG_8xx)		:= head_8xx.o
+extra-$(CONFIG_6xx)		+= idle_6xx.o
+extra-$(CONFIG_POWER4)		+= idle_power4.o
+extra-y				+= vmlinux.lds.s
+
+obj-y				:= entry.o traps.o irq.o idle.o time.o misc.o \
+					process.o signal.o ptrace.o align.o \
+					semaphore.o syscalls.o setup.o \
+					cputable.o ppc_htab.o
+obj-$(CONFIG_6xx)		+= l2cr.o cpu_setup_6xx.o
+obj-$(CONFIG_POWER4)		+= cpu_setup_power4.o
+obj-$(CONFIG_MODULES)		+= module.o ppc_ksyms.o
+obj-$(CONFIG_NOT_COHERENT_CACHE)	+= dma-mapping.o
+obj-$(CONFIG_PCI)		+= pci.o
+obj-$(CONFIG_KGDB)		+= ppc-stub.o
+obj-$(CONFIG_SMP)		+= smp.o smp-tbsync.o
+obj-$(CONFIG_TAU)		+= temp.o
+obj-$(CONFIG_ALTIVEC)		+= vecemu.o vector.o
+
+ifdef CONFIG_MATH_EMULATION
+obj-$(CONFIG_8xx)		+= softemu8xx.o
+endif
+
diff -ubw -Naur linux-2.6.8.orig/arch/ppc/kernel/pmdisk.S linux-2.6.8/arch/ppc/kernel/pmdisk.S
--- linux-2.6.8.orig/arch/ppc/kernel/pmdisk.S	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.8/arch/ppc/kernel/pmdisk.S	2004-08-14 18:23:39.948584856 +0200
@@ -0,0 +1,358 @@
+#include <linux/config.h>
+#include <linux/threads.h>
+#include <asm/processor.h>
+#include <asm/page.h>
+#include <asm/cputable.h>
+#include <asm/thread_info.h>
+#include <asm/ppc_asm.h>
+#include <asm/offsets.h>
+
+
+/*
+ * Structure for storing CPU registers on the save area.
+ */
+#define SL_SP		0
+#define SL_PC		4
+#define SL_MSR		8
+#define SL_SDR1		0xc
+#define SL_SPRG0	0x10	/* 4 sprg's */
+#define SL_DBAT0	0x20
+#define SL_IBAT0	0x28
+#define SL_DBAT1	0x30
+#define SL_IBAT1	0x38
+#define SL_DBAT2	0x40
+#define SL_IBAT2	0x48
+#define SL_DBAT3	0x50
+#define SL_IBAT3	0x58
+#define SL_TB		0x60
+#define SL_R2		0x68
+#define SL_CR		0x6c
+#define SL_LR		0x70
+#define SL_R12		0x74	/* r12 to r31 */
+#define SL_SIZE		(SL_R12 + 80)
+
+	.section .data
+	.align	5
+
+_GLOBAL(pmdisk_save_area)
+	.space	SL_SIZE
+
+
+	.section .text
+	.align	5
+
+_GLOBAL(pmdisk_arch_suspend)
+	cmpi	0,r3,0
+	bne	do_resume
+
+	lis	r11,pmdisk_save_area@h
+	ori	r11,r11,pmdisk_save_area@l
+
+	mflr	r0
+	stw	r0,SL_LR(r11)
+	mfcr	r0
+	stw	r0,SL_CR(r11)
+	stw	r1,SL_SP(r11)
+	stw	r2,SL_R2(r11)
+	stmw	r12,SL_R12(r11)
+
+	/* Save MSR & SDR1 */
+	mfmsr	r4
+	stw	r4,SL_MSR(r11)
+	mfsdr1	r4
+	stw	r4,SL_SDR1(r11)
+
+	/* Get a stable timebase and save it */
+1:	mftbu	r4
+	stw	r4,SL_TB(r11)
+	mftb	r5
+	stw	r5,SL_TB+4(r11)
+	mftbu	r3
+	cmpw	r3,r4
+	bne	1b
+
+	/* Save SPRGs */
+	mfsprg	r4,0
+	stw	r4,SL_SPRG0(r11)
+	mfsprg	r4,1
+	stw	r4,SL_SPRG0+4(r11)
+	mfsprg	r4,2
+	stw	r4,SL_SPRG0+8(r11)
+	mfsprg	r4,3
+	stw	r4,SL_SPRG0+12(r11)
+
+	/* Save BATs */
+	mfdbatu	r4,0
+	stw	r4,SL_DBAT0(r11)
+	mfdbatl	r4,0
+	stw	r4,SL_DBAT0+4(r11)
+	mfdbatu	r4,1
+	stw	r4,SL_DBAT1(r11)
+	mfdbatl	r4,1
+	stw	r4,SL_DBAT1+4(r11)
+	mfdbatu	r4,2
+	stw	r4,SL_DBAT2(r11)
+	mfdbatl	r4,2
+	stw	r4,SL_DBAT2+4(r11)
+	mfdbatu	r4,3
+	stw	r4,SL_DBAT3(r11)
+	mfdbatl	r4,3
+	stw	r4,SL_DBAT3+4(r11)
+	mfibatu	r4,0
+	stw	r4,SL_IBAT0(r11)
+	mfibatl	r4,0
+	stw	r4,SL_IBAT0+4(r11)
+	mfibatu	r4,1
+	stw	r4,SL_IBAT1(r11)
+	mfibatl	r4,1
+	stw	r4,SL_IBAT1+4(r11)
+	mfibatu	r4,2
+	stw	r4,SL_IBAT2(r11)
+	mfibatl	r4,2
+	stw	r4,SL_IBAT2+4(r11)
+	mfibatu	r4,3
+	stw	r4,SL_IBAT3(r11)
+	mfibatl	r4,3
+	stw	r4,SL_IBAT3+4(r11)
+
+#if  0
+	/* Backup various CPU config stuffs */
+	bl	__save_cpu_setup
+#endif
+	/* Call the low level suspend stuff (we should probably have made
+	 * a stackframe...
+	 */
+	bl	pmdisk_suspend
+
+	/* Restore LR from the save area */
+	lis	r11,pmdisk_save_area@h
+	ori	r11,r11,pmdisk_save_area@l
+	lwz	r0,SL_LR(r11)
+	mtlr	r0
+
+	blr
+
+
+/* Resume code */
+do_resume:
+
+	/* Stop pending alitvec streams and memory accesses */
+BEGIN_FTR_SECTION
+	DSSALL
+END_FTR_SECTION_IFSET(CPU_FTR_ALTIVEC)
+ 	sync
+
+	/* Disable MSR:DR to make sure we don't take a TLB or
+	 * hash miss during the copy, as our hash table will
+	 * for a while be unuseable. For .text, we assume we are
+	 * covered by a BAT. This works only for non-G5 at this
+	 * point. G5 will need a better approach, possibly using
+	 * a small temporary hash table filled with large mappings,
+	 * disabling the MMU completely isn't a good option for
+	 * performance reasons.
+	 * (Note that 750's may have the same performance issue as
+	 * the G5 in this case, we should investigate using moving
+	 * BATs for these CPUs)
+	 */
+	mfmsr	r0
+	sync
+	rlwinm	r0,r0,0,28,26		/* clear MSR_DR */
+	mtmsr	r0
+	sync
+	isync
+
+	/* Load ptr the list of pages to copy in r3 */
+	lis	r11,(pm_pagedir_nosave - KERNELBASE)@h
+	ori	r11,r11,pm_pagedir_nosave@l
+	lwz	r10,0(r11)
+	tophys(r3,r10)
+
+	/* Load the count of pages to copy in r4 */
+	lis	r11,(pmdisk_pages - KERNELBASE)@h
+	ori	r11,r11,pmdisk_pages@l
+	lwz	r4,0(r11)
+
+
+	/* Copy the pages. This is a very basic implementation, to
+	 * be replaced by something more cache efficient */
+1:
+	li	r0,256
+	mtctr	r0
+	lwz	r11,0(r3)	/* source */
+	tophys(r5,r11)
+	lwz	r10,4(r3)	/* destination */
+	tophys(r6,r10)
+2:
+	lwz	r8,0(r5)
+	lwz	r9,4(r5)
+	lwz	r10,8(r5)
+	lwz	r11,12(r5)
+	addi	r5,r5,16
+	stw	r8,0(r6)
+	stw	r9,4(r6)
+	stw	r10,8(r6)
+	stw	r11,12(r6)
+	addi	r6,r6,16
+	bdnz	2b
+	addi	r3,r3,16
+	subi	r4,r4,1
+	cmpwi	0,r4,0
+	bne	1b
+
+	/* Do a very simple cache flush/inval of the L1 to ensure
+	 * coherency of the icache
+	 */
+	lis	r3,0x0002
+	mtctr	r3
+	li	r3, 0
+1:
+	lwz	r0,0(r3)
+	addi	r3,r3,0x0020
+	bdnz	1b
+	isync
+	sync
+
+	/* Now flush those cache lines */
+	lis	r3,0x0002
+	mtctr	r3
+	li	r3, 0
+1:
+	dcbf	0,r3
+	addi	r3,r3,0x0020
+	bdnz	1b
+	sync
+
+	/* Ok, we are now running with the kernel data of the old
+	 * kernel fully restored. We can get to the save area
+	 * easily now. As for the rest of the code, it assumes the
+	 * loader kernel and the booted one are exactly identical
+	 */
+	lis	r11,pmdisk_save_area@h
+	ori	r11,r11,pmdisk_save_area@l
+	tophys(r11,r11)
+
+#if 0
+	/* Restore various CPU config stuffs */
+	bl	__restore_cpu_setup
+#endif
+	/* Restore the BATs, and SDR1.  Then we can turn on the MMU.
+	 * This is a bit hairy as we are running out of those BATs,
+	 * but first, our code is probably in the icache, and we are
+	 * writing the same value to the BAT, so that should be fine,
+	 * though a better solution will have to be found long-term
+	 */
+	lwz	r4,SL_SDR1(r11)
+	mtsdr1	r4
+	lwz	r4,SL_SPRG0(r11)
+	mtsprg	0,r4
+	lwz	r4,SL_SPRG0+4(r11)
+	mtsprg	1,r4
+	lwz	r4,SL_SPRG0+8(r11)
+	mtsprg	2,r4
+	lwz	r4,SL_SPRG0+12(r11)
+	mtsprg	3,r4
+
+#if 0
+	lwz	r4,SL_DBAT0(r11)
+	mtdbatu	0,r4
+	lwz	r4,SL_DBAT0+4(r11)
+	mtdbatl	0,r4
+	lwz	r4,SL_DBAT1(r11)
+	mtdbatu	1,r4
+	lwz	r4,SL_DBAT1+4(r11)
+	mtdbatl	1,r4
+	lwz	r4,SL_DBAT2(r11)
+	mtdbatu	2,r4
+	lwz	r4,SL_DBAT2+4(r11)
+	mtdbatl	2,r4
+	lwz	r4,SL_DBAT3(r11)
+	mtdbatu	3,r4
+	lwz	r4,SL_DBAT3+4(r11)
+	mtdbatl	3,r4
+	lwz	r4,SL_IBAT0(r11)
+	mtibatu	0,r4
+	lwz	r4,SL_IBAT0+4(r11)
+	mtibatl	0,r4
+	lwz	r4,SL_IBAT1(r11)
+	mtibatu	1,r4
+	lwz	r4,SL_IBAT1+4(r11)
+	mtibatl	1,r4
+	lwz	r4,SL_IBAT2(r11)
+	mtibatu	2,r4
+	lwz	r4,SL_IBAT2+4(r11)
+	mtibatl	2,r4
+	lwz	r4,SL_IBAT3(r11)
+	mtibatu	3,r4
+	lwz	r4,SL_IBAT3+4(r11)
+	mtibatl	3,r4
+#endif
+
+BEGIN_FTR_SECTION
+	li	r4,0
+	mtspr	SPRN_DBAT4U,r4
+	mtspr	SPRN_DBAT4L,r4
+	mtspr	SPRN_DBAT5U,r4
+	mtspr	SPRN_DBAT5L,r4
+	mtspr	SPRN_DBAT6U,r4
+	mtspr	SPRN_DBAT6L,r4
+	mtspr	SPRN_DBAT7U,r4
+	mtspr	SPRN_DBAT7L,r4
+	mtspr	SPRN_IBAT4U,r4
+	mtspr	SPRN_IBAT4L,r4
+	mtspr	SPRN_IBAT5U,r4
+	mtspr	SPRN_IBAT5L,r4
+	mtspr	SPRN_IBAT6U,r4
+	mtspr	SPRN_IBAT6L,r4
+	mtspr	SPRN_IBAT7U,r4
+	mtspr	SPRN_IBAT7L,r4
+END_FTR_SECTION_IFSET(CPU_FTR_HAS_HIGH_BATS)
+
+	/* Flush all TLBs */
+	lis	r4,0x1000
+1:	addic.	r4,r4,-0x1000
+	tlbie	r4
+	blt	1b
+	sync
+
+	/* restore the MSR and turn on the MMU */
+	lwz	r3,SL_MSR(r11)
+	bl	turn_on_mmu
+	tovirt(r11,r11)
+
+	/* Restore TB */
+	li	r3,0
+	mttbl	r3
+	lwz	r3,SL_TB(r11)
+	lwz	r4,SL_TB+4(r11)
+	mttbu	r3
+	mttbl	r4
+
+	/* Kick decrementer */
+	li	r0,1
+	mtdec	r0
+
+	/* Restore the callee-saved registers and return */
+	lwz	r0,SL_CR(r11)
+	mtcr	r0
+	lwz	r2,SL_R2(r11)
+	lmw	r12,SL_R12(r11)
+	lwz	r1,SL_SP(r11)
+	lwz	r0,SL_LR(r11)
+	mtlr	r0
+
+	// XXX Note: we don't really need to call pmdisk_resume
+
+	li	r3,0
+	blr
+
+/* FIXME:This construct is actually not useful since we don't shut
+ * down the instruction MMU, we could just flip back MSR-DR on.
+ */
+turn_on_mmu:
+	mflr	r4
+	mtsrr0	r4
+	mtsrr1	r3
+	sync
+	isync
+	rfi
+
diff -ubw -Naur linux-2.6.8.orig/arch/ppc/kernel/signal.c linux-2.6.8/arch/ppc/kernel/signal.c
--- linux-2.6.8.orig/arch/ppc/kernel/signal.c	2004-08-14 07:38:08.000000000 +0200
+++ linux-2.6.8/arch/ppc/kernel/signal.c	2004-08-14 18:23:39.954583944 +0200
@@ -28,6 +28,7 @@
 #include <linux/elf.h>
 #include <linux/tty.h>
 #include <linux/binfmts.h>
+#include <linux/suspend.h>
 #include <asm/ucontext.h>
 #include <asm/uaccess.h>
 #include <asm/pgtable.h>
@@ -608,6 +609,14 @@
 	unsigned long frame, newsp;
 	int signr, ret;
 
+	if (current->flags & PF_FREEZE) {
+		refrigerator(PF_FREEZE);
+		signr = 0;
+		ret = regs->gpr[3];
+		if (!signal_pending(current))
+			goto no_signal;
+	}
+
 	if (!oldset)
 		oldset = &current->blocked;
 
@@ -632,6 +641,7 @@
 			regs->gpr[3] = EINTR;
 			/* note that the cr0.SO bit is already set */
 		} else {
+no_signal:
 			regs->nip -= 4;	/* Back up & retry system call */
 			regs->result = 0;
 			regs->trap = 0;
diff -ubw -Naur linux-2.6.8.orig/arch/ppc/kernel/signal.c.orig linux-2.6.8/arch/ppc/kernel/signal.c.orig
--- linux-2.6.8.orig/arch/ppc/kernel/signal.c.orig	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.8/arch/ppc/kernel/signal.c.orig	2004-08-14 07:38:08.000000000 +0200
@@ -0,0 +1,674 @@
+/*
+ *  arch/ppc/kernel/signal.c
+ *
+ *  PowerPC version
+ *    Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
+ *
+ *  Derived from "arch/i386/kernel/signal.c"
+ *    Copyright (C) 1991, 1992 Linus Torvalds
+ *    1997-11-28  Modified for POSIX.1b signals by Richard Henderson
+ *
+ *  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/sched.h>
+#include <linux/mm.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/wait.h>
+#include <linux/ptrace.h>
+#include <linux/unistd.h>
+#include <linux/stddef.h>
+#include <linux/elf.h>
+#include <linux/tty.h>
+#include <linux/binfmts.h>
+#include <asm/ucontext.h>
+#include <asm/uaccess.h>
+#include <asm/pgtable.h>
+#include <asm/cacheflush.h>
+
+#undef DEBUG_SIG
+
+#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
+
+extern void sigreturn_exit(struct pt_regs *);
+
+#define GP_REGS_SIZE	min(sizeof(elf_gregset_t), sizeof(struct pt_regs))
+
+int do_signal(sigset_t *oldset, struct pt_regs *regs);
+
+/*
+ * Atomically swap in the new signal mask, and wait for a signal.
+ */
+int
+sys_sigsuspend(old_sigset_t mask, int p2, int p3, int p4, int p6, int p7,
+	       struct pt_regs *regs)
+{
+	sigset_t saveset;
+
+	mask &= _BLOCKABLE;
+	spin_lock_irq(&current->sighand->siglock);
+	saveset = current->blocked;
+	siginitset(&current->blocked, mask);
+	recalc_sigpending();
+	spin_unlock_irq(&current->sighand->siglock);
+
+	regs->result = -EINTR;
+	regs->gpr[3] = EINTR;
+	regs->ccr |= 0x10000000;
+	while (1) {
+		current->state = TASK_INTERRUPTIBLE;
+		schedule();
+		if (do_signal(&saveset, regs))
+			sigreturn_exit(regs);
+	}
+}
+
+int
+sys_rt_sigsuspend(sigset_t __user *unewset, size_t sigsetsize, int p3, int p4,
+		  int p6, int p7, struct pt_regs *regs)
+{
+	sigset_t saveset, newset;
+
+	/* XXX: Don't preclude handling different sized sigset_t's.  */
+	if (sigsetsize != sizeof(sigset_t))
+		return -EINVAL;
+
+	if (copy_from_user(&newset, unewset, sizeof(newset)))
+		return -EFAULT;
+	sigdelsetmask(&newset, ~_BLOCKABLE);
+
+	spin_lock_irq(&current->sighand->siglock);
+	saveset = current->blocked;
+	current->blocked = newset;
+	recalc_sigpending();
+	spin_unlock_irq(&current->sighand->siglock);
+
+	regs->result = -EINTR;
+	regs->gpr[3] = EINTR;
+	regs->ccr |= 0x10000000;
+	while (1) {
+		current->state = TASK_INTERRUPTIBLE;
+		schedule();
+		if (do_signal(&saveset, regs))
+			sigreturn_exit(regs);
+	}
+}
+
+
+int
+sys_sigaltstack(const stack_t __user *uss, stack_t __user *uoss, int r5,
+		int r6, int r7, int r8, struct pt_regs *regs)
+{
+	return do_sigaltstack(uss, uoss, regs->gpr[1]);
+}
+
+int
+sys_sigaction(int sig, const struct old_sigaction __user *act,
+	      struct old_sigaction __user *oact)
+{
+	struct k_sigaction new_ka, old_ka;
+	int ret;
+
+	if (act) {
+		old_sigset_t mask;
+		if (verify_area(VERIFY_READ, act, sizeof(*act)) ||
+		    __get_user(new_ka.sa.sa_handler, &act->sa_handler) ||
+		    __get_user(new_ka.sa.sa_restorer, &act->sa_restorer))
+			return -EFAULT;
+		__get_user(new_ka.sa.sa_flags, &act->sa_flags);
+		__get_user(mask, &act->sa_mask);
+		siginitset(&new_ka.sa.sa_mask, mask);
+	}
+
+	ret = do_sigaction(sig, (act? &new_ka: NULL), (oact? &old_ka: NULL));
+
+	if (!ret && oact) {
+		if (verify_area(VERIFY_WRITE, oact, sizeof(*oact)) ||
+		    __put_user(old_ka.sa.sa_handler, &oact->sa_handler) ||
+		    __put_user(old_ka.sa.sa_restorer, &oact->sa_restorer))
+			return -EFAULT;
+		__put_user(old_ka.sa.sa_flags, &oact->sa_flags);
+		__put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask);
+	}
+
+	return ret;
+}
+
+/*
+ * When we have signals to deliver, we set up on the
+ * user stack, going down from the original stack pointer:
+ *	a sigregs struct
+ *	a sigcontext struct
+ *	a gap of __SIGNAL_FRAMESIZE bytes
+ *
+ * Each of these things must be a multiple of 16 bytes in size.
+ *
+ */
+struct sigregs {
+	struct mcontext	mctx;		/* all the register values */
+	/* Programs using the rs6000/xcoff abi can save up to 19 gp regs
+	   and 18 fp regs below sp before decrementing it. */
+	int		abigap[56];
+};
+
+/* We use the mc_pad field for the signal return trampoline. */
+#define tramp	mc_pad
+
+/*
+ *  When we have rt signals to deliver, we set up on the
+ *  user stack, going down from the original stack pointer:
+ *	one rt_sigframe struct (siginfo + ucontext + ABI gap)
+ *	a gap of __SIGNAL_FRAMESIZE+16 bytes
+ *  (the +16 is to get the siginfo and ucontext in the same
+ *  positions as in older kernels).
+ *
+ *  Each of these things must be a multiple of 16 bytes in size.
+ *
+ */
+struct rt_sigframe
+{
+	struct siginfo info;
+	struct ucontext uc;
+	/* Programs using the rs6000/xcoff abi can save up to 19 gp regs
+	   and 18 fp regs below sp before decrementing it. */
+	int		abigap[56];
+};
+
+/*
+ * Save the current user registers on the user stack.
+ * We only save the altivec/spe registers if the process has used
+ * altivec/spe instructions at some point.
+ */
+static int
+save_user_regs(struct pt_regs *regs, struct mcontext __user *frame, int sigret)
+{
+	/* save general and floating-point registers */
+	CHECK_FULL_REGS(regs);
+	preempt_disable();
+	if (regs->msr & MSR_FP)
+		giveup_fpu(current);
+#ifdef CONFIG_ALTIVEC
+	if (current->thread.used_vr && (regs->msr & MSR_VEC))
+		giveup_altivec(current);
+#endif /* CONFIG_ALTIVEC */
+#ifdef CONFIG_SPE
+	if (current->thread.used_spe && (regs->msr & MSR_SPE))
+		giveup_spe(current);
+#endif /* CONFIG_ALTIVEC */
+	preempt_enable();
+
+	if (__copy_to_user(&frame->mc_gregs, regs, GP_REGS_SIZE)
+	    || __copy_to_user(&frame->mc_fregs, current->thread.fpr,
+			      ELF_NFPREG * sizeof(double)))
+		return 1;
+
+	current->thread.fpscr = 0;	/* turn off all fp exceptions */
+
+#ifdef CONFIG_ALTIVEC
+	/* save altivec registers */
+	if (current->thread.used_vr) {
+		if (__copy_to_user(&frame->mc_vregs, current->thread.vr,
+				   ELF_NVRREG * sizeof(vector128)))
+			return 1;
+		/* set MSR_VEC in the saved MSR value to indicate that
+		   frame->mc_vregs contains valid data */
+		if (__put_user(regs->msr | MSR_VEC, &frame->mc_gregs[PT_MSR]))
+			return 1;
+	}
+	/* else assert((regs->msr & MSR_VEC) == 0) */
+
+	/* We always copy to/from vrsave, it's 0 if we don't have or don't
+	 * use altivec. Since VSCR only contains 32 bits saved in the least
+	 * significant bits of a vector, we "cheat" and stuff VRSAVE in the
+	 * most significant bits of that same vector. --BenH
+	 */
+	if (__put_user(current->thread.vrsave, (u32 __user *)&frame->mc_vregs[32]))
+		return 1;
+#endif /* CONFIG_ALTIVEC */
+
+#ifdef CONFIG_SPE
+	/* save spe registers */
+	if (current->thread.used_spe) {
+		if (__copy_to_user(&frame->mc_vregs, current->thread.evr,
+				   ELF_NEVRREG * sizeof(u32)))
+			return 1;
+		/* set MSR_SPE in the saved MSR value to indicate that
+		   frame->mc_vregs contains valid data */
+		if (__put_user(regs->msr | MSR_SPE, &frame->mc_gregs[PT_MSR]))
+			return 1;
+	}
+	/* else assert((regs->msr & MSR_SPE) == 0) */
+
+	/* We always copy to/from spefscr */
+	if (__put_user(current->thread.spefscr, (u32 *)&frame->mc_vregs + ELF_NEVRREG))
+		return 1;
+#endif /* CONFIG_SPE */
+
+	if (sigret) {
+		/* Set up the sigreturn trampoline: li r0,sigret; sc */
+		if (__put_user(0x38000000UL + sigret, &frame->tramp[0])
+		    || __put_user(0x44000002UL, &frame->tramp[1]))
+			return 1;
+		flush_icache_range((unsigned long) &frame->tramp[0],
+				   (unsigned long) &frame->tramp[2]);
+	}
+
+	return 0;
+}
+
+/*
+ * Restore the current user register values from the user stack,
+ * (except for MSR).
+ */
+static int
+restore_user_regs(struct pt_regs *regs, struct mcontext __user *sr, int sig)
+{
+	unsigned long save_r2;
+#if defined(CONFIG_ALTIVEC) || defined(CONFIG_SPE)
+	unsigned long msr;
+#endif
+
+	/* backup/restore the TLS as we don't want it to be modified */
+	if (!sig)
+		save_r2 = regs->gpr[2];
+	/* copy up to but not including MSR */
+	if (__copy_from_user(regs, &sr->mc_gregs, PT_MSR * sizeof(elf_greg_t)))
+		return 1;
+	/* copy from orig_r3 (the word after the MSR) up to the end */
+	if (__copy_from_user(&regs->orig_gpr3, &sr->mc_gregs[PT_ORIG_R3],
+			     GP_REGS_SIZE - PT_ORIG_R3 * sizeof(elf_greg_t)))
+		return 1;
+	if (!sig)
+		regs->gpr[2] = save_r2;
+
+	/* force the process to reload the FP registers from
+	   current->thread when it next does FP instructions */
+	regs->msr &= ~MSR_FP;
+	if (__copy_from_user(current->thread.fpr, &sr->mc_fregs,
+			     sizeof(sr->mc_fregs)))
+		return 1;
+
+#ifdef CONFIG_ALTIVEC
+	/* force the process to reload the altivec registers from
+	   current->thread when it next does altivec instructions */
+	regs->msr &= ~MSR_VEC;
+	if (!__get_user(msr, &sr->mc_gregs[PT_MSR]) && (msr & MSR_VEC) != 0) {
+		/* restore altivec registers from the stack */
+		if (__copy_from_user(current->thread.vr, &sr->mc_vregs,
+				     sizeof(sr->mc_vregs)))
+			return 1;
+	} else if (current->thread.used_vr)
+		memset(&current->thread.vr, 0, ELF_NVRREG * sizeof(vector128));
+
+	/* Always get VRSAVE back */
+	if (__get_user(current->thread.vrsave, (u32 __user *)&sr->mc_vregs[32]))
+		return 1;
+#endif /* CONFIG_ALTIVEC */
+
+#ifdef CONFIG_SPE
+	/* force the process to reload the spe registers from
+	   current->thread when it next does spe instructions */
+	regs->msr &= ~MSR_SPE;
+	if (!__get_user(msr, &sr->mc_gregs[PT_MSR]) && (msr & MSR_SPE) != 0) {
+		/* restore spe registers from the stack */
+		if (__copy_from_user(current->thread.evr, &sr->mc_vregs,
+				     sizeof(sr->mc_vregs)))
+			return 1;
+	} else if (current->thread.used_spe)
+		memset(&current->thread.evr, 0, ELF_NEVRREG * sizeof(u32));
+
+	/* Always get SPEFSCR back */
+	if (__get_user(current->thread.spefscr, (u32 *)&sr->mc_vregs + ELF_NEVRREG))
+		return 1;
+#endif /* CONFIG_SPE */
+
+	return 0;
+}
+
+/*
+ * Restore the user process's signal mask
+ */
+static void
+restore_sigmask(sigset_t *set)
+{
+	sigdelsetmask(set, ~_BLOCKABLE);
+	spin_lock_irq(&current->sighand->siglock);
+	current->blocked = *set;
+	recalc_sigpending();
+	spin_unlock_irq(&current->sighand->siglock);
+}
+
+/*
+ * Set up a signal frame for a "real-time" signal handler
+ * (one which gets siginfo).
+ */
+static void
+handle_rt_signal(unsigned long sig, struct k_sigaction *ka,
+		 siginfo_t *info, sigset_t *oldset, struct pt_regs * regs,
+		 unsigned long newsp)
+{
+	struct rt_sigframe __user *rt_sf;
+	struct mcontext __user *frame;
+	unsigned long origsp = newsp;
+
+	/* Set up Signal Frame */
+	/* Put a Real Time Context onto stack */
+	newsp -= sizeof(*rt_sf);
+	rt_sf = (struct rt_sigframe __user *) newsp;
+
+	/* create a stack frame for the caller of the handler */
+	newsp -= __SIGNAL_FRAMESIZE + 16;
+
+	if (verify_area(VERIFY_WRITE, (void __user *) newsp, origsp - newsp))
+		goto badframe;
+
+	/* Put the siginfo & fill in most of the ucontext */
+	if (copy_siginfo_to_user(&rt_sf->info, info)
+	    || __put_user(0, &rt_sf->uc.uc_flags)
+	    || __put_user(0, &rt_sf->uc.uc_link)
+	    || __put_user(current->sas_ss_sp, &rt_sf->uc.uc_stack.ss_sp)
+	    || __put_user(sas_ss_flags(regs->gpr[1]),
+			  &rt_sf->uc.uc_stack.ss_flags)
+	    || __put_user(current->sas_ss_size, &rt_sf->uc.uc_stack.ss_size)
+	    || __put_user(&rt_sf->uc.uc_mcontext, &rt_sf->uc.uc_regs)
+	    || __copy_to_user(&rt_sf->uc.uc_sigmask, oldset, sizeof(*oldset)))
+		goto badframe;
+
+	/* Save user registers on the stack */
+	frame = &rt_sf->uc.uc_mcontext;
+	if (save_user_regs(regs, frame, __NR_rt_sigreturn))
+		goto badframe;
+
+	if (put_user(regs->gpr[1], (unsigned long __user *)newsp))
+		goto badframe;
+	regs->gpr[1] = newsp;
+	regs->gpr[3] = sig;
+	regs->gpr[4] = (unsigned long) &rt_sf->info;
+	regs->gpr[5] = (unsigned long) &rt_sf->uc;
+	regs->gpr[6] = (unsigned long) rt_sf;
+	regs->nip = (unsigned long) ka->sa.sa_handler;
+	regs->link = (unsigned long) frame->tramp;
+	regs->trap = 0;
+
+	return;
+
+badframe:
+#ifdef DEBUG_SIG
+	printk("badframe in handle_rt_signal, regs=%p frame=%p newsp=%lx\n",
+	       regs, frame, newsp);
+#endif
+	if (sig == SIGSEGV)
+		ka->sa.sa_handler = SIG_DFL;
+	force_sig(SIGSEGV, current);
+}
+
+static int do_setcontext(struct ucontext __user *ucp, struct pt_regs *regs, int sig)
+{
+	sigset_t set;
+	struct mcontext __user *mcp;
+
+	if (__copy_from_user(&set, &ucp->uc_sigmask, sizeof(set))
+	    || __get_user(mcp, &ucp->uc_regs))
+		return -EFAULT;
+	restore_sigmask(&set);
+	if (restore_user_regs(regs, mcp, sig))
+		return -EFAULT;
+
+	return 0;
+}
+
+int sys_swapcontext(struct ucontext __user *old_ctx,
+		    struct ucontext __user *new_ctx,
+		    int ctx_size, int r6, int r7, int r8, struct pt_regs *regs)
+{
+	unsigned char tmp;
+
+	/* Context size is for future use. Right now, we only make sure
+	 * we are passed something we understand
+	 */
+	if (ctx_size < sizeof(struct ucontext))
+		return -EINVAL;
+
+	if (old_ctx != NULL) {
+		if (verify_area(VERIFY_WRITE, old_ctx, sizeof(*old_ctx))
+		    || save_user_regs(regs, &old_ctx->uc_mcontext, 0)
+		    || __copy_to_user(&old_ctx->uc_sigmask,
+				      &current->blocked, sizeof(sigset_t))
+		    || __put_user(&old_ctx->uc_mcontext, &old_ctx->uc_regs))
+			return -EFAULT;
+	}
+	if (new_ctx == NULL)
+		return 0;
+	if (verify_area(VERIFY_READ, new_ctx, sizeof(*new_ctx))
+	    || __get_user(tmp, (u8 __user *) new_ctx)
+	    || __get_user(tmp, (u8 __user *) (new_ctx + 1) - 1))
+		return -EFAULT;
+
+	/*
+	 * If we get a fault copying the context into the kernel's
+	 * image of the user's registers, we can't just return -EFAULT
+	 * because the user's registers will be corrupted.  For instance
+	 * the NIP value may have been updated but not some of the
+	 * other registers.  Given that we have done the verify_area
+	 * and successfully read the first and last bytes of the region
+	 * above, this should only happen in an out-of-memory situation
+	 * or if another thread unmaps the region containing the context.
+	 * We kill the task with a SIGSEGV in this situation.
+	 */
+	if (do_setcontext(new_ctx, regs, 0))
+		do_exit(SIGSEGV);
+	sigreturn_exit(regs);
+	/* doesn't actually return back to here */
+	return 0;
+}
+
+int sys_rt_sigreturn(int r3, int r4, int r5, int r6, int r7, int r8,
+		     struct pt_regs *regs)
+{
+	struct rt_sigframe __user *rt_sf;
+
+	/* Always make any pending restarted system calls return -EINTR */
+	current_thread_info()->restart_block.fn = do_no_restart_syscall;
+
+	rt_sf = (struct rt_sigframe __user *)
+		(regs->gpr[1] + __SIGNAL_FRAMESIZE + 16);
+	if (verify_area(VERIFY_READ, rt_sf, sizeof(struct rt_sigframe)))
+		goto bad;
+	if (do_setcontext(&rt_sf->uc, regs, 1))
+		goto bad;
+
+	/*
+	 * It's not clear whether or why it is desirable to save the
+	 * sigaltstack setting on signal delivery and restore it on
+	 * signal return.  But other architectures do this and we have
+	 * always done it up until now so it is probably better not to
+	 * change it.  -- paulus
+	 */
+	do_sigaltstack(&rt_sf->uc.uc_stack, NULL, regs->gpr[1]);
+
+	sigreturn_exit(regs);		/* doesn't return here */
+	return 0;
+
+ bad:
+	force_sig(SIGSEGV, current);
+	return 0;
+}
+
+/*
+ * OK, we're invoking a handler
+ */
+static void
+handle_signal(unsigned long sig, struct k_sigaction *ka,
+	      siginfo_t *info, sigset_t *oldset, struct pt_regs * regs,
+	      unsigned long newsp)
+{
+	struct sigcontext __user *sc;
+	struct sigregs __user *frame;
+	unsigned long origsp = newsp;
+
+	/* Set up Signal Frame */
+	newsp -= sizeof(struct sigregs);
+	frame = (struct sigregs __user *) newsp;
+
+	/* Put a sigcontext on the stack */
+	newsp -= sizeof(*sc);
+	sc = (struct sigcontext __user *) newsp;
+
+	/* create a stack frame for the caller of the handler */
+	newsp -= __SIGNAL_FRAMESIZE;
+
+	if (verify_area(VERIFY_WRITE, (void __user *) newsp, origsp - newsp))
+		goto badframe;
+
+#if _NSIG != 64
+#error "Please adjust handle_signal()"
+#endif
+	if (__put_user((unsigned long) ka->sa.sa_handler, &sc->handler)
+	    || __put_user(oldset->sig[0], &sc->oldmask)
+	    || __put_user(oldset->sig[1], &sc->_unused[3])
+	    || __put_user((struct pt_regs *)frame, &sc->regs)
+	    || __put_user(sig, &sc->signal))
+		goto badframe;
+
+	if (save_user_regs(regs, &frame->mctx, __NR_sigreturn))
+		goto badframe;
+
+	if (put_user(regs->gpr[1], (unsigned long __user *)newsp))
+		goto badframe;
+	regs->gpr[1] = newsp;
+	regs->gpr[3] = sig;
+	regs->gpr[4] = (unsigned long) sc;
+	regs->nip = (unsigned long) ka->sa.sa_handler;
+	regs->link = (unsigned long) frame->mctx.tramp;
+	regs->trap = 0;
+
+	return;
+
+badframe:
+#ifdef DEBUG_SIG
+	printk("badframe in handle_signal, regs=%p frame=%p newsp=%lx\n",
+	       regs, frame, newsp);
+#endif
+	if (sig == SIGSEGV)
+		ka->sa.sa_handler = SIG_DFL;
+	force_sig(SIGSEGV, current);
+}
+
+/*
+ * Do a signal return; undo the signal stack.
+ */
+int sys_sigreturn(int r3, int r4, int r5, int r6, int r7, int r8,
+		  struct pt_regs *regs)
+{
+	struct sigcontext __user *sc;
+	struct sigcontext sigctx;
+	struct mcontext __user *sr;
+	sigset_t set;
+
+	/* Always make any pending restarted system calls return -EINTR */
+	current_thread_info()->restart_block.fn = do_no_restart_syscall;
+
+	sc = (struct sigcontext __user *)(regs->gpr[1] + __SIGNAL_FRAMESIZE);
+	if (copy_from_user(&sigctx, sc, sizeof(sigctx)))
+		goto badframe;
+
+	set.sig[0] = sigctx.oldmask;
+	set.sig[1] = sigctx._unused[3];
+	restore_sigmask(&set);
+
+	sr = (struct mcontext __user *) sigctx.regs;
+	if (verify_area(VERIFY_READ, sr, sizeof(*sr))
+	    || restore_user_regs(regs, sr, 1))
+		goto badframe;
+
+	sigreturn_exit(regs);		/* doesn't return */
+	return 0;
+
+badframe:
+	force_sig(SIGSEGV, current);
+	return 0;
+}
+
+/*
+ * Note that 'init' is a special process: it doesn't get signals it doesn't
+ * want to handle. Thus you cannot kill init even with a SIGKILL even by
+ * mistake.
+ */
+int do_signal(sigset_t *oldset, struct pt_regs *regs)
+{
+	siginfo_t info;
+	struct k_sigaction *ka;
+	unsigned long frame, newsp;
+	int signr, ret;
+
+	if (!oldset)
+		oldset = &current->blocked;
+
+	newsp = frame = 0;
+
+	signr = get_signal_to_deliver(&info, regs, NULL);
+
+	ka = (signr == 0)? NULL: &current->sighand->action[signr-1];
+
+	if (TRAP(regs) == 0x0C00		/* System Call! */
+	    && regs->ccr & 0x10000000		/* error signalled */
+	    && ((ret = regs->gpr[3]) == ERESTARTSYS
+		|| ret == ERESTARTNOHAND || ret == ERESTARTNOINTR
+		|| ret == ERESTART_RESTARTBLOCK)) {
+
+		if (signr > 0
+		    && (ret == ERESTARTNOHAND || ret == ERESTART_RESTARTBLOCK
+			|| (ret == ERESTARTSYS
+			    && !(ka->sa.sa_flags & SA_RESTART)))) {
+			/* make the system call return an EINTR error */
+			regs->result = -EINTR;
+			regs->gpr[3] = EINTR;
+			/* note that the cr0.SO bit is already set */
+		} else {
+			regs->nip -= 4;	/* Back up & retry system call */
+			regs->result = 0;
+			regs->trap = 0;
+			if (ret == ERESTART_RESTARTBLOCK)
+				regs->gpr[0] = __NR_restart_syscall;
+			else
+				regs->gpr[3] = regs->orig_gpr3;
+		}
+	}
+
+	if (signr == 0)
+		return 0;		/* no signals delivered */
+
+	if ((ka->sa.sa_flags & SA_ONSTACK) && current->sas_ss_size
+	    && !on_sig_stack(regs->gpr[1]))
+		newsp = current->sas_ss_sp + current->sas_ss_size;
+	else
+		newsp = regs->gpr[1];
+	newsp &= ~0xfUL;
+
+	/* Whee!  Actually deliver the signal.  */
+	if (ka->sa.sa_flags & SA_SIGINFO)
+		handle_rt_signal(signr, ka, &info, oldset, regs, newsp);
+	else
+		handle_signal(signr, ka, &info, oldset, regs, newsp);
+
+	if (ka->sa.sa_flags & SA_ONESHOT)
+		ka->sa.sa_handler = SIG_DFL;
+
+	if (!(ka->sa.sa_flags & SA_NODEFER)) {
+		spin_lock_irq(&current->sighand->siglock);
+		sigorsets(&current->blocked,&current->blocked,&ka->sa.sa_mask);
+		sigaddset(&current->blocked, signr);
+		recalc_sigpending();
+		spin_unlock_irq(&current->sighand->siglock);
+	}
+
+	return 1;
+}
+
diff -ubw -Naur linux-2.6.8.orig/arch/ppc/kernel/vmlinux.lds.S linux-2.6.8/arch/ppc/kernel/vmlinux.lds.S
--- linux-2.6.8.orig/arch/ppc/kernel/vmlinux.lds.S	2004-08-14 07:38:11.000000000 +0200
+++ linux-2.6.8/arch/ppc/kernel/vmlinux.lds.S	2004-08-14 18:23:39.956583640 +0200
@@ -73,6 +73,12 @@
     CONSTRUCTORS
   }
 
+  . = ALIGN(4096);
+  __nosave_begin = .;
+  .data_nosave : { *(.data.nosave) }
+  . = ALIGN(4096);
+  __nosave_end = .;
+
   . = ALIGN(32);
   .data.cacheline_aligned : { *(.data.cacheline_aligned) }
 
diff -ubw -Naur linux-2.6.8.orig/arch/ppc/platforms/pmac_setup.c linux-2.6.8/arch/ppc/platforms/pmac_setup.c
--- linux-2.6.8.orig/arch/ppc/platforms/pmac_setup.c	2004-08-14 07:37:25.000000000 +0200
+++ linux-2.6.8/arch/ppc/platforms/pmac_setup.c	2004-08-14 18:23:39.962582728 +0200
@@ -51,6 +51,7 @@
 #include <linux/irq.h>
 #include <linux/seq_file.h>
 #include <linux/root_dev.h>
+#include <linux/suspend.h>
 
 #include <asm/reg.h>
 #include <asm/sections.h>
@@ -70,6 +71,8 @@
 #include <asm/pmac_feature.h>
 #include <asm/time.h>
 #include <asm/of_device.h>
+#include <asm/mmu_context.h>
+
 #include "pmac_pic.h"
 #include "mem_pieces.h"
 
@@ -423,11 +426,67 @@
 #endif
 }
 
+/* TODO: Merge the suspend-to-ram with the common code !!!
+ * currently, this is a stub implementation for suspend-to-disk
+ * only
+ */
+
+#ifdef CONFIG_PM_DISK
+
+extern void enable_kernel_altivec(void);
+
+static int pmac_pm_prepare(u32 state)
+{
+	printk(KERN_DEBUG "pmac_pm_prepare(%d)\n", state);
+
+	return 0;
+}
+
+static int pmac_pm_enter(u32 state)
+{
+	printk(KERN_DEBUG "pmac_pm_enter(%d)\n", state);
+
+	/* Giveup the lazy FPU & vec so we don't have to back them
+	 * up from the low level code
+	 */
+	enable_kernel_fp();
+
+#ifdef CONFIG_ALTIVEC
+	if (cur_cpu_spec[0]->cpu_features & CPU_FTR_ALTIVEC)
+		enable_kernel_altivec();
+#endif /* CONFIG_ALTIVEC */
+
+	return 0;
+}
+
+static int pmac_pm_finish(u32 state)
+{
+	printk(KERN_DEBUG "pmac_pm_finish(%d)\n", state);
+
+	/* Restore userland MMU context */
+	set_context(current->active_mm->context, current->active_mm->pgd);
+
+	return 0;
+}
+
+static struct pm_ops pmac_pm_ops = {
+	.pm_disk_mode	= PM_DISK_SHUTDOWN,
+	.prepare	= pmac_pm_prepare,
+	.enter		= pmac_pm_enter,
+	.finish		= pmac_pm_finish,
+};
+
+#endif /* CONFIG_PM_DISK */
+
 static int initializing = 1;
 
 static int pmac_late_init(void)
 {
 	initializing = 0;
+
+#ifdef CONFIG_PM_DISK
+	pm_set_ops(&pmac_pm_ops);
+#endif /* CONFIG_PM_DISK */
 	return 0;
 }
 
diff -ubw -Naur linux-2.6.8.orig/arch/ppc/platforms/pmac_setup.c.orig linux-2.6.8/arch/ppc/platforms/pmac_setup.c.orig
--- linux-2.6.8.orig/arch/ppc/platforms/pmac_setup.c.orig	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.8/arch/ppc/platforms/pmac_setup.c.orig	2004-08-14 07:37:25.000000000 +0200
@@ -0,0 +1,692 @@
+/*
+ *  arch/ppc/platforms/setup.c
+ *
+ *  PowerPC version
+ *    Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
+ *
+ *  Adapted for Power Macintosh by Paul Mackerras
+ *    Copyright (C) 1996 Paul Mackerras (paulus@cs.anu.edu.au)
+ *
+ *  Derived from "arch/alpha/kernel/setup.c"
+ *    Copyright (C) 1995 Linus Torvalds
+ *
+ *  Maintained by Benjamin 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.
+ *
+ */
+
+/*
+ * bootup setup stuff..
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/stddef.h>
+#include <linux/unistd.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/user.h>
+#include <linux/a.out.h>
+#include <linux/tty.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/major.h>
+#include <linux/initrd.h>
+#include <linux/vt_kern.h>
+#include <linux/console.h>
+#include <linux/ide.h>
+#include <linux/pci.h>
+#include <linux/adb.h>
+#include <linux/cuda.h>
+#include <linux/pmu.h>
+#include <linux/irq.h>
+#include <linux/seq_file.h>
+#include <linux/root_dev.h>
+
+#include <asm/reg.h>
+#include <asm/sections.h>
+#include <asm/prom.h>
+#include <asm/system.h>
+#include <asm/pgtable.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/pci-bridge.h>
+#include <asm/ohare.h>
+#include <asm/mediabay.h>
+#include <asm/machdep.h>
+#include <asm/dma.h>
+#include <asm/bootx.h>
+#include <asm/cputable.h>
+#include <asm/btext.h>
+#include <asm/pmac_feature.h>
+#include <asm/time.h>
+#include <asm/of_device.h>
+#include "pmac_pic.h"
+#include "mem_pieces.h"
+
+#undef SHOW_GATWICK_IRQS
+
+extern long pmac_time_init(void);
+extern unsigned long pmac_get_rtc_time(void);
+extern int pmac_set_rtc_time(unsigned long nowtime);
+extern void pmac_read_rtc_time(void);
+extern void pmac_calibrate_decr(void);
+extern void pmac_pcibios_fixup(void);
+extern void pmac_find_bridges(void);
+extern unsigned long pmac_ide_get_base(int index);
+extern void pmac_ide_init_hwif_ports(hw_regs_t *hw,
+	unsigned long data_port, unsigned long ctrl_port, int *irq);
+
+extern void pmac_nvram_update(void);
+extern unsigned char pmac_nvram_read_byte(int addr);
+extern void pmac_nvram_write_byte(int addr, unsigned char val);
+extern int pmac_pci_enable_device_hook(struct pci_dev *dev, int initial);
+extern void pmac_pcibios_after_init(void);
+extern int of_show_percpuinfo(struct seq_file *m, int i);
+
+struct device_node *memory_node;
+
+unsigned char drive_info;
+
+int ppc_override_l2cr = 0;
+int ppc_override_l2cr_value;
+int has_l2cache = 0;
+
+static int current_root_goodness = -1;
+
+extern int pmac_newworld;
+
+#define DEFAULT_ROOT_DEVICE Root_SDA1	/* sda1 - slightly silly choice */
+
+extern void zs_kgdb_hook(int tty_num);
+static void ohare_init(void);
+#ifdef CONFIG_BOOTX_TEXT
+void pmac_progress(char *s, unsigned short hex);
+#endif
+
+sys_ctrler_t sys_ctrler = SYS_CTRLER_UNKNOWN;
+
+#ifdef CONFIG_SMP
+extern struct smp_ops_t psurge_smp_ops;
+extern struct smp_ops_t core99_smp_ops;
+#endif /* CONFIG_SMP */
+
+int __pmac
+pmac_show_cpuinfo(struct seq_file *m)
+{
+	struct device_node *np;
+	char *pp;
+	int plen;
+	int mbmodel = pmac_call_feature(PMAC_FTR_GET_MB_INFO,
+		NULL, PMAC_MB_INFO_MODEL, 0);
+	unsigned int mbflags = (unsigned int)pmac_call_feature(PMAC_FTR_GET_MB_INFO,
+		NULL, PMAC_MB_INFO_FLAGS, 0);
+	char* mbname;
+
+	if (pmac_call_feature(PMAC_FTR_GET_MB_INFO, NULL, PMAC_MB_INFO_NAME, (int)&mbname) != 0)
+		mbname = "Unknown";
+
+	/* find motherboard type */
+	seq_printf(m, "machine\t\t: ");
+	np = find_devices("device-tree");
+	if (np != NULL) {
+		pp = (char *) get_property(np, "model", NULL);
+		if (pp != NULL)
+			seq_printf(m, "%s\n", pp);
+		else
+			seq_printf(m, "PowerMac\n");
+		pp = (char *) get_property(np, "compatible", &plen);
+		if (pp != NULL) {
+			seq_printf(m, "motherboard\t:");
+			while (plen > 0) {
+				int l = strlen(pp) + 1;
+				seq_printf(m, " %s", pp);
+				plen -= l;
+				pp += l;
+			}
+			seq_printf(m, "\n");
+		}
+	} else
+		seq_printf(m, "PowerMac\n");
+
+	/* print parsed model */
+	seq_printf(m, "detected as\t: %d (%s)\n", mbmodel, mbname);
+	seq_printf(m, "pmac flags\t: %08x\n", mbflags);
+
+	/* find l2 cache info */
+	np = find_devices("l2-cache");
+	if (np == 0)
+		np = find_type_devices("cache");
+	if (np != 0) {
+		unsigned int *ic = (unsigned int *)
+			get_property(np, "i-cache-size", NULL);
+		unsigned int *dc = (unsigned int *)
+			get_property(np, "d-cache-size", NULL);
+		seq_printf(m, "L2 cache\t:");
+		has_l2cache = 1;
+		if (get_property(np, "cache-unified", NULL) != 0 && dc) {
+			seq_printf(m, " %dK unified", *dc / 1024);
+		} else {
+			if (ic)
+				seq_printf(m, " %dK instruction", *ic / 1024);
+			if (dc)
+				seq_printf(m, "%s %dK data",
+					   (ic? " +": ""), *dc / 1024);
+		}
+		pp = get_property(np, "ram-type", NULL);
+		if (pp)
+			seq_printf(m, " %s", pp);
+		seq_printf(m, "\n");
+	}
+
+	/* find ram info */
+	np = find_devices("memory");
+	if (np != 0) {
+		int n;
+		struct reg_property *reg = (struct reg_property *)
+			get_property(np, "reg", &n);
+
+		if (reg != 0) {
+			unsigned long total = 0;
+
+			for (n /= sizeof(struct reg_property); n > 0; --n)
+				total += (reg++)->size;
+			seq_printf(m, "memory\t\t: %luMB\n", total >> 20);
+		}
+	}
+
+	/* Checks "l2cr-value" property in the registry */
+	np = find_devices("cpus");
+	if (np == 0)
+		np = find_type_devices("cpu");
+	if (np != 0) {
+		unsigned int *l2cr = (unsigned int *)
+			get_property(np, "l2cr-value", NULL);
+		if (l2cr != 0) {
+			seq_printf(m, "l2cr override\t: 0x%x\n", *l2cr);
+		}
+	}
+
+	/* Indicate newworld/oldworld */
+	seq_printf(m, "pmac-generation\t: %s\n",
+		   pmac_newworld ? "NewWorld" : "OldWorld");
+
+
+	return 0;
+}
+
+int __openfirmware
+pmac_show_percpuinfo(struct seq_file *m, int i)
+{
+#ifdef CONFIG_CPU_FREQ_PMAC
+	extern unsigned int pmac_get_one_cpufreq(int i);
+	unsigned int freq = pmac_get_one_cpufreq(i);
+	if (freq != 0) {
+		seq_printf(m, "clock\t\t: %dMHz\n", freq/1000);
+		return 0;
+	}
+#endif /* CONFIG_CPU_FREQ_PMAC */
+	return of_show_percpuinfo(m, i);
+}
+
+static volatile u32 *sysctrl_regs;
+
+void __init
+pmac_setup_arch(void)
+{
+	struct device_node *cpu;
+	int *fp;
+	unsigned long pvr;
+
+	pvr = PVR_VER(mfspr(PVR));
+
+	/* Set loops_per_jiffy to a half-way reasonable value,
+	   for use until calibrate_delay gets called. */
+	cpu = find_type_devices("cpu");
+	if (cpu != 0) {
+		fp = (int *) get_property(cpu, "clock-frequency", NULL);
+		if (fp != 0) {
+			if (pvr == 4 || pvr >= 8)
+				/* 604, G3, G4 etc. */
+				loops_per_jiffy = *fp / HZ;
+			else
+				/* 601, 603, etc. */
+				loops_per_jiffy = *fp / (2*HZ);
+		} else
+			loops_per_jiffy = 50000000 / HZ;
+	}
+
+	/* this area has the CPU identification register
+	   and some registers used by smp boards */
+	sysctrl_regs = (volatile u32 *) ioremap(0xf8000000, 0x1000);
+	ohare_init();
+
+	/* Lookup PCI hosts */
+	pmac_find_bridges();
+
+	/* Checks "l2cr-value" property in the registry */
+	if (cur_cpu_spec[0]->cpu_features & CPU_FTR_L2CR) {
+		struct device_node *np = find_devices("cpus");
+		if (np == 0)
+			np = find_type_devices("cpu");
+		if (np != 0) {
+			unsigned int *l2cr = (unsigned int *)
+				get_property(np, "l2cr-value", NULL);
+			if (l2cr != 0) {
+				ppc_override_l2cr = 1;
+				ppc_override_l2cr_value = *l2cr;
+				_set_L2CR(0);
+				_set_L2CR(ppc_override_l2cr_value);
+			}
+		}
+	}
+
+	if (ppc_override_l2cr)
+		printk(KERN_INFO "L2CR overriden (0x%x), backside cache is %s\n",
+			ppc_override_l2cr_value, (ppc_override_l2cr_value & 0x80000000)
+				? "enabled" : "disabled");
+
+#ifdef CONFIG_KGDB
+	zs_kgdb_hook(0);
+#endif
+
+#ifdef CONFIG_ADB_CUDA
+	find_via_cuda();
+#else
+	if (find_devices("via-cuda")) {
+		printk("WARNING ! Your machine is Cuda based but your kernel\n");
+		printk("          wasn't compiled with CONFIG_ADB_CUDA option !\n");
+	}
+#endif
+#ifdef CONFIG_ADB_PMU
+	find_via_pmu();
+#else
+	if (find_devices("via-pmu")) {
+		printk("WARNING ! Your machine is PMU based but your kernel\n");
+		printk("          wasn't compiled with CONFIG_ADB_PMU option !\n");
+	}
+#endif
+#ifdef CONFIG_NVRAM
+	pmac_nvram_init();
+#endif
+#ifdef CONFIG_DUMMY_CONSOLE
+	conswitchp = &dummy_con;
+#endif
+#ifdef CONFIG_BLK_DEV_INITRD
+	if (initrd_start)
+		ROOT_DEV = Root_RAM0;
+	else
+#endif
+		ROOT_DEV = DEFAULT_ROOT_DEVICE;
+
+#ifdef CONFIG_SMP
+	/* Check for Core99 */
+	if (find_devices("uni-n") || find_devices("u3"))
+		ppc_md.smp_ops = &core99_smp_ops;
+	else
+		ppc_md.smp_ops = &psurge_smp_ops;
+#endif /* CONFIG_SMP */
+
+	pci_create_OF_bus_map();
+}
+
+static void __init ohare_init(void)
+{
+	/*
+	 * Turn on the L2 cache.
+	 * We assume that we have a PSX memory controller iff
+	 * we have an ohare I/O controller.
+	 */
+	if (find_devices("ohare") != NULL) {
+		if (((sysctrl_regs[2] >> 24) & 0xf) >= 3) {
+			if (sysctrl_regs[4] & 0x10)
+				sysctrl_regs[4] |= 0x04000020;
+			else
+				sysctrl_regs[4] |= 0x04000000;
+			if(has_l2cache)
+				printk(KERN_INFO "Level 2 cache enabled\n");
+		}
+	}
+}
+
+extern char *bootpath;
+extern char *bootdevice;
+void *boot_host;
+int boot_target;
+int boot_part;
+extern dev_t boot_dev;
+
+#ifdef CONFIG_SCSI
+void __init
+note_scsi_host(struct device_node *node, void *host)
+{
+	int l;
+	char *p;
+
+	l = strlen(node->full_name);
+	if (bootpath != NULL && bootdevice != NULL
+	    && strncmp(node->full_name, bootdevice, l) == 0
+	    && (bootdevice[l] == '/' || bootdevice[l] == 0)) {
+		boot_host = host;
+		/*
+		 * There's a bug in OF 1.0.5.  (Why am I not surprised.)
+		 * If you pass a path like scsi/sd@1:0 to canon, it returns
+		 * something like /bandit@F2000000/gc@10/53c94@10000/sd@0,0
+		 * That is, the scsi target number doesn't get preserved.
+		 * So we pick the target number out of bootpath and use that.
+		 */
+		p = strstr(bootpath, "/sd@");
+		if (p != NULL) {
+			p += 4;
+			boot_target = simple_strtoul(p, NULL, 10);
+			p = strchr(p, ':');
+			if (p != NULL)
+				boot_part = simple_strtoul(p + 1, NULL, 10);
+		}
+	}
+}
+#endif
+
+#if defined(CONFIG_BLK_DEV_IDE) && defined(CONFIG_BLK_DEV_IDE_PMAC)
+static dev_t __init
+find_ide_boot(void)
+{
+	char *p;
+	int n;
+	dev_t __init pmac_find_ide_boot(char *bootdevice, int n);
+
+	if (bootdevice == NULL)
+		return 0;
+	p = strrchr(bootdevice, '/');
+	if (p == NULL)
+		return 0;
+	n = p - bootdevice;
+
+	return pmac_find_ide_boot(bootdevice, n);
+}
+#endif /* CONFIG_BLK_DEV_IDE && CONFIG_BLK_DEV_IDE_PMAC */
+
+void __init
+find_boot_device(void)
+{
+#if defined(CONFIG_BLK_DEV_IDE) && defined(CONFIG_BLK_DEV_IDE_PMAC)
+	boot_dev = find_ide_boot();
+#endif
+}
+
+static int initializing = 1;
+
+static int pmac_late_init(void)
+{
+	initializing = 0;
+	return 0;
+}
+
+late_initcall(pmac_late_init);
+
+/* can't be __init - can be called whenever a disk is first accessed */
+void __pmac
+note_bootable_part(dev_t dev, int part, int goodness)
+{
+	static int found_boot = 0;
+	char *p;
+
+	if (!initializing)
+		return;
+	if ((goodness <= current_root_goodness) &&
+	    ROOT_DEV != DEFAULT_ROOT_DEVICE)
+		return;
+	p = strstr(saved_command_line, "root=");
+	if (p != NULL && (p == saved_command_line || p[-1] == ' '))
+		return;
+
+	if (!found_boot) {
+		find_boot_device();
+		found_boot = 1;
+	}
+	if (!boot_dev || dev == boot_dev) {
+		ROOT_DEV = dev + part;
+		boot_dev = 0;
+		current_root_goodness = goodness;
+	}
+}
+
+void __pmac
+pmac_restart(char *cmd)
+{
+#ifdef CONFIG_ADB_CUDA
+	struct adb_request req;
+#endif /* CONFIG_ADB_CUDA */
+
+	switch (sys_ctrler) {
+#ifdef CONFIG_ADB_CUDA
+	case SYS_CTRLER_CUDA:
+		cuda_request(&req, NULL, 2, CUDA_PACKET,
+			     CUDA_RESET_SYSTEM);
+		for (;;)
+			cuda_poll();
+		break;
+#endif /* CONFIG_ADB_CUDA */
+#ifdef CONFIG_ADB_PMU
+	case SYS_CTRLER_PMU:
+		pmu_restart();
+		break;
+#endif /* CONFIG_ADB_PMU */
+	default: ;
+	}
+}
+
+void __pmac
+pmac_power_off(void)
+{
+#ifdef CONFIG_ADB_CUDA
+	struct adb_request req;
+#endif /* CONFIG_ADB_CUDA */
+
+	switch (sys_ctrler) {
+#ifdef CONFIG_ADB_CUDA
+	case SYS_CTRLER_CUDA:
+		cuda_request(&req, NULL, 2, CUDA_PACKET,
+			     CUDA_POWERDOWN);
+		for (;;)
+			cuda_poll();
+		break;
+#endif /* CONFIG_ADB_CUDA */
+#ifdef CONFIG_ADB_PMU
+	case SYS_CTRLER_PMU:
+		pmu_shutdown();
+		break;
+#endif /* CONFIG_ADB_PMU */
+	default: ;
+	}
+}
+
+void __pmac
+pmac_halt(void)
+{
+   pmac_power_off();
+}
+
+/*
+ * Read in a property describing some pieces of memory.
+ */
+
+static int __init
+get_mem_prop(char *name, struct mem_pieces *mp)
+{
+	struct reg_property *rp;
+	int i, s;
+	unsigned int *ip;
+	int nac = prom_n_addr_cells(memory_node);
+	int nsc = prom_n_size_cells(memory_node);
+
+	ip = (unsigned int *) get_property(memory_node, name, &s);
+	if (ip == NULL) {
+		printk(KERN_ERR "error: couldn't get %s property on /memory\n",
+		       name);
+		return 0;
+	}
+	s /= (nsc + nac) * 4;
+	rp = mp->regions;
+	for (i = 0; i < s; ++i, ip += nac+nsc) {
+		if (nac >= 2 && ip[nac-2] != 0)
+			continue;
+		rp->address = ip[nac-1];
+		if (nsc >= 2 && ip[nac+nsc-2] != 0)
+			rp->size = ~0U;
+		else
+			rp->size = ip[nac+nsc-1];
+		++rp;
+	}
+	mp->n_regions = rp - mp->regions;
+
+	/* Make sure the pieces are sorted. */
+	mem_pieces_sort(mp);
+	mem_pieces_coalesce(mp);
+	return 1;
+}
+
+/*
+ * On systems with Open Firmware, collect information about
+ * physical RAM and which pieces are already in use.
+ * At this point, we have (at least) the first 8MB mapped with a BAT.
+ * Our text, data, bss use something over 1MB, starting at 0.
+ * Open Firmware may be using 1MB at the 4MB point.
+ */
+unsigned long __init
+pmac_find_end_of_memory(void)
+{
+	unsigned long a, total;
+	struct mem_pieces phys_mem;
+
+	/*
+	 * Find out where physical memory is, and check that it
+	 * starts at 0 and is contiguous.  It seems that RAM is
+	 * always physically contiguous on Power Macintoshes.
+	 *
+	 * Supporting discontiguous physical memory isn't hard,
+	 * it just makes the virtual <-> physical mapping functions
+	 * more complicated (or else you end up wasting space
+	 * in mem_map).
+	 */
+	memory_node = find_devices("memory");
+	if (memory_node == NULL || !get_mem_prop("reg", &phys_mem)
+	    || phys_mem.n_regions == 0)
+		panic("No RAM??");
+	a = phys_mem.regions[0].address;
+	if (a != 0)
+		panic("RAM doesn't start at physical address 0");
+	total = phys_mem.regions[0].size;
+
+	if (phys_mem.n_regions > 1) {
+		printk("RAM starting at 0x%x is not contiguous\n",
+		       phys_mem.regions[1].address);
+		printk("Using RAM from 0 to 0x%lx\n", total-1);
+	}
+
+	return total;
+}
+
+void __init
+pmac_init(unsigned long r3, unsigned long r4, unsigned long r5,
+	  unsigned long r6, unsigned long r7)
+{
+	/* isa_io_base gets set in pmac_find_bridges */
+	isa_mem_base = PMAC_ISA_MEM_BASE;
+	pci_dram_offset = PMAC_PCI_DRAM_OFFSET;
+	ISA_DMA_THRESHOLD = ~0L;
+	DMA_MODE_READ = 1;
+	DMA_MODE_WRITE = 2;
+
+	ppc_md.setup_arch     = pmac_setup_arch;
+	ppc_md.show_cpuinfo   = pmac_show_cpuinfo;
+	ppc_md.show_percpuinfo = pmac_show_percpuinfo;
+	ppc_md.irq_canonicalize = NULL;
+	ppc_md.init_IRQ       = pmac_pic_init;
+	ppc_md.get_irq        = pmac_get_irq; /* Changed later on ... */
+
+	ppc_md.pcibios_fixup  = pmac_pcibios_fixup;
+	ppc_md.pcibios_enable_device_hook = pmac_pci_enable_device_hook;
+	ppc_md.pcibios_after_init = pmac_pcibios_after_init;
+
+	ppc_md.restart        = pmac_restart;
+	ppc_md.power_off      = pmac_power_off;
+	ppc_md.halt           = pmac_halt;
+
+	ppc_md.time_init      = pmac_time_init;
+	ppc_md.set_rtc_time   = pmac_set_rtc_time;
+	ppc_md.get_rtc_time   = pmac_get_rtc_time;
+	ppc_md.calibrate_decr = pmac_calibrate_decr;
+
+	ppc_md.find_end_of_memory = pmac_find_end_of_memory;
+
+	ppc_md.feature_call   = pmac_do_feature_call;
+
+#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE)
+#ifdef CONFIG_BLK_DEV_IDE_PMAC
+        ppc_ide_md.ide_init_hwif	= pmac_ide_init_hwif_ports;
+        ppc_ide_md.default_io_base	= pmac_ide_get_base;
+#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;
+#endif /* CONFIG_BOOTX_TEXT */
+
+	if (ppc_md.progress) ppc_md.progress("pmac_init(): exit", 0);
+
+}
+
+#ifdef CONFIG_BOOTX_TEXT
+void __init
+pmac_progress(char *s, unsigned short hex)
+{
+	if (boot_text_mapped) {
+		btext_drawstring(s);
+		btext_drawchar('\n');
+	}
+}
+#endif /* CONFIG_BOOTX_TEXT */
+
+static int __init
+pmac_declare_of_platform_devices(void)
+{
+	struct device_node *np;
+
+	np = find_devices("uni-n");
+	if (np) {
+		for (np = np->child; np != NULL; np = np->sibling)
+			if (strncmp(np->name, "i2c", 3) == 0) {
+				of_platform_device_create(np, "uni-n-i2c");
+				break;
+			}
+	}
+	np = find_devices("u3");
+	if (np) {
+		for (np = np->child; np != NULL; np = np->sibling)
+			if (strncmp(np->name, "i2c", 3) == 0) {
+				of_platform_device_create(np, "u3-i2c");
+				break;
+			}
+	}
+
+	np = find_devices("valkyrie");
+	if (np)
+		of_platform_device_create(np, "valkyrie");
+	np = find_devices("platinum");
+	if (np)
+		of_platform_device_create(np, "platinum");
+
+	return 0;
+}
+
+device_initcall(pmac_declare_of_platform_devices);
diff -ubw -Naur linux-2.6.8.orig/drivers/ide/ppc/pmac.c linux-2.6.8/drivers/ide/ppc/pmac.c
--- linux-2.6.8.orig/drivers/ide/ppc/pmac.c	2004-08-14 07:36:16.000000000 +0200
+++ linux-2.6.8/drivers/ide/ppc/pmac.c	2004-08-14 18:23:39.970581512 +0200
@@ -32,6 +32,7 @@
 #include <linux/notifier.h>
 #include <linux/reboot.h>
 #include <linux/pci.h>
+#include <linux/pm.h>
 #include <linux/adb.h>
 #include <linux/pmu.h>
 
@@ -1368,7 +1369,7 @@
 	ide_hwif_t	*hwif = (ide_hwif_t *)dev_get_drvdata(&mdev->ofdev.dev);
 	int		rc = 0;
 
-	if (state != mdev->ofdev.dev.power_state && state >= 2) {
+	if (state != mdev->ofdev.dev.power_state && state == PM_SUSPEND_MEM) {
 		rc = pmac_ide_do_suspend(hwif);
 		if (rc == 0)
 			mdev->ofdev.dev.power_state = state;
@@ -1476,7 +1477,7 @@
 	ide_hwif_t	*hwif = (ide_hwif_t *)pci_get_drvdata(pdev);
 	int		rc = 0;
 	
-	if (state != pdev->dev.power_state && state >= 2) {
+	if (state != pdev->dev.power_state && state == PM_SUSPEND_MEM ) {
 		rc = pmac_ide_do_suspend(hwif);
 		if (rc == 0)
 			pdev->dev.power_state = state;
diff -ubw -Naur linux-2.6.8.orig/drivers/ide/ppc/pmac.c.orig linux-2.6.8/drivers/ide/ppc/pmac.c.orig
--- linux-2.6.8.orig/drivers/ide/ppc/pmac.c.orig	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.8/drivers/ide/ppc/pmac.c.orig	2004-08-14 07:36:16.000000000 +0200
@@ -0,0 +1,2199 @@
+/*
+ * linux/drivers/ide/ide-pmac.c
+ *
+ * Support for IDE interfaces on PowerMacs.
+ * These IDE interfaces are memory-mapped and have a DBDMA channel
+ * for doing DMA.
+ *
+ *  Copyright (C) 1998-2003 Paul Mackerras & Ben. Herrenschmidt
+ *
+ *  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.
+ *
+ * Some code taken from drivers/ide/ide-dma.c:
+ *
+ *  Copyright (c) 1995-1998  Mark Lord
+ *
+ * TODO: - Use pre-calculated (kauai) timing tables all the time and
+ * get rid of the "rounded" tables used previously, so we have the
+ * same table format for all controllers and can then just have one
+ * big table
+ * 
+ */
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/ide.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include <linux/pci.h>
+#include <linux/adb.h>
+#include <linux/pmu.h>
+
+#include <asm/prom.h>
+#include <asm/io.h>
+#include <asm/dbdma.h>
+#include <asm/ide.h>
+#include <asm/pci-bridge.h>
+#include <asm/machdep.h>
+#include <asm/pmac_feature.h>
+#include <asm/sections.h>
+#include <asm/irq.h>
+
+#ifndef CONFIG_PPC64
+#include <asm/mediabay.h>
+#endif
+
+#include "ide-timing.h"
+
+extern void ide_do_request(ide_hwgroup_t *hwgroup, int masked_irq);
+
+#define IDE_PMAC_DEBUG
+
+#define DMA_WAIT_TIMEOUT	50
+
+typedef struct pmac_ide_hwif {
+	unsigned long			regbase;
+	int				irq;
+	int				kind;
+	int				aapl_bus_id;
+	int				cable_80 : 1;
+	int				mediabay : 1;
+	int				broken_dma : 1;
+	int				broken_dma_warn : 1;
+	struct device_node*		node;
+	struct macio_dev		*mdev;
+	u32				timings[4];
+#ifdef CONFIG_BLK_DEV_IDEDMA_PMAC
+	/* Those fields are duplicating what is in hwif. We currently
+	 * can't use the hwif ones because of some assumptions that are
+	 * beeing done by the generic code about the kind of dma controller
+	 * and format of the dma table. This will have to be fixed though.
+	 */
+	volatile struct dbdma_regs*	dma_regs;
+	struct dbdma_cmd*		dma_table_cpu;
+	dma_addr_t			dma_table_dma;
+	struct scatterlist*		sg_table;
+	int				sg_nents;
+	int				sg_dma_direction;
+#endif
+	
+} pmac_ide_hwif_t;
+
+static pmac_ide_hwif_t pmac_ide[MAX_HWIFS] __pmacdata;
+static int pmac_ide_count;
+
+enum {
+	controller_ohare,	/* OHare based */
+	controller_heathrow,	/* Heathrow/Paddington */
+	controller_kl_ata3,	/* KeyLargo ATA-3 */
+	controller_kl_ata4,	/* KeyLargo ATA-4 */
+	controller_un_ata6,	/* UniNorth2 ATA-6 */
+	controller_k2_ata6	/* K2 ATA-6 */
+};
+
+static const char* model_name[] = {
+	"OHare ATA",		/* OHare based */
+	"Heathrow ATA",		/* Heathrow/Paddington */
+	"KeyLargo ATA-3",	/* KeyLargo ATA-3 (MDMA only) */
+	"KeyLargo ATA-4",	/* KeyLargo ATA-4 (UDMA/66) */
+	"UniNorth ATA-6",	/* UniNorth2 ATA-6 (UDMA/100) */
+	"K2 ATA-6",		/* K2 ATA-6 (UDMA/100) */
+};
+
+/*
+ * Extra registers, both 32-bit little-endian
+ */
+#define IDE_TIMING_CONFIG	0x200
+#define IDE_INTERRUPT		0x300
+
+/* Kauai (U2) ATA has different register setup */
+#define IDE_KAUAI_PIO_CONFIG	0x200
+#define IDE_KAUAI_ULTRA_CONFIG	0x210
+#define IDE_KAUAI_POLL_CONFIG	0x220
+
+/*
+ * Timing configuration 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 */
+
+/* 100Mhz cell, found in Uninorth 2. I don't have much infos about
+ * this one yet, it appears as a pci device (106b/0033) on uninorth
+ * internal PCI bus and it's clock is controlled like gem or fw. It
+ * appears to be an evolution of keylargo ATA4 with a timing register
+ * extended to 2 32bits registers and a similar DBDMA channel. Other
+ * registers seem to exist but I can't tell much about them.
+ * 
+ * So far, I'm using pre-calculated tables for this extracted from
+ * the values used by the MacOS X driver.
+ * 
+ * The "PIO" register controls PIO and MDMA timings, the "ULTRA"
+ * register controls the UDMA timings. At least, it seems bit 0
+ * of this one enables UDMA vs. MDMA, and bits 4..7 are the
+ * cycle time in units of 10ns. Bits 8..15 are used by I don't
+ * know their meaning yet
+ */
+#define TR_100_PIOREG_PIO_MASK		0xff000fff
+#define TR_100_PIOREG_MDMA_MASK		0x00fff000
+#define TR_100_UDMAREG_UDMA_MASK	0x0000ffff
+#define TR_100_UDMAREG_UDMA_EN		0x00000001
+
+
+/* 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.
+ */
+#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
+
+/*
+ * Interrupt register definitions
+ */
+#define IDE_INTR_DMA			0x80000000
+#define IDE_INTR_DEVICE			0x40000000
+
+#ifdef CONFIG_BLK_DEV_IDEDMA_PMAC
+
+/* 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;
+};
+
+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 }
+};
+
+struct mdma_timings_t mdma_timings_33k[] __pmacdata =
+{
+    { 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 }
+};
+
+struct mdma_timings_t mdma_timings_66[] __pmacdata =
+{
+    { 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 }
+};
+
+/* KeyLargo ATA-4 Ultra DMA timings (rounded) */
+struct {
+	int	addrSetup; /* ??? */
+	int	rdy2pause;
+	int	wrDataSetup;
+} kl66_udma_timings[] __pmacdata =
+{
+    {   0, 180,  120 },	/* Mode 0 */
+    {   0, 150,  90 },	/*      1 */
+    {   0, 120,  60 },	/*      2 */
+    {   0, 90,   45 },	/*      3 */
+    {   0, 90,   30 }	/*      4 */
+};
+
+/* UniNorth 2 ATA/100 timings */
+struct kauai_timing {
+	int	cycle_time;
+	u32	timing_reg;
+};
+
+static struct kauai_timing	kauai_pio_timings[] __pmacdata =
+{
+	{ 930	, 0x08000fff },
+	{ 600	, 0x08000a92 },
+	{ 383	, 0x0800060f },
+	{ 360	, 0x08000492 },
+	{ 330	, 0x0800048f },
+	{ 300	, 0x080003cf },
+	{ 270	, 0x080003cc },
+	{ 240	, 0x0800038b },
+	{ 239	, 0x0800030c },
+	{ 180	, 0x05000249 },
+	{ 120	, 0x04000148 }
+};
+
+static struct kauai_timing	kauai_mdma_timings[] __pmacdata =
+{
+	{ 1260	, 0x00fff000 },
+	{ 480	, 0x00618000 },
+	{ 360	, 0x00492000 },
+	{ 270	, 0x0038e000 },
+	{ 240	, 0x0030c000 },
+	{ 210	, 0x002cb000 },
+	{ 180	, 0x00249000 },
+	{ 150	, 0x00209000 },
+	{ 120	, 0x00148000 },
+	{ 0	, 0 },
+};
+
+static struct kauai_timing	kauai_udma_timings[] __pmacdata =
+{
+	{ 120	, 0x000070c0 },
+	{ 90	, 0x00005d80 },
+	{ 60	, 0x00004a60 },
+	{ 45	, 0x00003a50 },
+	{ 30	, 0x00002a30 },
+	{ 20	, 0x00002921 },
+	{ 0	, 0 },
+};
+
+static inline u32
+kauai_lookup_timing(struct kauai_timing* table, int cycle_time)
+{
+	int i;
+	
+	for (i=0; table[i].cycle_time; i++)
+		if (cycle_time > table[i+1].cycle_time)
+			return table[i].timing_reg;
+	return 0;
+}
+
+/* allow up to 256 DBDMA commands per xfer */
+#define MAX_DCMDS		256
+
+/* 
+ * Wait 1s for disk to answer on IDE bus after a hard reset
+ * of the device (via GPIO/FCR).
+ * 
+ * Some devices seem to "pollute" the bus even after dropping
+ * the BSY bit (typically some combo drives slave on the UDMA
+ * bus) after a hard reset. Since we hard reset all drives on
+ * KeyLargo ATA66, we have to keep that delay around. I may end
+ * up not hard resetting anymore on these and keep the delay only
+ * for older interfaces instead (we have to reset when coming
+ * from MacOS...) --BenH. 
+ */
+#define IDE_WAKEUP_DELAY	(1*HZ)
+
+static void pmac_ide_setup_dma(pmac_ide_hwif_t *pmif, ide_hwif_t *hwif);
+static int pmac_ide_build_dmatable(ide_drive_t *drive, struct request *rq);
+static int pmac_ide_tune_chipset(ide_drive_t *drive, u8 speed);
+static void pmac_ide_tuneproc(ide_drive_t *drive, u8 pio);
+static void pmac_ide_selectproc(ide_drive_t *drive);
+static void pmac_ide_kauai_selectproc(ide_drive_t *drive);
+static int pmac_ide_dma_begin (ide_drive_t *drive);
+
+#endif /* CONFIG_BLK_DEV_IDEDMA_PMAC */
+
+/*
+ * Below is the code for blinking the laptop LED along with hard
+ * disk activity.
+ */
+
+#ifdef CONFIG_BLK_DEV_IDE_PMAC_BLINK
+
+/* Set to 50ms minimum led-on time (also used to limit frequency
+ * of requests sent to the PMU
+ */
+#define PMU_HD_BLINK_TIME	(HZ/50)
+
+static struct adb_request pmu_blink_on, pmu_blink_off;
+static spinlock_t pmu_blink_lock;
+static unsigned long pmu_blink_stoptime;
+static int pmu_blink_ledstate;
+static struct timer_list pmu_blink_timer;
+static int pmu_ide_blink_enabled;
+
+
+static void
+pmu_hd_blink_timeout(unsigned long data)
+{
+	unsigned long flags;
+	
+	spin_lock_irqsave(&pmu_blink_lock, flags);
+
+	/* We may have been triggered again in a racy way, check
+	 * that we really want to switch it off
+	 */
+	if (time_after(pmu_blink_stoptime, jiffies))
+		goto done;
+
+	/* Previous req. not complete, try 100ms more */
+	if (pmu_blink_off.complete == 0)
+		mod_timer(&pmu_blink_timer, jiffies + PMU_HD_BLINK_TIME);
+	else if (pmu_blink_ledstate) {
+		pmu_request(&pmu_blink_off, NULL, 4, 0xee, 4, 0, 0);
+		pmu_blink_ledstate = 0;
+	}
+done:
+	spin_unlock_irqrestore(&pmu_blink_lock, flags);
+}
+
+static void
+pmu_hd_kick_blink(void *data, int rw)
+{
+	unsigned long flags;
+	
+	pmu_blink_stoptime = jiffies + PMU_HD_BLINK_TIME;
+	wmb();
+	mod_timer(&pmu_blink_timer, pmu_blink_stoptime);
+	/* Fast path when LED is already ON */
+	if (pmu_blink_ledstate == 1)
+		return;
+	spin_lock_irqsave(&pmu_blink_lock, flags);
+	if (pmu_blink_on.complete && !pmu_blink_ledstate) {
+		pmu_request(&pmu_blink_on, NULL, 4, 0xee, 4, 0, 1);
+		pmu_blink_ledstate = 1;
+	}
+	spin_unlock_irqrestore(&pmu_blink_lock, flags);
+}
+
+static int
+pmu_hd_blink_init(void)
+{
+	struct device_node *dt;
+	const char *model;
+
+	/* Currently, I only enable this feature on KeyLargo based laptops,
+	 * older laptops may support it (at least heathrow/paddington) but
+	 * I don't feel like loading those venerable old machines with so
+	 * much additional interrupt & PMU activity...
+	 */
+	if (pmu_get_model() != PMU_KEYLARGO_BASED)
+		return 0;
+	
+	dt = find_devices("device-tree");
+	if (dt == NULL)
+		return 0;
+	model = (const char *)get_property(dt, "model", NULL);
+	if (model == NULL)
+		return 0;
+	if (strncmp(model, "PowerBook", strlen("PowerBook")) != 0 &&
+	    strncmp(model, "iBook", strlen("iBook")) != 0)
+	    	return 0;
+	
+	pmu_blink_on.complete = 1;
+	pmu_blink_off.complete = 1;
+	spin_lock_init(&pmu_blink_lock);
+	init_timer(&pmu_blink_timer);
+	pmu_blink_timer.function = pmu_hd_blink_timeout;
+
+	return 1;
+}
+
+#endif /* CONFIG_BLK_DEV_IDE_PMAC_BLINK */
+
+/*
+ * N.B. this can't be an initfunc, because the media-bay task can
+ * call ide_[un]register at any time.
+ */
+void __pmac
+pmac_ide_init_hwif_ports(hw_regs_t *hw,
+			      unsigned long data_port, unsigned long ctrl_port,
+			      int *irq)
+{
+	int i, ix;
+
+	if (data_port == 0)
+		return;
+
+	for (ix = 0; ix < MAX_HWIFS; ++ix)
+		if (data_port == pmac_ide[ix].regbase)
+			break;
+
+	if (ix >= MAX_HWIFS) {
+		/* Probably a PCI interface... */
+		for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; ++i)
+			hw->io_ports[i] = data_port + i - IDE_DATA_OFFSET;
+		hw->io_ports[IDE_CONTROL_OFFSET] = ctrl_port;
+		return;
+	}
+
+	for (i = 0; i < 8; ++i)
+		hw->io_ports[i] = data_port + i * 0x10;
+	hw->io_ports[8] = data_port + 0x160;
+
+	if (irq != NULL)
+		*irq = pmac_ide[ix].irq;
+}
+
+/*
+ * Apply the timings of the proper unit (master/slave) to the shared
+ * timing register when selecting that unit. This version is for
+ * ASICs with a single timing register
+ */
+static void __pmac
+pmac_ide_selectproc(ide_drive_t *drive)
+{
+	pmac_ide_hwif_t* pmif = (pmac_ide_hwif_t *)HWIF(drive)->hwif_data;
+
+	if (pmif == NULL)
+		return;
+
+	if (drive->select.b.unit & 0x01)
+		writel(pmif->timings[1],
+			(unsigned *)(IDE_DATA_REG+IDE_TIMING_CONFIG));
+	else
+		writel(pmif->timings[0],
+			(unsigned *)(IDE_DATA_REG+IDE_TIMING_CONFIG));
+	(void)readl((unsigned *)(IDE_DATA_REG+IDE_TIMING_CONFIG));
+}
+
+/*
+ * Apply the timings of the proper unit (master/slave) to the shared
+ * timing register when selecting that unit. This version is for
+ * ASICs with a dual timing register (Kauai)
+ */
+static void __pmac
+pmac_ide_kauai_selectproc(ide_drive_t *drive)
+{
+	pmac_ide_hwif_t* pmif = (pmac_ide_hwif_t *)HWIF(drive)->hwif_data;
+
+	if (pmif == NULL)
+		return;
+
+	if (drive->select.b.unit & 0x01) {
+		writel(pmif->timings[1],
+		       (unsigned *)(IDE_DATA_REG + IDE_KAUAI_PIO_CONFIG));
+		writel(pmif->timings[3],
+		       (unsigned *)(IDE_DATA_REG + IDE_KAUAI_ULTRA_CONFIG));
+	} else {
+		writel(pmif->timings[0],
+		       (unsigned *)(IDE_DATA_REG + IDE_KAUAI_PIO_CONFIG));
+		writel(pmif->timings[2],
+		       (unsigned *)(IDE_DATA_REG + IDE_KAUAI_ULTRA_CONFIG));
+	}
+	(void)readl((unsigned *)(IDE_DATA_REG + IDE_KAUAI_PIO_CONFIG));
+}
+
+/*
+ * Force an update of controller timing values for a given drive
+ */
+static void __pmac
+pmac_ide_do_update_timings(ide_drive_t *drive)
+{
+	pmac_ide_hwif_t* pmif = (pmac_ide_hwif_t *)HWIF(drive)->hwif_data;
+
+	if (pmif == NULL)
+		return;
+
+	if (pmif->kind == controller_un_ata6 || pmif->kind == controller_k2_ata6)
+		pmac_ide_kauai_selectproc(drive);
+	else
+		pmac_ide_selectproc(drive);
+}
+
+static void
+pmac_outbsync(ide_drive_t *drive, u8 value, unsigned long port)
+{
+	u32 tmp;
+	
+	writeb(value, port);	
+	tmp = readl((unsigned *)(IDE_DATA_REG + IDE_TIMING_CONFIG));
+}
+
+/*
+ * Send the SET_FEATURE IDE command to the drive and update drive->id with
+ * the new state. We currently don't use the generic routine as it used to
+ * cause various trouble, especially with older mediabays.
+ * This code is sometimes triggering a spurrious interrupt though, I need
+ * to sort that out sooner or later and see if I can finally get the
+ * common version to work properly in all cases
+ */
+static int __pmac
+pmac_ide_do_setfeature(ide_drive_t *drive, u8 command)
+{
+	ide_hwif_t *hwif = HWIF(drive);
+	int result = 1;
+	
+	disable_irq_nosync(hwif->irq);
+	udelay(1);
+	SELECT_DRIVE(drive);
+	SELECT_MASK(drive, 0);
+	udelay(1);
+	/* Get rid of pending error state */
+	(void) hwif->INB(IDE_STATUS_REG);
+	/* Timeout bumped for some powerbooks */
+	if (wait_for_ready(drive, 2000)) {
+		/* Timeout bumped for some powerbooks */
+		printk(KERN_ERR "%s: pmac_ide_do_setfeature disk not ready "
+			"before SET_FEATURE!\n", drive->name);
+		goto out;
+	}
+	udelay(10);
+	hwif->OUTB(drive->ctl | 2, IDE_CONTROL_REG);
+	hwif->OUTB(command, IDE_NSECTOR_REG);
+	hwif->OUTB(SETFEATURES_XFER, IDE_FEATURE_REG);
+	hwif->OUTBSYNC(drive, WIN_SETFEATURES, IDE_COMMAND_REG);
+	udelay(1);
+	/* Timeout bumped for some powerbooks */
+	result = wait_for_ready(drive, 2000);
+	hwif->OUTB(drive->ctl, IDE_CONTROL_REG);
+	if (result)
+		printk(KERN_ERR "%s: pmac_ide_do_setfeature disk not ready "
+			"after SET_FEATURE !\n", drive->name);
+out:
+	SELECT_MASK(drive, 0);
+	if (result == 0) {
+		drive->id->dma_ultra &= ~0xFF00;
+		drive->id->dma_mword &= ~0x0F00;
+		drive->id->dma_1word &= ~0x0F00;
+		switch(command) {
+			case XFER_UDMA_7:
+				drive->id->dma_ultra |= 0x8080; break;
+			case XFER_UDMA_6:
+				drive->id->dma_ultra |= 0x4040; break;
+			case XFER_UDMA_5:
+				drive->id->dma_ultra |= 0x2020; break;
+			case XFER_UDMA_4:
+				drive->id->dma_ultra |= 0x1010; break;
+			case XFER_UDMA_3:
+				drive->id->dma_ultra |= 0x0808; break;
+			case XFER_UDMA_2:
+				drive->id->dma_ultra |= 0x0404; break;
+			case XFER_UDMA_1:
+				drive->id->dma_ultra |= 0x0202; break;
+			case XFER_UDMA_0:
+				drive->id->dma_ultra |= 0x0101; break;
+			case XFER_MW_DMA_2:
+				drive->id->dma_mword |= 0x0404; break;
+			case XFER_MW_DMA_1:
+				drive->id->dma_mword |= 0x0202; break;
+			case XFER_MW_DMA_0:
+				drive->id->dma_mword |= 0x0101; break;
+			case XFER_SW_DMA_2:
+				drive->id->dma_1word |= 0x0404; break;
+			case XFER_SW_DMA_1:
+				drive->id->dma_1word |= 0x0202; break;
+			case XFER_SW_DMA_0:
+				drive->id->dma_1word |= 0x0101; break;
+			default: break;
+		}
+	}
+	enable_irq(hwif->irq);
+	return result;
+}
+
+/*
+ * Old tuning functions (called on hdparm -p), sets up drive PIO timings
+ */
+static void __pmac
+pmac_ide_tuneproc(ide_drive_t *drive, u8 pio)
+{
+	ide_pio_data_t d;
+	u32 *timings;
+	unsigned accessTicks, recTicks;
+	unsigned accessTime, recTime;
+	pmac_ide_hwif_t* pmif = (pmac_ide_hwif_t *)HWIF(drive)->hwif_data;
+	
+	if (pmif == NULL)
+		return;
+		
+	/* which drive is it ? */
+	timings = &pmif->timings[drive->select.b.unit & 0x01];
+
+	pio = ide_get_best_pio_mode(drive, pio, 4, &d);
+
+	switch (pmif->kind) {
+	case controller_un_ata6:
+	case controller_k2_ata6: {
+		/* 100Mhz cell */
+		u32 tr = kauai_lookup_timing(kauai_pio_timings, d.cycle_time);
+		if (tr == 0)
+			return;
+		*timings = ((*timings) & ~TR_100_PIOREG_PIO_MASK) | tr;
+		break;
+		}
+	case controller_kl_ata4:
+		/* 66Mhz cell */
+		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);
+		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);
+		break;
+	default: {
+		/* 33Mhz cell */
+		int ebit = 0;
+		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);
+		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;
+		break;
+		}
+	}
+
+#ifdef IDE_PMAC_DEBUG
+	printk(KERN_ERR "%s: Set PIO timing for mode %d, reg: 0x%08x\n",
+		drive->name, pio,  *timings);
+#endif	
+
+	if (drive->select.all == HWIF(drive)->INB(IDE_SELECT_REG))
+		pmac_ide_do_update_timings(drive);
+}
+
+#ifdef CONFIG_BLK_DEV_IDEDMA_PMAC
+
+/*
+ * Calculate KeyLargo ATA/66 UDMA timings
+ */
+static int __pmac
+set_timings_udma_ata4(u32 *timings, u8 speed)
+{
+	unsigned rdyToPauseTicks, wrDataSetupTicks, addrTicks;
+
+	if (speed > XFER_UDMA_4)
+		return 1;
+
+	rdyToPauseTicks = SYSCLK_TICKS_66(kl66_udma_timings[speed & 0xf].rdy2pause);
+	wrDataSetupTicks = SYSCLK_TICKS_66(kl66_udma_timings[speed & 0xf].wrDataSetup);
+	addrTicks = SYSCLK_TICKS_66(kl66_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;
+}
+
+/*
+ * Calculate Kauai ATA/100 UDMA timings
+ */
+static int __pmac
+set_timings_udma_ata6(u32 *pio_timings, u32 *ultra_timings, u8 speed)
+{
+	struct ide_timing *t = ide_timing_find_mode(speed);
+	u32 tr;
+
+	if (speed > XFER_UDMA_5 || t == NULL)
+		return 1;
+	tr = kauai_lookup_timing(kauai_udma_timings, (int)t->udma);
+	if (tr == 0)
+		return 1;
+	*ultra_timings = ((*ultra_timings) & ~TR_100_UDMAREG_UDMA_MASK) | tr;
+	*ultra_timings = (*ultra_timings) | TR_100_UDMAREG_UDMA_EN;
+
+	return 0;
+}
+
+/*
+ * Calculate MDMA timings for all cells
+ */
+static int __pmac
+set_timings_mdma(ide_drive_t *drive, int intf_type, u32 *timings, u32 *timings2,
+			u8 speed, int drive_cycle_time)
+{
+	int cycleTime, accessTime, recTime;
+	unsigned accessTicks, recTicks;
+	struct mdma_timings_t* tm = NULL;
+	int i;
+
+	/* 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_un_ata6:
+		case controller_k2_ata6:
+			break;
+		case controller_kl_ata4:
+			tm = mdma_timings_66;
+			break;
+		case controller_kl_ata3:
+			tm = mdma_timings_33k;
+			break;
+		default:
+			tm = mdma_timings_33;
+			break;
+	}
+	if (tm != NULL) {
+		/* 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;
+
+#ifdef IDE_PMAC_DEBUG
+		printk(KERN_ERR "%s: MDMA, cycleTime: %d, accessTime: %d, recTime: %d\n",
+			drive->name, cycleTime, accessTime, recTime);
+#endif
+	}
+	switch(intf_type) {
+	case controller_un_ata6:
+	case controller_k2_ata6: {
+		/* 100Mhz cell */
+		u32 tr = kauai_lookup_timing(kauai_mdma_timings, cycleTime);
+		if (tr == 0)
+			return 1;
+		*timings = ((*timings) & ~TR_100_PIOREG_MDMA_MASK) | tr;
+		*timings2 = (*timings2) & ~TR_100_UDMAREG_UDMA_EN;
+		}
+		break;
+	case controller_kl_ata4:
+		/* 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);
+		break;
+	case 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);
+		break;
+	default: {
+		/* 33Mhz cell on others */
+		int halfTick = 0;
+		int origAccessTime = accessTime;
+		int origRecTime = recTime;
+		
+		accessTicks = SYSCLK_TICKS(accessTime);
+		accessTicks = max(accessTicks, 1U);
+		accessTicks = min(accessTicks, 0x1fU);
+		accessTime = accessTicks * IDE_SYSCLK_NS;
+		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 "%s: Set MDMA timing for mode %d, reg: 0x%08x\n",
+		drive->name, speed & 0xf,  *timings);
+#endif	
+	return 0;
+}
+#endif /* #ifdef CONFIG_BLK_DEV_IDEDMA_PMAC */
+
+/* 
+ * Speedproc. This function is called by the core to set any of the standard
+ * timing (PIO, MDMA or UDMA) to both the drive and the controller.
+ * You may notice we don't use this function on normal "dma check" operation,
+ * our dedicated function is more precise as it uses the drive provided
+ * cycle time value. We should probably fix this one to deal with that too...
+ */
+static int __pmac
+pmac_ide_tune_chipset (ide_drive_t *drive, byte speed)
+{
+	int unit = (drive->select.b.unit & 0x01);
+	int ret = 0;
+	pmac_ide_hwif_t* pmif = (pmac_ide_hwif_t *)HWIF(drive)->hwif_data;
+	u32 *timings, *timings2;
+
+	if (pmif == NULL)
+		return 1;
+		
+	timings = &pmif->timings[unit];
+	timings2 = &pmif->timings[unit+2];
+	
+	switch(speed) {
+#ifdef CONFIG_BLK_DEV_IDEDMA_PMAC
+		case XFER_UDMA_5:
+			if (pmif->kind != controller_un_ata6 &&
+			    pmif->kind != controller_k2_ata6)
+				return 1;
+		case XFER_UDMA_4:
+		case XFER_UDMA_3:
+			if (HWIF(drive)->udma_four == 0)
+				return 1;		
+		case XFER_UDMA_2:
+		case XFER_UDMA_1:
+		case XFER_UDMA_0:
+			if (pmif->kind == controller_kl_ata4)
+				ret = set_timings_udma_ata4(timings, speed);
+			else if (pmif->kind == controller_un_ata6
+				 || pmif->kind == controller_k2_ata6)
+				ret = set_timings_udma_ata6(timings, timings2, speed);
+			else
+				ret = 1;		
+			break;
+		case XFER_MW_DMA_2:
+		case XFER_MW_DMA_1:
+		case XFER_MW_DMA_0:
+			ret = set_timings_mdma(drive, pmif->kind, timings, timings2, speed, 0);
+			break;
+		case XFER_SW_DMA_2:
+		case XFER_SW_DMA_1:
+		case XFER_SW_DMA_0:
+			return 1;
+#endif /* CONFIG_BLK_DEV_IDEDMA_PMAC */
+		case XFER_PIO_4:
+		case XFER_PIO_3:
+		case XFER_PIO_2:
+		case XFER_PIO_1:
+		case XFER_PIO_0:
+			pmac_ide_tuneproc(drive, speed & 0x07);
+			break;
+		default:
+			ret = 1;
+	}
+	if (ret)
+		return ret;
+
+	ret = pmac_ide_do_setfeature(drive, speed);
+	if (ret)
+		return ret;
+		
+	pmac_ide_do_update_timings(drive);	
+	drive->current_speed = speed;
+
+	return 0;
+}
+
+/*
+ * Blast some well known "safe" values to the timing registers at init or
+ * wakeup from sleep time, before we do real calculation
+ */
+static void __pmac
+sanitize_timings(pmac_ide_hwif_t *pmif)
+{
+	unsigned int value, value2 = 0;
+	
+	switch(pmif->kind) {
+		case controller_un_ata6:
+		case controller_k2_ata6:
+			value = 0x08618a92;
+			value2 = 0x00002921;
+			break;
+		case controller_kl_ata4:
+			value = 0x0008438c;
+			break;
+		case controller_kl_ata3:
+			value = 0x00084526;
+			break;
+		case controller_heathrow:
+		case controller_ohare:
+		default:
+			value = 0x00074526;
+			break;
+	}
+	pmif->timings[0] = pmif->timings[1] = value;
+	pmif->timings[2] = pmif->timings[3] = value2;
+}
+
+unsigned long __pmac
+pmac_ide_get_base(int index)
+{
+	return pmac_ide[index].regbase;
+}
+
+int __pmac
+pmac_ide_check_base(unsigned long 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(unsigned long base)
+{
+	int ix;
+
+	for (ix = 0; ix < MAX_HWIFS; ++ix)
+		if (base == pmac_ide[ix].regbase)
+			return pmac_ide[ix].irq;
+	return 0;
+}
+
+static int ide_majors[]  __pmacdata = { 3, 22, 33, 34, 56, 57 };
+
+dev_t __init
+pmac_find_ide_boot(char *bootdevice, int n)
+{
+	int i;
+	
+	/*
+	 * Look through the list of IDE interfaces for this one.
+	 */
+	for (i = 0; i < pmac_ide_count; ++i) {
+		char *name;
+		if (!pmac_ide[i].node || !pmac_ide[i].node->full_name)
+			continue;
+		name = pmac_ide[i].node->full_name;
+		if (memcmp(name, bootdevice, n) == 0 && name[n] == 0) {
+			/* XXX should cope with the 2nd drive as well... */
+			return MKDEV(ide_majors[i], 0);
+		}
+	}
+
+	return 0;
+}
+
+/* Suspend call back, should be called after the child devices
+ * have actually been suspended
+ */
+static int
+pmac_ide_do_suspend(ide_hwif_t *hwif)
+{
+	pmac_ide_hwif_t *pmif = (pmac_ide_hwif_t *)hwif->hwif_data;
+	
+	/* We clear the timings */
+	pmif->timings[0] = 0;
+	pmif->timings[1] = 0;
+	
+#ifdef CONFIG_BLK_DEV_IDE_PMAC_BLINK
+	/* Note: This code will be called for every hwif, thus we'll
+	 * try several time to stop the LED blinker timer,  but that
+	 * should be harmless
+	 */
+	if (pmu_ide_blink_enabled) {
+		unsigned long flags;
+
+		/* Make sure we don't hit the PMU blink */
+		spin_lock_irqsave(&pmu_blink_lock, flags);
+		if (pmu_blink_ledstate)
+			del_timer(&pmu_blink_timer);
+		pmu_blink_ledstate = 0;
+		spin_unlock_irqrestore(&pmu_blink_lock, flags);
+	}
+#endif /* CONFIG_BLK_DEV_IDE_PMAC_BLINK */
+
+	/* The media bay will handle itself just fine */
+	if (pmif->mediabay)
+		return 0;
+	
+	/* Disable the bus */
+	ppc_md.feature_call(PMAC_FTR_IDE_ENABLE, pmif->node, pmif->aapl_bus_id, 0);
+
+	return 0;
+}
+
+/* Resume call back, should be called before the child devices
+ * are resumed
+ */
+static int
+pmac_ide_do_resume(ide_hwif_t *hwif)
+{
+	pmac_ide_hwif_t *pmif = (pmac_ide_hwif_t *)hwif->hwif_data;
+	
+	/* Hard reset & re-enable controller (do we really need to reset ? -BenH) */
+	if (!pmif->mediabay) {
+		ppc_md.feature_call(PMAC_FTR_IDE_RESET, pmif->node, pmif->aapl_bus_id, 1);
+		ppc_md.feature_call(PMAC_FTR_IDE_ENABLE, pmif->node, pmif->aapl_bus_id, 1);
+		msleep(10);
+		ppc_md.feature_call(PMAC_FTR_IDE_RESET, pmif->node, pmif->aapl_bus_id, 0);
+		msleep(jiffies_to_msecs(IDE_WAKEUP_DELAY));
+	}
+
+	/* Sanitize drive timings */
+	sanitize_timings(pmif);
+
+	return 0;
+}
+
+/*
+ * Setup, register & probe an IDE channel driven by this driver, this is
+ * called by one of the 2 probe functions (macio or PCI). Note that a channel
+ * that ends up beeing free of any device is not kept around by this driver
+ * (it is kept in 2.4). This introduce an interface numbering change on some
+ * rare machines unfortunately, but it's better this way.
+ */
+static int
+pmac_ide_setup_device(pmac_ide_hwif_t *pmif, ide_hwif_t *hwif)
+{
+	struct device_node *np = pmif->node;
+	int *bidp, i;
+
+	pmif->cable_80 = 0;
+	pmif->broken_dma = pmif->broken_dma_warn = 0;
+	if (device_is_compatible(np, "kauai-ata"))
+		pmif->kind = controller_un_ata6;
+	else if (device_is_compatible(np, "K2-UATA"))
+		pmif->kind = controller_k2_ata6;
+	else if (device_is_compatible(np, "keylargo-ata")) {
+		if (strcmp(np->name, "ata-4") == 0)
+			pmif->kind = controller_kl_ata4;
+		else
+			pmif->kind = controller_kl_ata3;
+	} else if (device_is_compatible(np, "heathrow-ata"))
+		pmif->kind = controller_heathrow;
+	else {
+		pmif->kind = controller_ohare;
+		pmif->broken_dma = 1;
+	}
+
+	bidp = (int *)get_property(np, "AAPL,bus-id", NULL);
+	pmif->aapl_bus_id =  bidp ? *bidp : 0;
+
+	/* Get cable type from device-tree */
+	if (pmif->kind == controller_kl_ata4 || pmif->kind == controller_un_ata6
+	    || pmif->kind == controller_k2_ata6) {
+		char* cable = get_property(np, "cable-type", NULL);
+		if (cable && !strncmp(cable, "80-", 3))
+			pmif->cable_80 = 1;
+	}
+
+	pmif->mediabay = 0;
+	
+	/* Make sure we have sane timings */
+	sanitize_timings(pmif);
+
+#ifndef CONFIG_PPC64
+	/* XXX FIXME: Media bay stuff need re-organizing */
+	if (np->parent && np->parent->name
+	    && strcasecmp(np->parent->name, "media-bay") == 0) {
+#ifdef CONFIG_PMAC_PBOOK
+		media_bay_set_ide_infos(np->parent, pmif->regbase, pmif->irq, hwif->index);
+#endif /* CONFIG_PMAC_PBOOK */
+		pmif->mediabay = 1;
+		if (!bidp)
+			pmif->aapl_bus_id = 1;
+	} else if (pmif->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
+		 */
+		ppc_md.feature_call(PMAC_FTR_IDE_ENABLE, np, 0, 1);
+	} else
+#endif
+	{
+ 		/* This is necessary to enable IDE when net-booting */
+		ppc_md.feature_call(PMAC_FTR_IDE_RESET, np, pmif->aapl_bus_id, 1);
+		ppc_md.feature_call(PMAC_FTR_IDE_ENABLE, np, pmif->aapl_bus_id, 1);
+		msleep(10);
+		ppc_md.feature_call(PMAC_FTR_IDE_RESET, np, pmif->aapl_bus_id, 0);
+		msleep(jiffies_to_msecs(IDE_WAKEUP_DELAY));
+	}
+
+	/* Setup MMIO ops */
+	default_hwif_mmiops(hwif);
+       	hwif->OUTBSYNC = pmac_outbsync;
+
+	/* Tell common code _not_ to mess with resources */
+	hwif->mmio = 2;
+	hwif->hwif_data = pmif;
+	pmac_ide_init_hwif_ports(&hwif->hw, pmif->regbase, 0, &hwif->irq);
+	memcpy(hwif->io_ports, hwif->hw.io_ports, sizeof(hwif->io_ports));
+	hwif->chipset = ide_pmac;
+	hwif->noprobe = !hwif->io_ports[IDE_DATA_OFFSET] || pmif->mediabay;
+	hwif->hold = pmif->mediabay;
+	hwif->udma_four = pmif->cable_80;
+	hwif->drives[0].unmask = 1;
+	hwif->drives[1].unmask = 1;
+	hwif->tuneproc = pmac_ide_tuneproc;
+	if (pmif->kind == controller_un_ata6 || pmif->kind == controller_k2_ata6)
+		hwif->selectproc = pmac_ide_kauai_selectproc;
+	else
+		hwif->selectproc = pmac_ide_selectproc;
+	hwif->speedproc = pmac_ide_tune_chipset;
+
+#ifdef CONFIG_BLK_DEV_IDE_PMAC_BLINK
+	pmu_ide_blink_enabled = pmu_hd_blink_init();
+
+	if (pmu_ide_blink_enabled)
+		hwif->led_act = pmu_hd_kick_blink;
+#endif
+
+	printk(KERN_INFO "ide%d: Found Apple %s controller, bus ID %d%s, irq %d\n",
+	       hwif->index, model_name[pmif->kind], pmif->aapl_bus_id,
+	       pmif->mediabay ? " (mediabay)" : "", hwif->irq);
+			
+#ifdef CONFIG_PMAC_PBOOK
+	if (pmif->mediabay && check_media_bay_by_base(pmif->regbase, MB_CD) == 0)
+		hwif->noprobe = 0;
+#endif /* CONFIG_PMAC_PBOOK */
+
+#ifdef CONFIG_BLK_DEV_IDEDMA_PMAC
+	/* has a DBDMA controller channel */
+	if (pmif->dma_regs)
+		pmac_ide_setup_dma(pmif, hwif);
+#endif /* CONFIG_BLK_DEV_IDEDMA_PMAC */
+
+	/* We probe the hwif now */
+	probe_hwif_init(hwif);
+
+	/* The code IDE code will have set hwif->present if we have devices attached,
+	 * if we don't, the discard the interface except if we are on a media bay slot
+	 */
+	if (!hwif->present && !pmif->mediabay) {
+		printk(KERN_INFO "ide%d: Bus empty, interface released.\n",
+			hwif->index);
+		default_hwif_iops(hwif);
+		for (i = IDE_DATA_OFFSET; i <= IDE_CONTROL_OFFSET; ++i)
+			hwif->io_ports[i] = 0;
+		hwif->chipset = ide_unknown;
+		hwif->noprobe = 1;
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+/*
+ * Attach to a macio probed interface
+ */
+static int __devinit
+pmac_ide_macio_attach(struct macio_dev *mdev, const struct of_match *match)
+{
+	unsigned long base, regbase;
+	int irq;
+	ide_hwif_t *hwif;
+	pmac_ide_hwif_t *pmif;
+	int i, rc;
+
+	i = 0;
+	while (i < MAX_HWIFS && (ide_hwifs[i].io_ports[IDE_DATA_OFFSET] != 0
+	    || pmac_ide[i].node != NULL))
+		++i;
+	if (i >= MAX_HWIFS) {
+		printk(KERN_ERR "ide-pmac: MacIO interface attach with no slot\n");
+		printk(KERN_ERR "          %s\n", mdev->ofdev.node->full_name);
+		return -ENODEV;
+	}
+
+	pmif = &pmac_ide[i];
+	hwif = &ide_hwifs[i];
+
+	if (mdev->ofdev.node->n_addrs == 0) {
+		printk(KERN_WARNING "ide%d: no address for %s\n",
+		       i, mdev->ofdev.node->full_name);
+		return -ENXIO;
+	}
+
+	/* Request memory resource for IO ports */
+	if (macio_request_resource(mdev, 0, "ide-pmac (ports)")) {
+		printk(KERN_ERR "ide%d: can't request mmio resource !\n", i);
+		return -EBUSY;
+	}
+			
+	/* XXX This is bogus. Should be fixed in the registry by checking
+	 * the kind of host interrupt controller, a bit like gatwick
+	 * fixes in irq.c. That works well enough for the single case
+	 * where that happens though...
+	 */
+	if (macio_irq_count(mdev) == 0) {
+		printk(KERN_WARNING "ide%d: no intrs for device %s, using 13\n",
+			i, mdev->ofdev.node->full_name);
+		irq = 13;
+	} else
+		irq = macio_irq(mdev, 0);
+
+	base =  (unsigned long)ioremap(macio_resource_start(mdev, 0), 0x400);
+	regbase = base;
+
+	hwif->pci_dev = mdev->bus->pdev;
+	hwif->gendev.parent = &mdev->ofdev.dev;
+
+	pmif->mdev = mdev;
+	pmif->node = mdev->ofdev.node;
+	pmif->regbase = regbase;
+	pmif->irq = irq;
+#ifdef CONFIG_BLK_DEV_IDEDMA_PMAC
+	if (macio_resource_count(mdev) >= 2) {
+		if (macio_request_resource(mdev, 1, "ide-pmac (dma)"))
+			printk(KERN_WARNING "ide%d: can't request DMA resource !\n", i);
+		else
+			pmif->dma_regs = (volatile struct dbdma_regs*)
+				ioremap(macio_resource_start(mdev, 1), 0x1000);
+	} else
+		pmif->dma_regs = NULL;
+#endif /* CONFIG_BLK_DEV_IDEDMA_PMAC */
+	dev_set_drvdata(&mdev->ofdev.dev, hwif);
+
+	rc = pmac_ide_setup_device(pmif, hwif);
+	if (rc != 0) {
+		/* The inteface is released to the common IDE layer */
+		dev_set_drvdata(&mdev->ofdev.dev, NULL);
+		iounmap((void *)base);
+		if (pmif->dma_regs)
+			iounmap((void *)pmif->dma_regs);
+		memset(pmif, 0, sizeof(*pmif));
+		macio_release_resource(mdev, 0);
+		if (pmif->dma_regs)
+			macio_release_resource(mdev, 1);
+	}
+
+	return rc;
+}
+
+static int
+pmac_ide_macio_suspend(struct macio_dev *mdev, u32 state)
+{
+	ide_hwif_t	*hwif = (ide_hwif_t *)dev_get_drvdata(&mdev->ofdev.dev);
+	int		rc = 0;
+
+	if (state != mdev->ofdev.dev.power_state && state >= 2) {
+		rc = pmac_ide_do_suspend(hwif);
+		if (rc == 0)
+			mdev->ofdev.dev.power_state = state;
+	}
+
+	return rc;
+}
+
+static int
+pmac_ide_macio_resume(struct macio_dev *mdev)
+{
+	ide_hwif_t	*hwif = (ide_hwif_t *)dev_get_drvdata(&mdev->ofdev.dev);
+	int		rc = 0;
+	
+	if (mdev->ofdev.dev.power_state != 0) {
+		rc = pmac_ide_do_resume(hwif);
+		if (rc == 0)
+			mdev->ofdev.dev.power_state = 0;
+	}
+
+	return rc;
+}
+
+/*
+ * Attach to a PCI probed interface
+ */
+static int __devinit
+pmac_ide_pci_attach(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+	ide_hwif_t *hwif;
+	struct device_node *np;
+	pmac_ide_hwif_t *pmif;
+	unsigned long base;
+	unsigned long rbase, rlen;
+	int i, rc;
+
+	np = pci_device_to_OF_node(pdev);
+	if (np == NULL) {
+		printk(KERN_ERR "ide-pmac: cannot find MacIO node for Kauai ATA interface\n");
+		return -ENODEV;
+	}
+	i = 0;
+	while (i < MAX_HWIFS && (ide_hwifs[i].io_ports[IDE_DATA_OFFSET] != 0
+	    || pmac_ide[i].node != NULL))
+		++i;
+	if (i >= MAX_HWIFS) {
+		printk(KERN_ERR "ide-pmac: PCI interface attach with no slot\n");
+		printk(KERN_ERR "          %s\n", np->full_name);
+		return -ENODEV;
+	}
+
+	pmif = &pmac_ide[i];
+	hwif = &ide_hwifs[i];
+
+	if (pci_enable_device(pdev)) {
+		printk(KERN_WARNING "ide%i: Can't enable PCI device for %s\n",
+			i, np->full_name);
+		return -ENXIO;
+	}
+	pci_set_master(pdev);
+			
+	if (pci_request_regions(pdev, "Kauai ATA")) {
+		printk(KERN_ERR "ide%d: Cannot obtain PCI resources for %s\n",
+			i, np->full_name);
+		return -ENXIO;
+	}
+
+	hwif->pci_dev = pdev;
+	hwif->gendev.parent = &pdev->dev;
+	pmif->mdev = NULL;
+	pmif->node = np;
+
+	rbase = pci_resource_start(pdev, 0);
+	rlen = pci_resource_len(pdev, 0);
+
+	base = (unsigned long) ioremap(rbase, rlen);
+	pmif->regbase = base + 0x2000;
+#ifdef CONFIG_BLK_DEV_IDEDMA_PMAC
+	pmif->dma_regs = (volatile struct dbdma_regs*)(base + 0x1000);
+#endif /* CONFIG_BLK_DEV_IDEDMA_PMAC */	
+
+	/* We use the OF node irq mapping */
+	if (np->n_intrs == 0)
+		pmif->irq = pdev->irq;
+	else
+		pmif->irq = np->intrs[0].line;
+
+	pci_set_drvdata(pdev, hwif);
+
+	rc = pmac_ide_setup_device(pmif, hwif);
+	if (rc != 0) {
+		/* The inteface is released to the common IDE layer */
+		pci_set_drvdata(pdev, NULL);
+		iounmap((void *)base);
+		memset(pmif, 0, sizeof(*pmif));
+		pci_release_regions(pdev);
+	}
+
+	return rc;
+}
+
+static int
+pmac_ide_pci_suspend(struct pci_dev *pdev, u32 state)
+{
+	ide_hwif_t	*hwif = (ide_hwif_t *)pci_get_drvdata(pdev);
+	int		rc = 0;
+	
+	if (state != pdev->dev.power_state && state >= 2) {
+		rc = pmac_ide_do_suspend(hwif);
+		if (rc == 0)
+			pdev->dev.power_state = state;
+	}
+
+	return rc;
+}
+
+static int
+pmac_ide_pci_resume(struct pci_dev *pdev)
+{
+	ide_hwif_t	*hwif = (ide_hwif_t *)pci_get_drvdata(pdev);
+	int		rc = 0;
+	
+	if (pdev->dev.power_state != 0) {
+		rc = pmac_ide_do_resume(hwif);
+		if (rc == 0)
+			pdev->dev.power_state = 0;
+	}
+
+	return rc;
+}
+
+static struct of_match pmac_ide_macio_match[] = 
+{
+	{
+	.name 		= "IDE",
+	.type		= OF_ANY_MATCH,
+	.compatible	= OF_ANY_MATCH
+	},
+	{
+	.name 		= "ATA",
+	.type		= OF_ANY_MATCH,
+	.compatible	= OF_ANY_MATCH
+	},
+	{
+	.name 		= OF_ANY_MATCH,
+	.type		= "ide",
+	.compatible	= OF_ANY_MATCH
+	},
+	{
+	.name 		= OF_ANY_MATCH,
+	.type		= "ata",
+	.compatible	= OF_ANY_MATCH
+	},
+	{},
+};
+
+static struct macio_driver pmac_ide_macio_driver = 
+{
+	.name 		= "ide-pmac",
+	.match_table	= pmac_ide_macio_match,
+	.probe		= pmac_ide_macio_attach,
+	.suspend	= pmac_ide_macio_suspend,
+	.resume		= pmac_ide_macio_resume,
+};
+
+static struct pci_device_id pmac_ide_pci_match[] = {
+	{ PCI_VENDOR_ID_APPLE, PCI_DEVIEC_ID_APPLE_UNI_N_ATA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{ PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_IPID_ATA100, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{ PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_K2_ATA100, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+};
+
+static struct pci_driver pmac_ide_pci_driver = {
+	.name		= "ide-pmac",
+	.id_table	= pmac_ide_pci_match,
+	.probe		= pmac_ide_pci_attach,
+	.suspend	= pmac_ide_pci_suspend,
+	.resume		= pmac_ide_pci_resume,
+};
+
+void __init
+pmac_ide_probe(void)
+{
+	if (_machine != _MACH_Pmac)
+		return;
+
+#ifdef CONFIG_BLK_DEV_IDE_PMAC_ATA100FIRST
+	pci_register_driver(&pmac_ide_pci_driver);
+	macio_register_driver(&pmac_ide_macio_driver);
+#else
+	macio_register_driver(&pmac_ide_macio_driver);
+	pci_register_driver(&pmac_ide_pci_driver);
+#endif	
+}
+
+#ifdef CONFIG_BLK_DEV_IDEDMA_PMAC
+
+/*
+ * This is very close to the generic ide-dma version of the function except
+ * that we don't use the fields in the hwif but our own copies for sg_table
+ * and friends. We build & map the sglist for a given request
+ */
+static int __pmac
+pmac_ide_build_sglist(ide_drive_t *drive, struct request *rq)
+{
+	ide_hwif_t *hwif = HWIF(drive);
+	pmac_ide_hwif_t *pmif = (pmac_ide_hwif_t *)hwif->hwif_data;
+	struct scatterlist *sg = pmif->sg_table;
+	int nents;
+
+	if (hwif->sg_dma_active)
+		BUG();
+		
+	nents = blk_rq_map_sg(drive->queue, rq, sg);
+		
+	if (rq_data_dir(rq) == READ)
+		pmif->sg_dma_direction = PCI_DMA_FROMDEVICE;
+	else
+		pmif->sg_dma_direction = PCI_DMA_TODEVICE;
+
+	return pci_map_sg(hwif->pci_dev, sg, nents, pmif->sg_dma_direction);
+}
+
+/*
+ * Same as above but for a "raw" taskfile request
+ */
+static int __pmac
+pmac_ide_raw_build_sglist(ide_drive_t *drive, struct request *rq)
+{
+	ide_hwif_t *hwif = HWIF(drive);
+	pmac_ide_hwif_t *pmif = (pmac_ide_hwif_t *)hwif->hwif_data;
+	struct scatterlist *sg = pmif->sg_table;
+	int nents = 0;
+	ide_task_t *args = rq->special;
+	unsigned char *virt_addr = rq->buffer;
+	int sector_count = rq->nr_sectors;
+
+	if (args->command_type == IDE_DRIVE_TASK_RAW_WRITE)
+		pmif->sg_dma_direction = PCI_DMA_TODEVICE;
+	else
+		pmif->sg_dma_direction = PCI_DMA_FROMDEVICE;
+	
+	if (sector_count > 128) {
+		memset(&sg[nents], 0, sizeof(*sg));
+		sg[nents].page = virt_to_page(virt_addr);
+		sg[nents].offset = offset_in_page(virt_addr);
+		sg[nents].length = 128  * SECTOR_SIZE;
+		nents++;
+		virt_addr = virt_addr + (128 * SECTOR_SIZE);
+		sector_count -= 128;
+	}
+	memset(&sg[nents], 0, sizeof(*sg));
+	sg[nents].page = virt_to_page(virt_addr);
+	sg[nents].offset = offset_in_page(virt_addr);
+	sg[nents].length =  sector_count  * SECTOR_SIZE;
+	nents++;
+   
+	return pci_map_sg(hwif->pci_dev, sg, nents, pmif->sg_dma_direction);
+}
+
+/*
+ * pmac_ide_build_dmatable builds the DBDMA command list
+ * for a transfer and sets the DBDMA channel to point to it.
+ */
+static int __pmac
+pmac_ide_build_dmatable(ide_drive_t *drive, struct request *rq)
+{
+	struct dbdma_cmd *table;
+	int i, count = 0;
+	ide_hwif_t *hwif = HWIF(drive);
+	pmac_ide_hwif_t* pmif = (pmac_ide_hwif_t *)hwif->hwif_data;
+	volatile struct dbdma_regs *dma = pmif->dma_regs;
+	struct scatterlist *sg;
+	int wr = (rq_data_dir(rq) == WRITE);
+
+	/* DMA table is already aligned */
+	table = (struct dbdma_cmd *) pmif->dma_table_cpu;
+
+	/* Make sure DMA controller is stopped (necessary ?) */
+	writel((RUN|PAUSE|FLUSH|WAKE|DEAD) << 16, &dma->control);
+	while (readl(&dma->status) & RUN)
+		udelay(1);
+
+	/* Build sglist */
+	if (HWGROUP(drive)->rq->flags & REQ_DRIVE_TASKFILE)
+		pmif->sg_nents = i = pmac_ide_raw_build_sglist(drive, rq);
+	else
+		pmif->sg_nents = i = pmac_ide_build_sglist(drive, rq);
+	if (!i)
+		return 0;
+
+	/* Build DBDMA commands list */
+	sg = pmif->sg_table;
+	while (i && sg_dma_len(sg)) {
+		u32 cur_addr;
+		u32 cur_len;
+
+		cur_addr = sg_dma_address(sg);
+		cur_len = sg_dma_len(sg);
+
+		if (pmif->broken_dma && cur_addr & (L1_CACHE_BYTES - 1)) {
+			if (pmif->broken_dma_warn == 0) {
+				printk(KERN_WARNING "%s: DMA on non aligned address,"
+				       "switching to PIO on Ohare chipset\n", drive->name);
+				pmif->broken_dma_warn = 1;
+			}
+			goto use_pio_instead;
+		}
+		while (cur_len) {
+			unsigned int tc = (cur_len < 0xfe00)? cur_len: 0xfe00;
+
+			if (count++ >= MAX_DCMDS) {
+				printk(KERN_WARNING "%s: DMA table too small\n",
+				       drive->name);
+				goto use_pio_instead;
+			}
+			st_le16(&table->command, wr? OUTPUT_MORE: INPUT_MORE);
+			st_le16(&table->req_count, tc);
+			st_le32(&table->phy_addr, cur_addr);
+			table->cmd_dep = 0;
+			table->xfer_status = 0;
+			table->res_count = 0;
+			cur_addr += tc;
+			cur_len -= tc;
+			++table;
+		}
+		sg++;
+		i--;
+	}
+
+	/* convert the last command to an input/output last command */
+	if (count) {
+		st_le16(&table[-1].command, wr? OUTPUT_LAST: INPUT_LAST);
+		/* add the stop command to the end of the list */
+		memset(table, 0, sizeof(struct dbdma_cmd));
+		st_le16(&table->command, DBDMA_STOP);
+		mb();
+		writel(pmif->dma_table_dma, &dma->cmdptr);
+		return 1;
+	}
+
+	printk(KERN_DEBUG "%s: empty DMA table?\n", drive->name);
+ use_pio_instead:
+	pci_unmap_sg(hwif->pci_dev,
+		     pmif->sg_table,
+		     pmif->sg_nents,
+		     pmif->sg_dma_direction);
+	hwif->sg_dma_active = 0;
+	return 0; /* revert to PIO for this request */
+}
+
+/* Teardown mappings after DMA has completed.  */
+static void __pmac
+pmac_ide_destroy_dmatable (ide_drive_t *drive)
+{
+	struct pci_dev *dev = HWIF(drive)->pci_dev;
+	pmac_ide_hwif_t* pmif = (pmac_ide_hwif_t *)HWIF(drive)->hwif_data;
+	struct scatterlist *sg = pmif->sg_table;
+	int nents = pmif->sg_nents;
+
+	if (nents) {
+		pci_unmap_sg(dev, sg, nents, pmif->sg_dma_direction);
+		pmif->sg_nents = 0;
+		HWIF(drive)->sg_dma_active = 0;
+	}
+}
+
+/*
+ * Pick up best MDMA timing for the drive and apply it
+ */
+static int __pmac
+pmac_ide_mdma_enable(ide_drive_t *drive, u16 mode)
+{
+	ide_hwif_t *hwif = HWIF(drive);
+	pmac_ide_hwif_t* pmif = (pmac_ide_hwif_t *)hwif->hwif_data;
+	int drive_cycle_time;
+	struct hd_driveid *id = drive->id;
+	u32 *timings, *timings2;
+	u32 timing_local[2];
+	int ret;
+
+	/* which drive is it ? */
+	timings = &pmif->timings[drive->select.b.unit & 0x01];
+	timings2 = &pmif->timings[(drive->select.b.unit & 0x01) + 2];
+
+	/* Check if drive provide explicit cycle time */
+	if ((id->field_valid & 2) && (id->eide_dma_time))
+		drive_cycle_time = id->eide_dma_time;
+	else
+		drive_cycle_time = 0;
+
+	/* Copy timings to local image */
+	timing_local[0] = *timings;
+	timing_local[1] = *timings2;
+
+	/* Calculate controller timings */
+	ret = set_timings_mdma(	drive, pmif->kind,
+				&timing_local[0],
+				&timing_local[1],
+				mode,
+				drive_cycle_time);
+	if (ret)
+		return 0;
+
+	/* Set feature on drive */
+    	printk(KERN_INFO "%s: Enabling MultiWord DMA %d\n", drive->name, mode & 0xf);
+	ret = pmac_ide_do_setfeature(drive, mode);
+	if (ret) {
+	    	printk(KERN_WARNING "%s: Failed !\n", drive->name);
+	    	return 0;
+	}
+
+	/* Apply timings to controller */
+	*timings = timing_local[0];
+	*timings2 = timing_local[1];
+	
+	/* Set speed info in drive */
+	drive->current_speed = mode;	
+	if (!drive->init_speed)
+		drive->init_speed = mode;
+
+	return 1;
+}
+
+/*
+ * Pick up best UDMA timing for the drive and apply it
+ */
+static int __pmac
+pmac_ide_udma_enable(ide_drive_t *drive, u16 mode)
+{
+	ide_hwif_t *hwif = HWIF(drive);
+	pmac_ide_hwif_t* pmif = (pmac_ide_hwif_t *)hwif->hwif_data;
+	u32 *timings, *timings2;
+	u32 timing_local[2];
+	int ret;
+		
+	/* which drive is it ? */
+	timings = &pmif->timings[drive->select.b.unit & 0x01];
+	timings2 = &pmif->timings[(drive->select.b.unit & 0x01) + 2];
+
+	/* Copy timings to local image */
+	timing_local[0] = *timings;
+	timing_local[1] = *timings2;
+	
+	/* Calculate timings for interface */
+	if (pmif->kind == controller_un_ata6 || pmif->kind == controller_k2_ata6)
+		ret = set_timings_udma_ata6(	&timing_local[0],
+						&timing_local[1],
+						mode);
+	else
+		ret = set_timings_udma_ata4(&timing_local[0], mode);
+	if (ret)
+		return 0;
+		
+	/* Set feature on drive */
+    	printk(KERN_INFO "%s: Enabling Ultra DMA %d\n", drive->name, mode & 0x0f);
+	ret = pmac_ide_do_setfeature(drive, mode);
+	if (ret) {
+		printk(KERN_WARNING "%s: Failed !\n", drive->name);
+		return 0;
+	}
+
+	/* Apply timings to controller */
+	*timings = timing_local[0];
+	*timings2 = timing_local[1];
+
+	/* Set speed info in drive */
+	drive->current_speed = mode;	
+	if (!drive->init_speed)
+		drive->init_speed = mode;
+
+	return 1;
+}
+
+/*
+ * Check what is the best DMA timing setting for the drive and
+ * call appropriate functions to apply it.
+ */
+static int __pmac
+pmac_ide_dma_check(ide_drive_t *drive)
+{
+	struct hd_driveid *id = drive->id;
+	ide_hwif_t *hwif = HWIF(drive);
+	pmac_ide_hwif_t* pmif = (pmac_ide_hwif_t *)hwif->hwif_data;
+	int enable = 1;
+	int map;
+	drive->using_dma = 0;
+	
+	if (drive->media == ide_floppy)
+		enable = 0;
+	if (((id->capability & 1) == 0) && !__ide_dma_good_drive(drive))
+		enable = 0;
+	if (__ide_dma_bad_drive(drive))
+		enable = 0;
+
+	if (enable) {
+		short mode;
+		
+		map = XFER_MWDMA;
+		if (pmif->kind == controller_kl_ata4 || pmif->kind == controller_un_ata6
+		    || pmif->kind == controller_k2_ata6) {
+			map |= XFER_UDMA;
+			if (pmif->cable_80) {
+				map |= XFER_UDMA_66;
+				if (pmif->kind == controller_un_ata6 ||
+				    pmif->kind == controller_k2_ata6)
+					map |= XFER_UDMA_100;
+			}
+		}
+		mode = ide_find_best_mode(drive, map);
+		if (mode & XFER_UDMA)
+			drive->using_dma = pmac_ide_udma_enable(drive, mode);
+		else if (mode & XFER_MWDMA)
+			drive->using_dma = pmac_ide_mdma_enable(drive, mode);
+		hwif->OUTB(0, IDE_CONTROL_REG);
+		/* Apply settings to controller */
+		pmac_ide_do_update_timings(drive);
+	}
+	return 0;
+}
+
+/*
+ * Prepare a DMA transfer. We build the DMA table, adjust the timings for
+ * a read on KeyLargo ATA/66 and mark us as waiting for DMA completion
+ */
+static int __pmac
+pmac_ide_dma_start(ide_drive_t *drive, int reading)
+{
+	ide_hwif_t *hwif = HWIF(drive);
+	pmac_ide_hwif_t* pmif = (pmac_ide_hwif_t *)hwif->hwif_data;
+	struct request *rq = HWGROUP(drive)->rq;
+	u8 unit = (drive->select.b.unit & 0x01);
+	u8 ata4;
+
+	if (pmif == NULL)
+		return 1;
+	ata4 = (pmif->kind == controller_kl_ata4);	
+
+	if (!pmac_ide_build_dmatable(drive, rq))
+		return 1;
+
+	/* Apple adds 60ns to wrDataSetup on reads */
+	if (ata4 && (pmif->timings[unit] & TR_66_UDMA_EN)) {
+		writel(pmif->timings[unit] + (reading ? 0x00800000UL : 0),
+			(unsigned *)(IDE_DATA_REG+IDE_TIMING_CONFIG));
+		(void)readl((unsigned *)(IDE_DATA_REG + IDE_TIMING_CONFIG));
+	}
+
+	drive->waiting_for_dma = 1;
+
+	return 0;
+}
+
+/*
+ * Start a DMA READ command
+ */
+static int __pmac
+pmac_ide_dma_read(ide_drive_t *drive)
+{
+	struct request *rq = HWGROUP(drive)->rq;
+	u8 lba48 = (drive->addressing == 1) ? 1 : 0;
+	task_ioreg_t command = WIN_NOP;
+
+	if (pmac_ide_dma_start(drive, 1))
+		return 1;
+
+	if (drive->media != ide_disk)
+		return 0;
+
+	command = (lba48) ? WIN_READDMA_EXT : WIN_READDMA;
+	
+	if (drive->vdma)
+		command = (lba48) ? WIN_READ_EXT: WIN_READ;
+		
+	if (rq->flags & REQ_DRIVE_TASKFILE) {
+		ide_task_t *args = rq->special;
+		command = args->tfRegister[IDE_COMMAND_OFFSET];
+	}
+
+	/* issue cmd to drive */
+	ide_execute_command(drive, command, &ide_dma_intr, 2*WAIT_CMD, NULL);
+
+	return pmac_ide_dma_begin(drive);
+}
+
+/*
+ * Start a DMA WRITE command
+ */
+static int __pmac
+pmac_ide_dma_write (ide_drive_t *drive)
+{
+	struct request *rq = HWGROUP(drive)->rq;
+	u8 lba48 = (drive->addressing == 1) ? 1 : 0;
+	task_ioreg_t command = WIN_NOP;
+
+	if (pmac_ide_dma_start(drive, 0))
+		return 1;
+
+	if (drive->media != ide_disk)
+		return 0;
+
+	command = (lba48) ? WIN_WRITEDMA_EXT : WIN_WRITEDMA;
+	if (drive->vdma)
+		command = (lba48) ? WIN_WRITE_EXT: WIN_WRITE;
+		
+	if (rq->flags & REQ_DRIVE_TASKFILE) {
+		ide_task_t *args = rq->special;
+		command = args->tfRegister[IDE_COMMAND_OFFSET];
+	}
+
+	/* issue cmd to drive */
+	ide_execute_command(drive, command, &ide_dma_intr, 2*WAIT_CMD, NULL);
+
+	return pmac_ide_dma_begin(drive);
+}
+
+/*
+ * Kick the DMA controller into life after the DMA command has been issued
+ * to the drive.
+ */
+static int __pmac
+pmac_ide_dma_begin (ide_drive_t *drive)
+{
+	pmac_ide_hwif_t* pmif = (pmac_ide_hwif_t *)HWIF(drive)->hwif_data;
+	volatile struct dbdma_regs *dma;
+
+	if (pmif == NULL)
+		return 1;
+	dma = pmif->dma_regs;
+
+	writel((RUN << 16) | RUN, &dma->control);
+	/* Make sure it gets to the controller right now */
+	(void)readl(&dma->control);
+	return 0;
+}
+
+/*
+ * After a DMA transfer, make sure the controller is stopped
+ */
+static int __pmac
+pmac_ide_dma_end (ide_drive_t *drive)
+{
+	pmac_ide_hwif_t* pmif = (pmac_ide_hwif_t *)HWIF(drive)->hwif_data;
+	volatile struct dbdma_regs *dma;
+	u32 dstat;
+	
+	if (pmif == NULL)
+		return 0;
+	dma = pmif->dma_regs;
+
+	drive->waiting_for_dma = 0;
+	dstat = readl(&dma->status);
+	writel(((RUN|WAKE|DEAD) << 16), &dma->control);
+	pmac_ide_destroy_dmatable(drive);
+	/* verify good dma status. we don't check for ACTIVE beeing 0. We should...
+	 * in theory, but with ATAPI decices doing buffer underruns, that would
+	 * cause us to disable DMA, which isn't what we want
+	 */
+	return (dstat & (RUN|DEAD)) != RUN;
+}
+
+/*
+ * Check out that the interrupt we got was for us. We can't always know this
+ * for sure with those Apple interfaces (well, we could on the recent ones but
+ * that's not implemented yet), on the other hand, we don't have shared interrupts
+ * so it's not really a problem
+ */
+static int __pmac
+pmac_ide_dma_test_irq (ide_drive_t *drive)
+{
+	pmac_ide_hwif_t* pmif = (pmac_ide_hwif_t *)HWIF(drive)->hwif_data;
+	volatile struct dbdma_regs *dma;
+	unsigned long status, timeout;
+
+	if (pmif == NULL)
+		return 0;
+	dma = pmif->dma_regs;
+
+	/* We have to things to deal with here:
+	 * 
+	 * - The dbdma won't stop if the command was started
+	 * but completed with an error without transferring 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.
+	 * 
+	 */
+
+	/* If ACTIVE is cleared, the STOP command have passed and
+	 * transfer is complete.
+	 */
+	status = readl(&dma->status);
+	if (!(status & ACTIVE))
+		return 1;
+	if (!drive->waiting_for_dma)
+		printk(KERN_WARNING "ide%d, ide_dma_test_irq \
+			called while not waiting\n", HWIF(drive)->index);
+
+	/* If dbdma didn't execute the STOP command yet, the
+	 * active bit is still set. We consider that we aren't
+	 * sharing interrupts (which is hopefully the case with
+	 * those controllers) and so we just try to flush the
+	 * channel for pending data in the fifo
+	 */
+	udelay(1);
+	writel((FLUSH << 16) | FLUSH, &dma->control);
+	timeout = 0;
+	for (;;) {
+		udelay(1);
+		status = readl(&dma->status);
+		if ((status & FLUSH) == 0)
+			break;
+		if (++timeout > 100) {
+			printk(KERN_WARNING "ide%d, ide_dma_test_irq \
+			timeout flushing channel\n", HWIF(drive)->index);
+			break;
+		}
+	}	
+	return 1;
+}
+
+static int __pmac
+pmac_ide_dma_host_off (ide_drive_t *drive)
+{
+	return 0;
+}
+
+static int __pmac
+pmac_ide_dma_host_on (ide_drive_t *drive)
+{
+	return 0;
+}
+
+static int __pmac
+pmac_ide_dma_lostirq (ide_drive_t *drive)
+{
+	pmac_ide_hwif_t* pmif = (pmac_ide_hwif_t *)HWIF(drive)->hwif_data;
+	volatile struct dbdma_regs *dma;
+	unsigned long status;
+
+	if (pmif == NULL)
+		return 0;
+	dma = pmif->dma_regs;
+
+	status = readl(&dma->status);
+	printk(KERN_ERR "ide-pmac lost interrupt, dma status: %lx\n", status);
+	return 0;
+}
+
+/*
+ * Allocate the data structures needed for using DMA with an interface
+ * and fill the proper list of functions pointers
+ */
+static void __init 
+pmac_ide_setup_dma(pmac_ide_hwif_t *pmif, ide_hwif_t *hwif)
+{
+	/* We won't need pci_dev if we switch to generic consistent
+	 * DMA routines ...
+	 */
+	if (hwif->pci_dev == NULL)
+		return;
+	/*
+	 * Allocate space for the DBDMA commands.
+	 * The +2 is +1 for the stop command and +1 to allow for
+	 * aligning the start address to a multiple of 16 bytes.
+	 */
+	pmif->dma_table_cpu = (struct dbdma_cmd*)pci_alloc_consistent(
+		hwif->pci_dev,
+		(MAX_DCMDS + 2) * sizeof(struct dbdma_cmd),
+		&pmif->dma_table_dma);
+	if (pmif->dma_table_cpu == NULL) {
+		printk(KERN_ERR "%s: unable to allocate DMA command list\n",
+		       hwif->name);
+		return;
+	}
+
+	pmif->sg_table = kmalloc(sizeof(struct scatterlist) * MAX_DCMDS,
+				 GFP_KERNEL);
+	if (pmif->sg_table == NULL) {
+		pci_free_consistent(	hwif->pci_dev,
+					(MAX_DCMDS + 2) * sizeof(struct dbdma_cmd),
+				    	pmif->dma_table_cpu, pmif->dma_table_dma);
+		return;
+	}
+	hwif->ide_dma_off_quietly = &__ide_dma_off_quietly;
+	hwif->ide_dma_on = &__ide_dma_on;
+	hwif->ide_dma_check = &pmac_ide_dma_check;
+	hwif->ide_dma_read = &pmac_ide_dma_read;
+	hwif->ide_dma_write = &pmac_ide_dma_write;
+	hwif->ide_dma_begin = &pmac_ide_dma_begin;
+	hwif->ide_dma_end = &pmac_ide_dma_end;
+	hwif->ide_dma_test_irq = &pmac_ide_dma_test_irq;
+	hwif->ide_dma_host_off = &pmac_ide_dma_host_off;
+	hwif->ide_dma_host_on = &pmac_ide_dma_host_on;
+	hwif->ide_dma_verbose = &__ide_dma_verbose;
+	hwif->ide_dma_timeout = &__ide_dma_timeout;
+	hwif->ide_dma_lostirq = &pmac_ide_dma_lostirq;
+
+#ifdef CONFIG_BLK_DEV_IDEDMA_PMAC_AUTO
+	if (!noautodma)
+		hwif->autodma = 1;
+#endif
+	hwif->drives[0].autodma = hwif->autodma;
+	hwif->drives[1].autodma = hwif->autodma;
+
+	hwif->atapi_dma = 1;
+	switch(pmif->kind) {
+		case controller_un_ata6:
+		case controller_k2_ata6:
+			hwif->ultra_mask = pmif->cable_80 ? 0x3f : 0x07;
+			hwif->mwdma_mask = 0x07;
+			hwif->swdma_mask = 0x00;
+			break;
+		case controller_kl_ata4:
+			hwif->ultra_mask = pmif->cable_80 ? 0x1f : 0x07;
+			hwif->mwdma_mask = 0x07;
+			hwif->swdma_mask = 0x00;
+			break;
+		default:
+			hwif->ultra_mask = 0x00;
+			hwif->mwdma_mask = 0x07;
+			hwif->swdma_mask = 0x00;
+			break;
+	}	
+}
+
+#endif /* CONFIG_BLK_DEV_IDEDMA_PMAC */
diff -ubw -Naur linux-2.6.8.orig/drivers/macintosh/Kconfig linux-2.6.8/drivers/macintosh/Kconfig
--- linux-2.6.8.orig/drivers/macintosh/Kconfig	2004-08-14 07:36:56.000000000 +0200
+++ linux-2.6.8/drivers/macintosh/Kconfig	2004-08-14 18:23:39.973581056 +0200
@@ -80,7 +80,7 @@
 
 config PMAC_PBOOK
 	bool "Power management support for PowerBooks"
-	depends on ADB_PMU
+	depends on PM && ADB_PMU
 	---help---
 	  This provides support for putting a PowerBook to sleep; it also
 	  enables media bay support.  Power management works on the
@@ -97,11 +97,6 @@
 	  have it autoloaded. The act of removing the module shuts down the
 	  sound hardware for more power savings.
 
-config PM
-	bool
-	depends on PPC_PMAC && ADB_PMU && PMAC_PBOOK
-	default y
-
 config PMAC_APM_EMU
 	tristate "APM emulation"
 	depends on PMAC_PBOOK
diff -ubw -Naur linux-2.6.8.orig/drivers/macintosh/mediabay.c linux-2.6.8/drivers/macintosh/mediabay.c
--- linux-2.6.8.orig/drivers/macintosh/mediabay.c	2004-08-14 07:36:32.000000000 +0200
+++ linux-2.6.8/drivers/macintosh/mediabay.c	2004-08-14 18:23:39.978580296 +0200
@@ -713,7 +713,7 @@
 {
 	struct media_bay_info	*bay = macio_get_drvdata(mdev);
 
-	if (state != mdev->ofdev.dev.power_state && state >= 2) {
+	if (state != mdev->ofdev.dev.power_state && state == PM_SUSPEND_MEM) {
 		down(&bay->lock);
 		bay->sleeping = 1;
 		set_mb_power(bay, 0);
diff -ubw -Naur linux-2.6.8.orig/drivers/macintosh/mediabay.c.orig linux-2.6.8/drivers/macintosh/mediabay.c.orig
--- linux-2.6.8.orig/drivers/macintosh/mediabay.c.orig	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.8/drivers/macintosh/mediabay.c.orig	2004-08-14 07:36:32.000000000 +0200
@@ -0,0 +1,856 @@
+/*
+ * Driver for the media bay on the PowerBook 3400 and 2400.
+ *
+ * Copyright (C) 1998 Paul Mackerras.
+ *
+ * Various evolutions by Benjamin Herrenschmidt & Henry Worth
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version
+ *  2 of the License, or (at your option) any later version.
+ */
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/hdreg.h>
+#include <linux/stddef.h>
+#include <linux/init.h>
+#include <linux/ide.h>
+#include <asm/prom.h>
+#include <asm/pgtable.h>
+#include <asm/io.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>
+
+
+#define MB_DEBUG
+#define MB_IGNORE_SIGNALS
+
+#ifdef MB_DEBUG
+#define MBDBG(fmt, arg...)	printk(KERN_INFO fmt , ## arg)
+#else
+#define MBDBG(fmt, arg...)	do { } while (0)
+#endif
+
+#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;
+	void	(*init)(struct media_bay_info *bay);
+	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 u32*			base;
+	int				content_id;
+	int				state;
+	int				last_value;
+	int				value_count;
+	int				timer;
+	struct macio_dev		*mdev;
+	struct mb_ops*			ops;
+	int				index;
+	int				cached_gpio;
+	int				sleeping;
+	struct semaphore		lock;
+#ifdef CONFIG_BLK_DEV_IDE
+	unsigned long			cd_base;
+	int 				cd_index;
+	int				cd_irq;
+	int				cd_retry;
+#endif
+};
+
+#define MAX_BAYS	2
+
+static struct media_bay_info media_bays[MAX_BAYS];
+int media_bay_count = 0;
+
+#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)	((readb(media_bays[i].cd_base + 0x70) & 0x80) == 0)
+#endif
+
+/* Note: All delays are not in milliseconds and converted to HZ relative
+ * values by the macro below
+ */
+#define MS_TO_HZ(ms)	((ms * HZ + 999) / 1000)
+
+/*
+ * Wait that number of ms between each step in normal polling mode
+ */
+#define MB_POLL_DELAY	25
+
+/*
+ * Consider the media-bay ID value stable if it is the same for
+ * this number of milliseconds
+ */
+#define MB_STABLE_DELAY	100
+
+/* Wait after powering up the media bay this delay in ms
+ * timeout bumped for some powerbooks
+ */
+#define MB_POWER_DELAY	200
+
+/*
+ * Hold the media-bay reset signal true for this many ticks
+ * after a device is inserted before releasing it.
+ */
+#define MB_RESET_DELAY	50
+
+/*
+ * Wait this long after the reset signal is released and before doing
+ * further operations. After this delay, the IDE reset signal is released
+ * too for an IDE device
+ */
+#define MB_SETUP_DELAY	100
+
+/*
+ * Wait this many ticks after an IDE device (e.g. CD-ROM) is inserted
+ * (or until the device is ready) before waiting for busy bit to disappear
+ */
+#define MB_IDE_WAIT	1000
+
+/*
+ * Timeout waiting for busy bit of an IDE device to go down
+ */
+#define MB_IDE_TIMEOUT	5000
+
+/*
+ * Max retries of the full power up/down sequence for an IDE device
+ */
+#define MAX_CD_RETRIES	3
+
+/*
+ * States of a media bay
+ */
+enum {
+	mb_empty = 0,		/* Idle */
+	mb_powering_up,		/* power bit set, waiting MB_POWER_DELAY */
+	mb_enabling_bay,	/* enable bits set, waiting MB_RESET_DELAY */
+	mb_resetting,		/* reset bit unset, waiting MB_SETUP_DELAY */
+	mb_ide_resetting,	/* IDE reset bit unser, waiting MB_IDE_WAIT */
+	mb_ide_waiting,		/* Waiting for BUSY bit to go away until MB_IDE_TIMEOUT */
+	mb_up,			/* Media bay full */
+	mb_powering_down	/* Powering down (avoid too fast down/up) */
+};
+
+#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
+
+/*
+ * 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;
+}
+
+/*
+ * Functions for powering up/down the bay, puts the bay device
+ * into reset state as well
+ */
+
+static void __pmac
+ohare_mb_power(struct media_bay_info* bay, int on_off)
+{
+	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);
+	}
+	MB_BIC(bay, OHARE_MBCR, 0x00000F00);
+}
+
+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);
+}
+
+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);
+}
+
+/*
+ * Functions for configuring the media bay for a given type of device,
+ * enable the related busses
+ */
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+/*
+ * Functions for tweaking resets
+ */
+
+static void __pmac
+ohare_mb_un_reset(struct media_bay_info* bay)
+{
+	MB_BIS(bay, OHARE_FCR, OH_BAY_RESET_N);
+}
+
+static void __pmac keylargo_mb_init(struct media_bay_info *bay)
+{
+	MB_BIS(bay, KEYLARGO_MBCR, KL_MBCR_MB0_ENABLE);
+}
+
+static void __pmac 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);
+}
+
+static void __pmac keylargo_mb_un_reset_ide(struct media_bay_info* bay)
+{
+	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) {
+		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);
+	}
+	bay->timer = MS_TO_HZ(MB_POWER_DELAY);
+}
+
+static void __pmac poll_media_bay(struct media_bay_info* bay)
+{
+	int id = bay->ops->content(bay);
+
+	if (id == bay->last_value) {
+		if (id != bay->content_id) {
+			bay->value_count += MS_TO_HZ(MB_POLL_DELAY);
+			if (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);
+				}
+				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;
+	}
+}
+
+int __pmac check_media_bay(struct device_node *which_bay, int what)
+{
+#ifdef CONFIG_BLK_DEV_IDE
+	int	i;
+
+	for (i=0; i<media_bay_count; i++)
+		if (media_bays[i].mdev && which_bay == media_bays[i].mdev->ofdev.node) {
+			if ((what == media_bays[i].content_id) && media_bays[i].state == mb_up)
+				return 0;
+			media_bays[i].cd_index = -1;
+			return -EINVAL;
+		}
+#endif /* CONFIG_BLK_DEV_IDE */
+	return -ENODEV;
+}
+EXPORT_SYMBOL(check_media_bay);
+
+int __pmac check_media_bay_by_base(unsigned long base, int what)
+{
+#ifdef CONFIG_BLK_DEV_IDE
+	int	i;
+
+	for (i=0; i<media_bay_count; i++)
+		if (media_bays[i].mdev && base == media_bays[i].cd_base) {
+			if ((what == media_bays[i].content_id) && media_bays[i].state == mb_up)
+				return 0;
+			media_bays[i].cd_index = -1;
+			return -EINVAL;
+		} 
+#endif
+	
+	return -ENODEV;
+}
+
+int __pmac media_bay_set_ide_infos(struct device_node* which_bay, unsigned long base,
+	int irq, int index)
+{
+#ifdef CONFIG_BLK_DEV_IDE
+	int	i;
+
+	for (i=0; i<media_bay_count; i++) {
+		struct media_bay_info* bay = &media_bays[i];
+
+		if (bay->mdev && which_bay == bay->mdev->ofdev.node) {
+			int timeout = 5000;
+			
+			down(&bay->lock);
+
+ 			bay->cd_base	= base;
+			bay->cd_irq	= irq;
+
+			if ((MB_CD != bay->content_id) || bay->state != mb_up) {
+				up(&bay->lock);
+				return 0;
+			}
+			printk(KERN_DEBUG "Registered ide%d for media bay %d\n", index, i);
+			do {
+				if (MB_IDE_READY(i)) {
+					bay->cd_index	= index;
+					up(&bay->lock);
+					return 0;
+				}
+				mdelay(1);
+			} while(--timeout);
+			printk(KERN_DEBUG "Timeount waiting IDE in bay %d\n", i);
+			up(&bay->lock);
+			return -ENODEV;
+		}
+	}
+#endif /* CONFIG_BLK_DEV_IDE */
+	
+	return -ENODEV;
+}
+
+static void __pmac media_bay_step(int 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(bay);
+
+	/* If timer expired or polling IDE busy, run state machine */
+	if ((bay->state != mb_ide_waiting) && (bay->timer != 0)) {
+		bay->timer -= MS_TO_HZ(MB_POLL_DELAY);
+		if (bay->timer > 0)
+			return;
+		bay->timer = 0;
+	}
+
+	switch(bay->state) {
+	case mb_powering_up:
+	    	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:
+		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);
+	    	break;
+	    
+	case mb_resetting:
+		if (bay->content_id != MB_CD) {
+			MBDBG("mediabay%d: bay is up (kind:%d)\n", i, bay->content_id);
+			bay->state = mb_up;
+			break;
+	    	}
+#ifdef CONFIG_BLK_DEV_IDE
+		MBDBG("mediabay%d: waiting IDE reset (kind:%d)\n", i, bay->content_id);
+		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(bay, 0);
+#endif /* CONFIG_BLK_DEV_IDE */
+	    	break;
+	    
+#ifdef CONFIG_BLK_DEV_IDE
+	case mb_ide_resetting:
+	    	bay->timer = MS_TO_HZ(MB_IDE_TIMEOUT);
+	    	bay->state = mb_ide_waiting;
+		MBDBG("mediabay%d: waiting IDE ready (kind:%d)\n", i, bay->content_id);
+	    	break;
+	    
+	case mb_ide_waiting:
+		if (bay->cd_base == 0) {
+			bay->timer = 0;
+			bay->state = mb_up;
+			MBDBG("mediabay%d: up before IDE init\n", i);
+			break;
+		} else if (MB_IDE_READY(i)) {
+			bay->timer = 0;
+			bay->state = mb_up;
+			if (bay->cd_index < 0) {
+				hw_regs_t hw;
+
+				printk("mediabay %d, registering IDE...\n", i);
+				pmu_suspend();
+				ide_init_hwif_ports(&hw, (unsigned long) bay->cd_base, (unsigned long) 0, NULL);
+				hw.irq = bay->cd_irq;
+				hw.chipset = ide_pmac;
+				bay->cd_index = ide_register_hw(&hw, NULL);
+				pmu_resume();
+			}
+			if (bay->cd_index == -1) {
+				/* We eventually do a retry */
+				bay->cd_retry++;
+				printk("IDE register error\n");
+				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);
+			}
+			break;
+	    	} else if (bay->timer > 0)
+			bay->timer -= MS_TO_HZ(MB_POLL_DELAY);
+	    	if (bay->timer <= 0) {
+			printk("\nIDE Timeout in bay %d !, IDE state is: 0x%02x\n",
+			       i, readb(bay->cd_base + 0x70));
+			MBDBG("mediabay%d: nIDE Timeout !\n", i);
+			set_mb_power(bay, 0);
+			bay->timer = 0;
+	    	}
+		break;
+#endif /* CONFIG_BLK_DEV_IDE */
+
+	case mb_powering_down:
+	    	bay->state = mb_empty;
+#ifdef CONFIG_BLK_DEV_IDE
+    	        if (bay->cd_index >= 0) {
+			printk(KERN_DEBUG "Unregistering mb %d ide, index:%d\n", i,
+			       bay->cd_index);
+			ide_unregister(bay->cd_index);
+			bay->cd_index = -1;
+		}
+	    	if (bay->cd_retry) {
+			if (bay->cd_retry > MAX_CD_RETRIES) {
+				/* Should add an error sound (sort of beep in dmasound) */
+				printk("\nmedia-bay %d, IDE device badly inserted or unrecognised\n", i);
+			} else {
+				/* Force a new power down/up sequence */
+				bay->content_id = MB_NO;
+			}
+	    	}
+#endif /* CONFIG_BLK_DEV_IDE */    
+		MBDBG("mediabay%d: end of power down\n", i);
+	    	break;
+	}
+}
+
+/*
+ * This procedure runs as a kernel thread to poll the media bay
+ * once each tick and register and unregister the IDE interface
+ * with the IDE driver.  It needs to be a thread because
+ * ide_register can't be called from interrupt context.
+ */
+static int __pmac media_bay_task(void *x)
+{
+	int	i;
+
+	strcpy(current->comm, "media-bay");
+#ifdef MB_IGNORE_SIGNALS
+	sigfillset(&current->blocked);
+#endif
+
+	for (;;) {
+		for (i = 0; i < media_bay_count; ++i) {
+			down(&media_bays[i].lock);
+			if (!media_bays[i].sleeping)
+				media_bay_step(i);
+			up(&media_bays[i].lock);
+		}
+
+		current->state = TASK_INTERRUPTIBLE;
+		schedule_timeout(MS_TO_HZ(MB_POLL_DELAY));
+		if (signal_pending(current))
+			return 0;
+	}
+}
+
+static int __devinit media_bay_attach(struct macio_dev *mdev, const struct of_match *match)
+{
+	struct media_bay_info* bay;
+	volatile u32 *regbase;
+	struct device_node *ofnode;
+	int i;
+
+	ofnode = mdev->ofdev.node;
+
+	if (macio_resource_count(mdev) < 1)
+		return -ENODEV;
+	if (macio_request_resources(mdev, "media-bay"))
+		return -EBUSY;
+	/* Media bay registers are located at the beginning of the
+         * mac-io chip, we get the parent address for now (hrm...)
+         */
+	regbase = (volatile u32 *)ioremap(ofnode->parent->addrs[0].address, 0x100);
+	if (regbase == NULL) {
+		macio_release_resources(mdev);
+		return -ENOMEM;
+	}
+	
+	i = media_bay_count++;
+	bay = &media_bays[i];
+	bay->mdev = mdev;
+	bay->base = regbase;
+	bay->index = i;
+	bay->ops = match->data;
+	bay->sleeping = 0;
+	init_MUTEX(&bay->lock);
+
+	/* Init HW probing */
+	if (bay->ops->init)
+		bay->ops->init(bay);
+
+	printk(KERN_INFO "mediabay%d: Registered %s media-bay\n", i, bay->ops->name);
+
+	/* Force an immediate detect */
+	set_mb_power(bay, 0);
+	msleep(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 {
+		msleep(MB_POLL_DELAY);
+		media_bay_step(i);
+	} while((bay->state != mb_empty) &&
+		(bay->state != mb_up));
+
+	/* Mark us ready by filling our mdev data */
+	macio_set_drvdata(mdev, bay);
+
+	/* Startup kernel thread */
+	if (i == 0)
+		kernel_thread(media_bay_task, NULL, CLONE_KERNEL);
+
+	return 0;
+
+}
+
+static int __pmac media_bay_suspend(struct macio_dev *mdev, u32 state)
+{
+	struct media_bay_info	*bay = macio_get_drvdata(mdev);
+
+	if (state != mdev->ofdev.dev.power_state && state >= 2) {
+		down(&bay->lock);
+		bay->sleeping = 1;
+		set_mb_power(bay, 0);
+		up(&bay->lock);
+		msleep(MB_POLL_DELAY);
+		mdev->ofdev.dev.power_state = state;
+	}
+	return 0;
+}
+
+static int __pmac media_bay_resume(struct macio_dev *mdev)
+{
+	struct media_bay_info	*bay = macio_get_drvdata(mdev);
+
+	if (mdev->ofdev.dev.power_state != 0) {
+		mdev->ofdev.dev.power_state = 0;
+
+	       	/* We re-enable the bay using it's previous content
+	       	   only if it did not change. Note those bozo timings,
+	       	   they seem to help the 3400 get it right.
+	       	 */
+	       	/* Force MB power to 0 */
+		down(&bay->lock);
+	       	set_mb_power(bay, 0);
+		msleep(MB_POWER_DELAY);
+	       	if (bay->ops->content(bay) != bay->content_id) {
+			printk("mediabay%d: content changed during sleep...\n", bay->index);
+			up(&bay->lock);
+	       		return 0;
+		}
+	       	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);
+#ifdef CONFIG_BLK_DEV_IDE
+	       	bay->cd_retry = 0;
+#endif
+	       	do {
+			msleep(MB_POLL_DELAY);
+	       		media_bay_step(bay->index);
+	       	} while((bay->state != mb_empty) &&
+	       		(bay->state != mb_up));
+		bay->sleeping = 0;
+		up(&bay->lock);
+	}
+	return 0;
+}
+
+
+/* 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",
+	.init		= keylargo_mb_init,
+	.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.
+ */
+
+static struct of_match media_bay_match[] =
+{
+	{
+	.name		= "media-bay",
+	.type		= OF_ANY_MATCH,
+	.compatible	= "keylargo-media-bay",
+	.data		= &keylargo_mb_ops,
+	},
+	{
+	.name		= "media-bay",
+	.type		= OF_ANY_MATCH,
+	.compatible	= "heathrow-media-bay",
+	.data		= &heathrow_mb_ops,
+	},
+	{
+	.name		= "media-bay",
+	.type		= OF_ANY_MATCH,
+	.compatible	= "ohare-media-bay",
+	.data		= &ohare_mb_ops,
+	},
+	{},
+};
+
+static struct macio_driver media_bay_driver =
+{
+	.name		= "media-bay",
+	.match_table	= media_bay_match,
+	.probe		= media_bay_attach,
+	.suspend	= media_bay_suspend,
+	.resume		= media_bay_resume
+};
+
+static int __init media_bay_init(void)
+{
+	int 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 (_machine != _MACH_Pmac)
+		return -ENODEV;
+
+	macio_register_driver(&media_bay_driver);	
+
+	return 0;
+}
+
+device_initcall(media_bay_init);
diff -ubw -Naur linux-2.6.8.orig/drivers/macintosh/via-pmu.c linux-2.6.8/drivers/macintosh/via-pmu.c
--- linux-2.6.8.orig/drivers/macintosh/via-pmu.c	2004-08-14 07:37:41.000000000 +0200
+++ linux-2.6.8/drivers/macintosh/via-pmu.c	2004-08-14 18:23:39.988578776 +0200
@@ -43,6 +43,7 @@
 #include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/device.h>
+#include <linux/sysdev.h>
 #include <linux/suspend.h>
 #include <linux/syscalls.h>
 #include <asm/prom.h>
@@ -2324,7 +2325,7 @@
 	/* Sync the disks. */
 	/* XXX It would be nice to have some way to ensure that
 	 * nobody is dirtying any new buffers while we wait. That
-	 * could be acheived using the refrigerator for processes
+	 * could be achieved using the refrigerator for processes
 	 * that swsusp uses
 	 */
 	sys_sync();
@@ -2377,7 +2378,6 @@
 
 	/* Wait for completion of async backlight requests */
 	while (!bright_req_1.complete || !bright_req_2.complete ||
-
 			!batt_req.complete)
 		pmu_poll();
 
@@ -3046,6 +3046,88 @@
 }
 #endif /* DEBUG_SLEEP */
 
+
+/* FIXME: This is a temporary set of callbacks to enable us
+ * to do suspend-to-disk.
+ */
+
+#ifdef CONFIG_PM
+
+static int pmu_sys_suspended = 0;
+
+static int pmu_sys_suspend(struct sys_device *sysdev, u32 state)
+{
+	if (state != PM_SUSPEND_DISK || pmu_sys_suspended)
+		return 0;
+
+	/* Suspend PMU event interrupts */
+	pmu_suspend();
+
+	pmu_sys_suspended = 1;
+	return 0;
+}
+
+static int pmu_sys_resume(struct sys_device *sysdev)
+{
+	struct adb_request req;
+
+	if (!pmu_sys_suspended)
+		return 0;
+
+	/* Tell PMU we are ready */
+	pmu_request(&req, NULL, 2, PMU_SYSTEM_READY, 2);
+	pmu_wait_complete(&req);
+
+	/* Resume PMU event interrupts */
+	pmu_resume();
+
+	pmu_sys_suspended = 0;
+
+	return 0;
+}
+
+#endif /* CONFIG_PM */
+
+static struct sysdev_class pmu_sysclass = {
+	set_kset_name("pmu"),
+};
+
+static struct sys_device device_pmu = {
+	.id		= 0,
+	.cls		= &pmu_sysclass,
+};
+
+static struct sysdev_driver driver_pmu = {
+#ifdef CONFIG_PM
+	.suspend	= &pmu_sys_suspend,
+	.resume		= &pmu_sys_resume,
+#endif /* CONFIG_PM */
+};
+
+static int __init init_pmu_sysfs(void)
+{
+	int rc;
+
+	rc = sysdev_class_register(&pmu_sysclass);
+	if (rc) {
+		printk(KERN_ERR "Failed registering PMU sys class\n");
+		return -ENODEV;
+	}
+	rc = sysdev_register(&device_pmu);
+	if (rc) {
+		printk(KERN_ERR "Failed registering PMU sys device\n");
+		return -ENODEV;
+	}
+	rc = sysdev_driver_register(&pmu_sysclass, &driver_pmu);
+	if (rc) {
+		printk(KERN_ERR "Failed registering PMU sys driver\n");
+		return -ENODEV;
+	}
+	return 0;
+}
+
+subsys_initcall(init_pmu_sysfs);
+
 EXPORT_SYMBOL(pmu_request);
 EXPORT_SYMBOL(pmu_poll);
 EXPORT_SYMBOL(pmu_poll_adb);
diff -ubw -Naur linux-2.6.8.orig/drivers/macintosh/via-pmu.c.orig linux-2.6.8/drivers/macintosh/via-pmu.c.orig
--- linux-2.6.8.orig/drivers/macintosh/via-pmu.c.orig	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.8/drivers/macintosh/via-pmu.c.orig	2004-08-14 07:37:41.000000000 +0200
@@ -0,0 +1,3068 @@
+/*
+ * Device driver for the via-pmu on Apple Powermacs.
+ *
+ * The VIA (versatile interface adapter) interfaces to the PMU,
+ * a 6805 microprocessor core whose primary function is to control
+ * battery charging and system power on the PowerBook 3400 and 2400.
+ * The PMU also controls the ADB (Apple Desktop Bus) which connects
+ * to the keyboard and mouse, as well as the non-volatile RAM
+ * and the RTC (real time clock) chip.
+ *
+ * Copyright (C) 1998 Paul Mackerras and Fabio Riccardi.
+ * Copyright (C) 2001-2002 Benjamin Herrenschmidt
+ *
+ * THIS DRIVER IS BECOMING A TOTAL MESS !
+ *  - Cleanup atomically disabling reply to PMU events after
+ *    a sleep or a freq. switch
+ *  - Move sleep code out of here to pmac_pm, merge into new
+ *    common PM infrastructure
+ *  - Move backlight code out as well
+ *  - Save/Restore PCI space properly
+ *
+ */
+#include <stdarg.h>
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/miscdevice.h>
+#include <linux/blkdev.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/adb.h>
+#include <linux/pmu.h>
+#include <linux/cuda.h>
+#include <linux/smp_lock.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/pm.h>
+#include <linux/proc_fs.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/suspend.h>
+#include <linux/syscalls.h>
+#include <asm/prom.h>
+#include <asm/machdep.h>
+#include <asm/io.h>
+#include <asm/pgtable.h>
+#include <asm/system.h>
+#include <asm/sections.h>
+#include <asm/irq.h>
+#include <asm/hardirq.h>
+#include <asm/pmac_feature.h>
+#include <asm/uaccess.h>
+#include <asm/mmu_context.h>
+#include <asm/cputable.h>
+#include <asm/time.h>
+#ifdef CONFIG_PMAC_BACKLIGHT
+#include <asm/backlight.h>
+#endif
+
+/* 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
+
+/* How many iterations between battery polls */
+#define BATTERY_POLLING_COUNT	2
+
+static volatile unsigned char *via;
+
+/* VIA registers - spaced 0x200 bytes apart */
+#define RS		0x200		/* skip between registers */
+#define B		0		/* B-side data */
+#define A		RS		/* A-side data */
+#define DIRB		(2*RS)		/* B-side direction (1=output) */
+#define DIRA		(3*RS)		/* A-side direction (1=output) */
+#define T1CL		(4*RS)		/* Timer 1 ctr/latch (low 8 bits) */
+#define T1CH		(5*RS)		/* Timer 1 counter (high 8 bits) */
+#define T1LL		(6*RS)		/* Timer 1 latch (low 8 bits) */
+#define T1LH		(7*RS)		/* Timer 1 latch (high 8 bits) */
+#define T2CL		(8*RS)		/* Timer 2 ctr/latch (low 8 bits) */
+#define T2CH		(9*RS)		/* Timer 2 counter (high 8 bits) */
+#define SR		(10*RS)		/* Shift register */
+#define ACR		(11*RS)		/* Auxiliary control register */
+#define PCR		(12*RS)		/* Peripheral control register */
+#define IFR		(13*RS)		/* Interrupt flag register */
+#define IER		(14*RS)		/* Interrupt enable register */
+#define ANH		(15*RS)		/* A-side data, no handshake */
+
+/* Bits in B data register: both active low */
+#define TACK		0x08		/* Transfer acknowledge (input) */
+#define TREQ		0x10		/* Transfer request (output) */
+
+/* Bits in ACR */
+#define SR_CTRL		0x1c		/* Shift register control bits */
+#define SR_EXT		0x0c		/* Shift on external clock */
+#define SR_OUT		0x10		/* Shift out if 1 */
+
+/* Bits in IFR and IER */
+#define IER_SET		0x80		/* set bits in IER */
+#define IER_CLR		0		/* clear bits in IER */
+#define SR_INT		0x04		/* Shift register full/empty */
+#define CB2_INT		0x08
+#define CB1_INT		0x10		/* transition on CB1 input */
+
+static volatile enum pmu_state {
+	idle,
+	sending,
+	intack,
+	reading,
+	reading_intr,
+	locked,
+} pmu_state;
+
+static volatile enum int_data_state {
+	int_data_empty,
+	int_data_fill,
+	int_data_ready,
+	int_data_flush
+} int_data_state[2] = { int_data_empty, int_data_empty };
+
+static struct adb_request *current_req;
+static struct adb_request *last_req;
+static struct adb_request *req_awaiting_reply;
+static unsigned char interrupt_data[2][32];
+static int interrupt_data_len[2];
+static int int_data_last;
+static unsigned char *reply_ptr;
+static int data_index;
+static int data_len;
+static volatile int adb_int_pending;
+static volatile int disable_poll;
+static struct adb_request bright_req_1, bright_req_2;
+static unsigned long async_req_locks;
+static struct device_node *vias;
+static int pmu_kind = PMU_UNKNOWN;
+static int pmu_fully_inited = 0;
+static int pmu_has_adb;
+static unsigned char *gpio_reg = NULL;
+static int gpio_irq = -1;
+static int gpio_irq_enabled = -1;
+static volatile int pmu_suspended = 0;
+static spinlock_t pmu_lock;
+static u8 pmu_intr_mask;
+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 unsigned int pmu_irq_stats[11];
+
+static struct proc_dir_entry *proc_pmu_root;
+static struct proc_dir_entry *proc_pmu_info;
+static struct proc_dir_entry *proc_pmu_irqstats;
+static struct proc_dir_entry *proc_pmu_options;
+static int option_server_mode;
+
+#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 */
+
+#if defined(CONFIG_INPUT_ADBHID) && defined(CONFIG_PMAC_BACKLIGHT)
+extern int disable_kernel_backlight;
+#endif /* defined(CONFIG_INPUT_ADBHID) && defined(CONFIG_PMAC_BACKLIGHT) */
+
+int __fake_sleep;
+int asleep;
+struct notifier_block *sleep_notifier_list;
+
+#ifdef CONFIG_ADB
+static int adb_dev_map = 0;
+static int pmu_adb_flags;
+
+static int pmu_probe(void);
+static int pmu_init(void);
+static int pmu_send_request(struct adb_request *req, int sync);
+static int pmu_adb_autopoll(int devs);
+static int pmu_adb_reset_bus(void);
+#endif /* CONFIG_ADB */
+
+static int init_pmu(void);
+static int pmu_queue_request(struct adb_request *req);
+static void pmu_start(void);
+static irqreturn_t via_pmu_interrupt(int irq, void *arg, struct pt_regs *regs);
+static irqreturn_t 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);
+static int proc_get_irqstats(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);
+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 __user *buffer,
+			unsigned long count, void *data);
+
+#ifdef CONFIG_ADB
+struct adb_driver via_pmu_driver = {
+	"PMU",
+	pmu_probe,
+	pmu_init,
+	pmu_send_request,
+	pmu_adb_autopoll,
+	pmu_poll_adb,
+	pmu_adb_reset_bus
+};
+#endif /* CONFIG_ADB */
+
+extern void low_sleep_handler(void);
+extern void enable_kernel_altivec(void);
+extern void enable_kernel_fp(void);
+
+#ifdef DEBUG_SLEEP
+int pmu_polled_request(struct adb_request *req);
+int pmu_wink(struct adb_request *req);
+#endif
+
+/*
+ * This table indicates for each PMU opcode:
+ * - the number of data bytes to be sent with the command, or -1
+ *   if a length byte should be sent,
+ * - the number of response bytes which the PMU will return, or
+ *   -1 if it will send a length byte.
+ */
+static const s8 pmu_data_len[256][2] __openfirmwaredata = {
+/*	   0	   1	   2	   3	   4	   5	   6	   7  */
+/*00*/	{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},
+/*08*/	{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},
+/*10*/	{ 1, 0},{ 1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},
+/*18*/	{ 0, 1},{ 0, 1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{ 0, 0},
+/*20*/	{-1, 0},{ 0, 0},{ 2, 0},{ 1, 0},{ 1, 0},{-1, 0},{-1, 0},{-1, 0},
+/*28*/	{ 0,-1},{ 0,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{ 0,-1},
+/*30*/	{ 4, 0},{20, 0},{-1, 0},{ 3, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},
+/*38*/	{ 0, 4},{ 0,20},{ 2,-1},{ 2, 1},{ 3,-1},{-1,-1},{-1,-1},{ 4, 0},
+/*40*/	{ 1, 0},{ 1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},
+/*48*/	{ 0, 1},{ 0, 1},{-1,-1},{ 1, 0},{ 1, 0},{-1,-1},{-1,-1},{-1,-1},
+/*50*/	{ 1, 0},{ 0, 0},{ 2, 0},{ 2, 0},{-1, 0},{ 1, 0},{ 3, 0},{ 1, 0},
+/*58*/	{ 0, 1},{ 1, 0},{ 0, 2},{ 0, 2},{ 0,-1},{-1,-1},{-1,-1},{-1,-1},
+/*60*/	{ 2, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},
+/*68*/	{ 0, 3},{ 0, 3},{ 0, 2},{ 0, 8},{ 0,-1},{ 0,-1},{-1,-1},{-1,-1},
+/*70*/	{ 1, 0},{ 1, 0},{ 1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},
+/*78*/	{ 0,-1},{ 0,-1},{-1,-1},{-1,-1},{-1,-1},{ 5, 1},{ 4, 1},{ 4, 1},
+/*80*/	{ 4, 0},{-1, 0},{ 0, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},
+/*88*/	{ 0, 5},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},
+/*90*/	{ 1, 0},{ 2, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},
+/*98*/	{ 0, 1},{ 0, 1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},
+/*a0*/	{ 2, 0},{ 2, 0},{ 2, 0},{ 4, 0},{-1, 0},{ 0, 0},{-1, 0},{-1, 0},
+/*a8*/	{ 1, 1},{ 1, 0},{ 3, 0},{ 2, 0},{-1,-1},{-1,-1},{-1,-1},{-1,-1},
+/*b0*/	{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},
+/*b8*/	{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},
+/*c0*/	{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},
+/*c8*/	{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},
+/*d0*/	{ 0, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},
+/*d8*/	{ 1, 1},{ 1, 1},{-1,-1},{-1,-1},{ 0, 1},{ 0,-1},{-1,-1},{-1,-1},
+/*e0*/	{-1, 0},{ 4, 0},{ 0, 1},{-1, 0},{-1, 0},{ 4, 0},{-1, 0},{-1, 0},
+/*e8*/	{ 3,-1},{-1,-1},{ 0, 1},{-1,-1},{ 0,-1},{-1,-1},{-1,-1},{ 0, 0},
+/*f0*/	{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},
+/*f8*/	{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},
+};
+
+static char *pbook_type[] = {
+	"Unknown PowerBook",
+	"PowerBook 2400/3400/3500(G3)",
+	"PowerBook G3 Series",
+	"1999 PowerBook G3",
+	"Core99"
+};
+
+#ifdef CONFIG_PMAC_BACKLIGHT
+static struct backlight_controller pmu_backlight_controller = {
+	pmu_set_backlight_enable,
+	pmu_set_backlight_level
+};
+#endif /* CONFIG_PMAC_BACKLIGHT */
+
+int __openfirmware
+find_via_pmu(void)
+{
+	if (via != 0)
+		return 1;
+	vias = find_devices("via-pmu");
+	if (vias == 0)
+		return 0;
+	if (vias->next != 0)
+		printk(KERN_WARNING "Warning: only using 1st via-pmu\n");
+
+	if (vias->n_addrs < 1 || vias->n_intrs < 1) {
+		printk(KERN_ERR "via-pmu: %d addresses, %d interrupts!\n",
+		       vias->n_addrs, vias->n_intrs);
+		if (vias->n_addrs < 1 || vias->n_intrs < 1)
+			return 0;
+	}
+
+	spin_lock_init(&pmu_lock);
+
+	pmu_has_adb = 1;
+
+	pmu_intr_mask =	PMU_INT_PCEJECT |
+			PMU_INT_SNDBRT |
+			PMU_INT_ADB |
+			PMU_INT_TICK;
+	
+	if (vias->parent->name && ((strcmp(vias->parent->name, "ohare") == 0)
+	    || device_is_compatible(vias->parent, "ohare")))
+		pmu_kind = PMU_OHARE_BASED;
+	else if (device_is_compatible(vias->parent, "paddington"))
+		pmu_kind = PMU_PADDINGTON_BASED;
+	else if (device_is_compatible(vias->parent, "heathrow"))
+		pmu_kind = PMU_HEATHROW_BASED;
+	else if (device_is_compatible(vias->parent, "Keylargo")
+		 || device_is_compatible(vias->parent, "K2-Keylargo")) {
+		struct device_node *gpio, *gpiop;
+
+		pmu_kind = PMU_KEYLARGO_BASED;
+		pmu_has_adb = (find_type_devices("adb") != NULL);
+		pmu_intr_mask =	PMU_INT_PCEJECT |
+				PMU_INT_SNDBRT |
+				PMU_INT_ADB |
+				PMU_INT_TICK |
+				PMU_INT_ENVIRONMENT;
+		
+		gpiop = find_devices("gpio");
+		if (gpiop && gpiop->n_addrs) {
+			gpio_reg = ioremap(gpiop->addrs->address, 0x10);
+			gpio = find_devices("extint-gpio1");
+			if (gpio == NULL)
+				gpio = find_devices("pmu-interrupt");
+			if (gpio && gpio->parent == gpiop && gpio->n_intrs)
+				gpio_irq = gpio->intrs[0].line;
+		}
+	} else
+		pmu_kind = PMU_UNKNOWN;
+
+	via = (volatile unsigned char *) ioremap(vias->addrs->address, 0x2000);
+	
+	out_8(&via[IER], IER_CLR | 0x7f);	/* disable all intrs */
+	out_8(&via[IFR], 0x7f);			/* clear IFR */
+
+	pmu_state = idle;
+
+	if (!init_pmu()) {
+		via = NULL;
+		return 0;
+	}
+
+	printk(KERN_INFO "PMU driver %d initialized for %s, firmware: %02x\n",
+	       PMU_DRIVER_VERSION, pbook_type[pmu_kind], pmu_version);
+	       
+#ifndef CONFIG_PPC64
+	sys_ctrler = SYS_CTRLER_PMU;
+#endif
+	
+	return 1;
+}
+
+#ifdef CONFIG_ADB
+static int __openfirmware
+pmu_probe(void)
+{
+	return vias == NULL? -ENODEV: 0;
+}
+
+static int __init
+pmu_init(void)
+{
+	if (vias == NULL)
+		return -ENODEV;
+	return 0;
+}
+#endif /* CONFIG_ADB */
+
+/*
+ * We can't wait until pmu_init gets called, that happens too late.
+ * It happens after IDE and SCSI initialization, which can take a few
+ * seconds, and by that time the PMU could have given up on us and
+ * turned us off.
+ * Thus this is called with arch_initcall rather than device_initcall.
+ */
+static int __init via_pmu_start(void)
+{
+	if (vias == NULL)
+		return -ENODEV;
+
+	bright_req_1.complete = 1;
+	bright_req_2.complete = 1;
+#ifdef CONFIG_PMAC_PBOOK
+	batt_req.complete = 1;
+	if (pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,-1) >= 0)
+		can_sleep = 1;
+#endif
+
+	if (request_irq(vias->intrs[0].line, via_pmu_interrupt, 0, "VIA-PMU",
+			(void *)0)) {
+		printk(KERN_ERR "VIA-PMU: can't get irq %d\n",
+		       vias->intrs[0].line);
+		return -EAGAIN;
+	}
+
+	if (pmu_kind == PMU_KEYLARGO_BASED && gpio_irq != -1) {
+		if (request_irq(gpio_irq, gpio1_interrupt, 0, "GPIO1/ADB", (void *)0))
+			printk(KERN_ERR "pmu: can't get irq %d (GPIO1)\n", gpio_irq);
+		gpio_irq_enabled = 1;
+	}
+
+	/* Enable interrupts */
+	out_8(&via[IER], IER_SET | SR_INT | CB1_INT);
+
+	pmu_fully_inited = 1;
+
+	/* 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
+	 * Not that on Core99 machines, the PMU keeps sending us environement
+	 * messages, we should find a way to either fix IDE or make it call
+	 * pmu_suspend() before masking interrupts. This can also happens while
+	 * scolling with some fbdevs.
+	 */
+	do {
+		pmu_poll();
+	} while (pmu_state != idle);
+
+	return 0;
+}
+
+arch_initcall(via_pmu_start);
+
+/*
+ * This has to be done after pci_init, which is a subsys_initcall.
+ */
+static int __init via_pmu_dev_init(void)
+{
+	if (vias == NULL)
+		return -ENODEV;
+
+#ifndef CONFIG_PPC64
+	request_OF_resource(vias, 0, NULL);
+#endif
+#ifdef CONFIG_PMAC_BACKLIGHT
+	/* Enable backlight */
+	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")) {
+		int mb = pmac_call_feature(PMAC_FTR_GET_MB_INFO,
+			NULL, PMAC_MB_INFO_MODEL, 0);
+		pmu_battery_count = 1;
+		if (mb == PMAC_TYPE_COMET)
+			pmu_batteries[0].flags |= PMU_BATT_TYPE_COMET;
+		else
+			pmu_batteries[0].flags |= PMU_BATT_TYPE_HOOPER;
+	} else if (machine_is_compatible("AAPL,PowerBook1998") ||
+		machine_is_compatible("PowerBook1,1")) {
+		pmu_battery_count = 2;
+		pmu_batteries[0].flags |= PMU_BATT_TYPE_SMART;
+		pmu_batteries[1].flags |= PMU_BATT_TYPE_SMART;
+	} 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;
+			pmu_batteries[0].flags |= PMU_BATT_TYPE_SMART;
+			if (pmu_battery_count > 1)
+				pmu_batteries[1].flags |= PMU_BATT_TYPE_SMART;
+		}
+	}
+#endif /* CONFIG_PMAC_PBOOK */
+	/* Create /proc/pmu */
+	proc_pmu_root = proc_mkdir("pmu", NULL);
+	if (proc_pmu_root) {
+		int i;
+		proc_pmu_info = create_proc_read_entry("info", 0, proc_pmu_root,
+					proc_get_info, NULL);
+		proc_pmu_irqstats = create_proc_read_entry("interrupts", 0, proc_pmu_root,
+					proc_get_irqstats, 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;
+		}
+	}
+	return 0;
+}
+
+device_initcall(via_pmu_dev_init);
+
+static int __openfirmware
+init_pmu(void)
+{
+	int timeout;
+	struct adb_request req;
+
+	out_8(&via[B], via[B] | TREQ);			/* negate TREQ */
+	out_8(&via[DIRB], (via[DIRB] | TREQ) & ~TACK);	/* TACK in, TREQ out */
+
+	pmu_request(&req, NULL, 2, PMU_SET_INTR_MASK, pmu_intr_mask);
+	timeout =  100000;
+	while (!req.complete) {
+		if (--timeout < 0) {
+			printk(KERN_ERR "init_pmu: no response from PMU\n");
+			return 0;
+		}
+		udelay(10);
+		pmu_poll();
+	}
+
+	/* ack all pending interrupts */
+	timeout = 100000;
+	interrupt_data[0][0] = 1;
+	while (interrupt_data[0][0] || pmu_state != idle) {
+		if (--timeout < 0) {
+			printk(KERN_ERR "init_pmu: timed out acking intrs\n");
+			return 0;
+		}
+		if (pmu_state == idle)
+			adb_int_pending = 1;
+		via_pmu_interrupt(0, NULL, NULL);
+		udelay(10);
+	}
+
+	/* Tell PMU we are ready.  */
+	if (pmu_kind == PMU_KEYLARGO_BASED) {
+		pmu_request(&req, NULL, 2, PMU_SYSTEM_READY, 2);
+		while (!req.complete)
+			pmu_poll();
+	}
+
+	/* Read PMU version */
+	pmu_request(&req, NULL, 1, PMU_GET_VERSION);
+	pmu_wait_complete(&req);
+	if (req.reply_len > 0)
+		pmu_version = req.reply[0];
+	
+	/* Read server mode setting */
+	if (pmu_kind == PMU_KEYLARGO_BASED) {
+		pmu_request(&req, NULL, 2, PMU_POWER_EVENTS,
+			    PMU_PWR_GET_POWERUP_EVENTS);
+		pmu_wait_complete(&req);
+		if (req.reply_len == 2) {
+			if (req.reply[1] & PMU_PWR_WAKEUP_AC_INSERT)
+				option_server_mode = 1;
+			printk(KERN_INFO "via-pmu: Server Mode is %s\n",
+			       option_server_mode ? "enabled" : "disabled");
+		}
+	}
+	return 1;
+}
+
+int
+pmu_get_model(void)
+{
+	return pmu_kind;
+}
+
+#ifndef CONFIG_PPC64
+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();
+}
+#endif
+
+static void pmu_set_server_mode(int server_mode)
+{
+	struct adb_request req;
+
+	if (pmu_kind != PMU_KEYLARGO_BASED)
+		return;
+
+	option_server_mode = server_mode;
+	pmu_request(&req, NULL, 2, PMU_POWER_EVENTS, PMU_PWR_GET_POWERUP_EVENTS);
+	pmu_wait_complete(&req);
+	if (req.reply_len < 2)
+		return;
+	if (server_mode)
+		pmu_request(&req, NULL, 4, PMU_POWER_EVENTS,
+			    PMU_PWR_SET_POWERUP_EVENTS,
+			    req.reply[0], PMU_PWR_WAKEUP_AC_INSERT); 
+	else
+		pmu_request(&req, NULL, 4, PMU_POWER_EVENTS,
+			    PMU_PWR_CLR_POWERUP_EVENTS,
+			    req.reply[0], PMU_PWR_WAKEUP_AC_INSERT); 
+	pmu_wait_complete(&req);
+}
+
+#ifdef CONFIG_PMAC_PBOOK
+
+/* This new version of the code for 2400/3400/3500 powerbooks
+ * is inspired from the implementation in gkrellm-pmu
+ */
+static void __pmac
+done_battery_state_ohare(struct adb_request* req)
+{
+	/* format:
+	 *  [0]    :  flags
+	 *    0x01 :  AC indicator
+	 *    0x02 :  charging
+	 *    0x04 :  battery exist
+	 *    0x08 :  
+	 *    0x10 :  
+	 *    0x20 :  full charged
+	 *    0x40 :  pcharge reset
+	 *    0x80 :  battery exist
+	 *
+	 *  [1][2] :  battery voltage
+	 *  [3]    :  CPU temperature
+	 *  [4]    :  battery temperature
+	 *  [5]    :  current
+	 *  [6][7] :  pcharge
+	 *              --tkoba
+	 */
+	unsigned int bat_flags = PMU_BATT_TYPE_HOOPER;
+	long pcharge, charge, vb, vmax, lmax;
+	long vmax_charging, vmax_charged;
+	long amperage, voltage, time, max;
+	int mb = pmac_call_feature(PMAC_FTR_GET_MB_INFO,
+			NULL, PMAC_MB_INFO_MODEL, 0);
+
+	if (req->reply[0] & 0x01)
+		pmu_power_flags |= PMU_PWR_AC_PRESENT;
+	else
+		pmu_power_flags &= ~PMU_PWR_AC_PRESENT;
+	
+	if (mb == PMAC_TYPE_COMET) {
+		vmax_charged = 189;
+		vmax_charging = 213;
+		lmax = 6500;
+	} else {
+		vmax_charged = 330;
+		vmax_charging = 330;
+		lmax = 6500;
+	}
+	vmax = vmax_charged;
+
+	/* If battery installed */
+	if (req->reply[0] & 0x04) {
+		bat_flags |= PMU_BATT_PRESENT;
+		if (req->reply[0] & 0x02)
+			bat_flags |= PMU_BATT_CHARGING;
+		vb = (req->reply[1] << 8) | req->reply[2];
+		voltage = (vb * 265 + 72665) / 10;
+		amperage = req->reply[5];
+		if ((req->reply[0] & 0x01) == 0) {
+			if (amperage > 200)
+				vb += ((amperage - 200) * 15)/100;
+		} else if (req->reply[0] & 0x02) {
+			vb = (vb * 97) / 100;
+			vmax = vmax_charging;
+		}
+		charge = (100 * vb) / vmax;
+		if (req->reply[0] & 0x40) {
+			pcharge = (req->reply[6] << 8) + req->reply[7];
+			if (pcharge > lmax)
+				pcharge = lmax;
+			pcharge *= 100;
+			pcharge = 100 - pcharge / lmax;
+			if (pcharge < charge)
+				charge = pcharge;
+		}
+		if (amperage > 0)
+			time = (charge * 16440) / amperage;
+		else
+			time = 0;
+		max = 100;
+		amperage = -amperage;
+	} else
+		charge = max = amperage = voltage = time = 0;
+
+	pmu_batteries[pmu_cur_battery].flags = bat_flags;
+	pmu_batteries[pmu_cur_battery].charge = charge;
+	pmu_batteries[pmu_cur_battery].max_charge = max;
+	pmu_batteries[pmu_cur_battery].amperage = amperage;
+	pmu_batteries[pmu_cur_battery].voltage = voltage;
+	pmu_batteries[pmu_cur_battery].time_remaining = time;
+
+	clear_bit(0, &async_req_locks);
+}
+
+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 = PMU_BATT_TYPE_SMART;
+	int amperage;
+	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];
+				amperage = *((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];
+				amperage = *((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 = amperage = voltage = 0;
+
+	if ((req->reply[1] & 0x01) && (amperage > 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].amperage = amperage;
+	pmu_batteries[pmu_cur_battery].voltage = voltage;
+	if (amperage) {
+		if ((req->reply[1] & 0x01) && (amperage > 0))
+			pmu_batteries[pmu_cur_battery].time_remaining
+				= ((max-capa) * 3600) / amperage;
+		else
+			pmu_batteries[pmu_cur_battery].time_remaining
+				= (capa * 3600) / (-amperage);
+	} else
+		pmu_batteries[pmu_cur_battery].time_remaining = 0;
+
+	pmu_cur_battery = (pmu_cur_battery + 1) % pmu_battery_count;
+
+	clear_bit(0, &async_req_locks);
+}
+
+static void __pmac
+query_battery_state(void)
+{
+	if (test_and_set_bit(0, &async_req_locks))
+		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 __pmac
+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;
+}
+
+static int __pmac
+proc_get_irqstats(char *page, char **start, off_t off,
+		  int count, int *eof, void *data)
+{
+	int i;
+	char* p = page;
+	static const char *irq_names[] = {
+		"Total CB1 triggered events",
+		"Total GPIO1 triggered events",
+		"PC-Card eject button",
+		"Sound/Brightness button",
+		"ADB message",
+		"Battery state change",
+		"Environment interrupt",
+		"Tick timer",
+		"Ghost interrupt (zero len)",
+		"Empty interrupt (empty mask)",
+		"Max irqs in a row"
+        };
+
+	for (i=0; i<11; i++) {
+		p += sprintf(p, " %2u: %10u (%s)\n",
+			     i, pmu_irq_stats[i], irq_names[i]);
+	}
+	return p - page;
+}
+
+#ifdef CONFIG_PMAC_PBOOK
+static int __pmac
+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].amperage);
+	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 __pmac
+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 */
+	if (pmu_kind == PMU_KEYLARGO_BASED)
+		p += sprintf(p, "server_mode=%d\n", option_server_mode);
+
+	return p - page;
+}
+			
+static int __pmac
+proc_write_options(struct file *file, const char __user *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 */
+	if (pmu_kind == PMU_KEYLARGO_BASED && !strcmp(label, "server_mode")) {
+		int new_value;
+		new_value = ((*val) == '1');
+		if (new_value != option_server_mode)
+			pmu_set_server_mode(new_value);
+	}
+	return fcount;
+}
+
+#ifdef CONFIG_ADB
+/* Send an ADB command */
+static int __pmac
+pmu_send_request(struct adb_request *req, int sync)
+{
+	int i, ret;
+
+	if ((vias == NULL) || (!pmu_fully_inited)) {
+		req->complete = 1;
+		return -ENXIO;
+	}
+
+	ret = -EINVAL;
+
+	switch (req->data[0]) {
+	case PMU_PACKET:
+		for (i = 0; i < req->nbytes - 1; ++i)
+			req->data[i] = req->data[i+1];
+		--req->nbytes;
+		if (pmu_data_len[req->data[0]][1] != 0) {
+			req->reply[0] = ADB_RET_OK;
+			req->reply_len = 1;
+		} else
+			req->reply_len = 0;
+		ret = pmu_queue_request(req);
+		break;
+	case CUDA_PACKET:
+		switch (req->data[1]) {
+		case CUDA_GET_TIME:
+			if (req->nbytes != 2)
+				break;
+			req->data[0] = PMU_READ_RTC;
+			req->nbytes = 1;
+			req->reply_len = 3;
+			req->reply[0] = CUDA_PACKET;
+			req->reply[1] = 0;
+			req->reply[2] = CUDA_GET_TIME;
+			ret = pmu_queue_request(req);
+			break;
+		case CUDA_SET_TIME:
+			if (req->nbytes != 6)
+				break;
+			req->data[0] = PMU_SET_RTC;
+			req->nbytes = 5;
+			for (i = 1; i <= 4; ++i)
+				req->data[i] = req->data[i+1];
+			req->reply_len = 3;
+			req->reply[0] = CUDA_PACKET;
+			req->reply[1] = 0;
+			req->reply[2] = CUDA_SET_TIME;
+			ret = pmu_queue_request(req);
+			break;
+		}
+		break;
+	case ADB_PACKET:
+	    	if (!pmu_has_adb)
+    			return -ENXIO;
+		for (i = req->nbytes - 1; i > 1; --i)
+			req->data[i+2] = req->data[i];
+		req->data[3] = req->nbytes - 2;
+		req->data[2] = pmu_adb_flags;
+		/*req->data[1] = req->data[1];*/
+		req->data[0] = PMU_ADB_CMD;
+		req->nbytes += 2;
+		req->reply_expected = 1;
+		req->reply_len = 0;
+		ret = pmu_queue_request(req);
+		break;
+	}
+	if (ret) {
+		req->complete = 1;
+		return ret;
+	}
+
+	if (sync)
+		while (!req->complete)
+			pmu_poll();
+
+	return 0;
+}
+
+/* Enable/disable autopolling */
+static int __pmac
+pmu_adb_autopoll(int devs)
+{
+	struct adb_request req;
+
+	if ((vias == NULL) || (!pmu_fully_inited) || !pmu_has_adb)
+		return -ENXIO;
+
+	if (devs) {
+		adb_dev_map = devs;
+		pmu_request(&req, NULL, 5, PMU_ADB_CMD, 0, 0x86,
+			    adb_dev_map >> 8, adb_dev_map);
+		pmu_adb_flags = 2;
+	} else {
+		pmu_request(&req, NULL, 1, PMU_ADB_POLL_OFF);
+		pmu_adb_flags = 0;
+	}
+	while (!req.complete)
+		pmu_poll();
+	return 0;
+}
+
+/* Reset the ADB bus */
+static int __pmac
+pmu_adb_reset_bus(void)
+{
+	struct adb_request req;
+	int save_autopoll = adb_dev_map;
+
+	if ((vias == NULL) || (!pmu_fully_inited) || !pmu_has_adb)
+		return -ENXIO;
+
+	/* anyone got a better idea?? */
+	pmu_adb_autopoll(0);
+
+	req.nbytes = 5;
+	req.done = NULL;
+	req.data[0] = PMU_ADB_CMD;
+	req.data[1] = 0;
+	req.data[2] = ADB_BUSRESET;
+	req.data[3] = 0;
+	req.data[4] = 0;
+	req.reply_len = 0;
+	req.reply_expected = 1;
+	if (pmu_queue_request(&req) != 0) {
+		printk(KERN_ERR "pmu_adb_reset_bus: pmu_queue_request failed\n");
+		return -EIO;
+	}
+	pmu_wait_complete(&req);
+
+	if (save_autopoll != 0)
+		pmu_adb_autopoll(save_autopoll);
+
+	return 0;
+}
+#endif /* CONFIG_ADB */
+
+/* Construct and send a pmu request */
+int __openfirmware
+pmu_request(struct adb_request *req, void (*done)(struct adb_request *),
+	    int nbytes, ...)
+{
+	va_list list;
+	int i;
+
+	if (vias == NULL)
+		return -ENXIO;
+
+	if (nbytes < 0 || nbytes > 32) {
+		printk(KERN_ERR "pmu_request: bad nbytes (%d)\n", nbytes);
+		req->complete = 1;
+		return -EINVAL;
+	}
+	req->nbytes = nbytes;
+	req->done = done;
+	va_start(list, nbytes);
+	for (i = 0; i < nbytes; ++i)
+		req->data[i] = va_arg(list, int);
+	va_end(list);
+	req->reply_len = 0;
+	req->reply_expected = 0;
+	return pmu_queue_request(req);
+}
+
+int __pmac
+pmu_queue_request(struct adb_request *req)
+{
+	unsigned long flags;
+	int nsend;
+
+	if (via == NULL) {
+		req->complete = 1;
+		return -ENXIO;
+	}
+	if (req->nbytes <= 0) {
+		req->complete = 1;
+		return 0;
+	}
+	nsend = pmu_data_len[req->data[0]][0];
+	if (nsend >= 0 && req->nbytes != nsend + 1) {
+		req->complete = 1;
+		return -EINVAL;
+	}
+
+	req->next = NULL;
+	req->sent = 0;
+	req->complete = 0;
+
+	spin_lock_irqsave(&pmu_lock, flags);
+	if (current_req != 0) {
+		last_req->next = req;
+		last_req = req;
+	} else {
+		current_req = req;
+		last_req = req;
+		if (pmu_state == idle)
+			pmu_start();
+	}
+	spin_unlock_irqrestore(&pmu_lock, flags);
+
+	return 0;
+}
+
+static inline void
+wait_for_ack(void)
+{
+	/* Sightly increased the delay, I had one occurrence of the message
+	 * reported
+	 */
+	int timeout = 4000;
+	while ((in_8(&via[B]) & TACK) == 0) {
+		if (--timeout < 0) {
+			printk(KERN_ERR "PMU not responding (!ack)\n");
+			return;
+		}
+		udelay(10);
+	}
+}
+
+/* New PMU seems to be very sensitive to those timings, so we make sure
+ * PCI is flushed immediately */
+static inline void
+send_byte(int x)
+{
+	volatile unsigned char *v = via;
+
+	out_8(&v[ACR], in_8(&v[ACR]) | SR_OUT | SR_EXT);
+	out_8(&v[SR], x);
+	out_8(&v[B], in_8(&v[B]) & ~TREQ);		/* assert TREQ */
+	(void)in_8(&v[B]);
+}
+
+static inline void
+recv_byte(void)
+{
+	volatile unsigned char *v = via;
+
+	out_8(&v[ACR], (in_8(&v[ACR]) & ~SR_OUT) | SR_EXT);
+	in_8(&v[SR]);		/* resets SR */
+	out_8(&v[B], in_8(&v[B]) & ~TREQ);
+	(void)in_8(&v[B]);
+}
+
+static inline void
+pmu_done(struct adb_request *req)
+{
+	void (*done)(struct adb_request *) = req->done;
+	mb();
+	req->complete = 1;
+    	/* Here, we assume that if the request has a done member, the
+    	 * struct request will survive to setting req->complete to 1
+    	 */
+	if (done)
+		(*done)(req);
+}
+
+static void __pmac
+pmu_start(void)
+{
+	struct adb_request *req;
+
+	/* assert pmu_state == idle */
+	/* get the packet to send */
+	req = current_req;
+	if (req == 0 || pmu_state != idle
+	    || (/*req->reply_expected && */req_awaiting_reply))
+		return;
+
+	pmu_state = sending;
+	data_index = 1;
+	data_len = pmu_data_len[req->data[0]][0];
+
+	/* Sounds safer to make sure ACK is high before writing. This helped
+	 * kill a problem with ADB and some iBooks
+	 */
+	wait_for_ack();
+	/* set the shift register to shift out and send a byte */
+	send_byte(req->data[0]);
+}
+
+void __openfirmware
+pmu_poll(void)
+{
+	if (!via)
+		return;
+	if (disable_poll)
+		return;
+	via_pmu_interrupt(0, NULL, NULL);
+}
+
+void __openfirmware
+pmu_poll_adb(void)
+{
+	if (!via)
+		return;
+	if (disable_poll)
+		return;
+	/* Kicks ADB read when PMU is suspended */
+	adb_int_pending = 1;
+	do {
+		via_pmu_interrupt(0, NULL, NULL);
+	} while (pmu_suspended && (adb_int_pending || pmu_state != idle
+		|| req_awaiting_reply));
+}
+
+void __openfirmware
+pmu_wait_complete(struct adb_request *req)
+{
+	if (!via)
+		return;
+	while((pmu_state != idle && pmu_state != locked) || !req->complete)
+		via_pmu_interrupt(0, NULL, NULL);
+}
+
+/* This function loops until the PMU is idle and prevents it from
+ * anwsering to ADB interrupts. pmu_request can still be called.
+ * This is done to avoid spurrious shutdowns when we know we'll have
+ * interrupts switched off for a long time
+ */
+void __openfirmware
+pmu_suspend(void)
+{
+	unsigned long flags;
+#ifdef SUSPEND_USES_PMU
+	struct adb_request *req;
+#endif
+	if (!via)
+		return;
+	
+	spin_lock_irqsave(&pmu_lock, flags);
+	pmu_suspended++;
+	if (pmu_suspended > 1) {
+		spin_unlock_irqrestore(&pmu_lock, flags);
+		return;
+	}
+
+	do {
+		spin_unlock_irqrestore(&pmu_lock, flags);
+		if (req_awaiting_reply)
+			adb_int_pending = 1;
+		via_pmu_interrupt(0, NULL, NULL);
+		spin_lock_irqsave(&pmu_lock, flags);
+		if (!adb_int_pending && pmu_state == idle && !req_awaiting_reply) {
+#ifdef SUSPEND_USES_PMU
+			pmu_request(&req, NULL, 2, PMU_SET_INTR_MASK, 0);
+			spin_unlock_irqrestore(&pmu_lock, flags);
+			while(!req.complete)
+				pmu_poll();
+#else /* SUSPEND_USES_PMU */
+			if (gpio_irq >= 0)
+				disable_irq_nosync(gpio_irq);
+			out_8(&via[IER], CB1_INT | IER_CLR);
+			spin_unlock_irqrestore(&pmu_lock, flags);
+#endif /* SUSPEND_USES_PMU */
+			break;
+		}
+	} while (1);
+}
+
+void __openfirmware
+pmu_resume(void)
+{
+	unsigned long flags;
+
+	if (!via || (pmu_suspended < 1))
+		return;
+
+	spin_lock_irqsave(&pmu_lock, flags);
+	pmu_suspended--;
+	if (pmu_suspended > 0) {
+		spin_unlock_irqrestore(&pmu_lock, flags);
+		return;
+	}
+	adb_int_pending = 1;
+#ifdef SUSPEND_USES_PMU
+	pmu_request(&req, NULL, 2, PMU_SET_INTR_MASK, pmu_intr_mask);
+	spin_unlock_irqrestore(&pmu_lock, flags);
+	while(!req.complete)
+		pmu_poll();
+#else /* SUSPEND_USES_PMU */
+	if (gpio_irq >= 0)
+		enable_irq(gpio_irq);
+	out_8(&via[IER], CB1_INT | IER_SET);
+	spin_unlock_irqrestore(&pmu_lock, flags);
+	pmu_poll();
+#endif /* SUSPEND_USES_PMU */
+}
+
+/* Interrupt data could be the result data from an ADB cmd */
+static void __pmac
+pmu_handle_data(unsigned char *data, int len, struct pt_regs *regs)
+{
+	unsigned char ints, pirq;
+	int i = 0;
+
+	asleep = 0;
+	if (drop_interrupts || len < 1) {
+		adb_int_pending = 0;
+		pmu_irq_stats[8]++;
+		return;
+	}
+
+	/* Get PMU interrupt mask */
+	ints = data[0];
+
+	/* Record zero interrupts for stats */
+	if (ints == 0)
+		pmu_irq_stats[9]++;
+
+	/* Hack to deal with ADB autopoll flag */
+	if (ints & PMU_INT_ADB)
+		ints &= ~(PMU_INT_ADB_AUTO | PMU_INT_AUTO_SRQ_POLL);
+
+next:
+
+	if (ints == 0) {
+		if (i > pmu_irq_stats[10])
+			pmu_irq_stats[10] = i;
+		return;
+	}
+
+	for (pirq = 0; pirq < 8; pirq++)
+		if (ints & (1 << pirq))
+			break;
+	pmu_irq_stats[pirq]++;
+	i++;
+	ints &= ~(1 << pirq);
+
+	/* Note: for some reason, we get an interrupt with len=1,
+	 * data[0]==0 after each normal ADB interrupt, at least
+	 * on the Pismo. Still investigating...  --BenH
+	 */
+	if ((1 << pirq) & PMU_INT_ADB) {
+		if ((data[0] & PMU_INT_ADB_AUTO) == 0) {
+			struct adb_request *req = req_awaiting_reply;
+			if (req == 0) {
+				printk(KERN_ERR "PMU: extra ADB reply\n");
+				return;
+			}
+			req_awaiting_reply = NULL;
+			if (len <= 2)
+				req->reply_len = 0;
+			else {
+				memcpy(req->reply, data + 1, len - 1);
+				req->reply_len = len - 1;
+			}
+			pmu_done(req);
+		} else {
+#if defined(CONFIG_XMON) && !defined(CONFIG_PPC64)
+			if (len == 4 && data[1] == 0x2c) {
+				extern int xmon_wants_key, xmon_adb_keycode;
+				if (xmon_wants_key) {
+					xmon_adb_keycode = data[2];
+					return;
+				}
+			}
+#endif /* defined(CONFIG_XMON) && !defined(CONFIG_PPC64) */
+#ifdef CONFIG_ADB
+			/*
+			 * XXX On the [23]400 the PMU gives us an up
+			 * event for keycodes 0x74 or 0x75 when the PC
+			 * card eject buttons are released, so we
+			 * ignore those events.
+			 */
+			if (!(pmu_kind == PMU_OHARE_BASED && len == 4
+			      && data[1] == 0x2c && data[3] == 0xff
+			      && (data[2] & ~1) == 0xf4))
+				adb_input(data+1, len-1, regs, 1);
+#endif /* CONFIG_ADB */		
+		}
+	}
+	/* Sound/brightness button pressed */
+	else if ((1 << pirq) & PMU_INT_SNDBRT) {
+#ifdef CONFIG_PMAC_BACKLIGHT
+		if (len == 3)
+#ifdef CONFIG_INPUT_ADBHID
+			if (!disable_kernel_backlight)
+#endif /* CONFIG_INPUT_ADBHID */
+				set_backlight_level(data[1] >> 4);
+#endif /* CONFIG_PMAC_BACKLIGHT */
+	}
+	/* Tick interrupt */
+	else if ((1 << pirq) & PMU_INT_TICK) {
+#ifdef CONFIG_PMAC_PBOOK
+		/* Environement or tick interrupt, query batteries */
+		if (pmu_battery_count) {
+			if ((--query_batt_timer) == 0) {
+				query_battery_state();
+				query_batt_timer = BATTERY_POLLING_COUNT;
+			}
+		}
+        }
+	else if ((1 << pirq) & PMU_INT_ENVIRONMENT) {
+		if (pmu_battery_count)
+			query_battery_state();
+		pmu_pass_intr(data, len);
+	} else {
+	       pmu_pass_intr(data, len);
+#endif /* CONFIG_PMAC_PBOOK */
+	}
+	goto next;
+}
+
+static struct adb_request* __pmac
+pmu_sr_intr(struct pt_regs *regs)
+{
+	struct adb_request *req;
+	int bite;
+
+	if (via[B] & TREQ) {
+		printk(KERN_ERR "PMU: spurious SR intr (%x)\n", via[B]);
+		out_8(&via[IFR], SR_INT);
+		return NULL;
+	}
+	/* The ack may not yet be low when we get the interrupt */
+	while ((in_8(&via[B]) & TACK) != 0)
+			;
+
+	/* if reading grab the byte, and reset the interrupt */
+	if (pmu_state == reading || pmu_state == reading_intr)
+		bite = in_8(&via[SR]);
+
+	/* reset TREQ and wait for TACK to go high */
+	out_8(&via[B], in_8(&via[B]) | TREQ);
+	wait_for_ack();
+
+	switch (pmu_state) {
+	case sending:
+		req = current_req;
+		if (data_len < 0) {
+			data_len = req->nbytes - 1;
+			send_byte(data_len);
+			break;
+		}
+		if (data_index <= data_len) {
+			send_byte(req->data[data_index++]);
+			break;
+		}
+		req->sent = 1;
+		data_len = pmu_data_len[req->data[0]][1];
+		if (data_len == 0) {
+			pmu_state = idle;
+			current_req = req->next;
+			if (req->reply_expected)
+				req_awaiting_reply = req;
+			else
+				return req;
+		} else {
+			pmu_state = reading;
+			data_index = 0;
+			reply_ptr = req->reply + req->reply_len;
+			recv_byte();
+		}
+		break;
+
+	case intack:
+		data_index = 0;
+		data_len = -1;
+		pmu_state = reading_intr;
+		reply_ptr = interrupt_data[int_data_last];
+		recv_byte();
+		if (gpio_irq >= 0 && !gpio_irq_enabled) {
+			enable_irq(gpio_irq);
+			gpio_irq_enabled = 1;
+		}
+		break;
+
+	case reading:
+	case reading_intr:
+		if (data_len == -1) {
+			data_len = bite;
+			if (bite > 32)
+				printk(KERN_ERR "PMU: bad reply len %d\n", bite);
+		} else if (data_index < 32) {
+			reply_ptr[data_index++] = bite;
+		}
+		if (data_index < data_len) {
+			recv_byte();
+			break;
+		}
+
+		if (pmu_state == reading_intr) {
+			pmu_state = idle;
+			int_data_state[int_data_last] = int_data_ready;
+			interrupt_data_len[int_data_last] = data_len;
+		} else {
+			req = current_req;
+			/* 
+			 * For PMU sleep and freq change requests, we lock the
+			 * PMU until it's explicitely unlocked. This avoids any
+			 * spurrious event polling getting in
+			 */
+			current_req = req->next;
+			req->reply_len += data_index;
+			if (req->data[0] == PMU_SLEEP || req->data[0] == PMU_CPU_SPEED)
+				pmu_state = locked;
+			else
+				pmu_state = idle;
+			return req;
+		}
+		break;
+
+	default:
+		printk(KERN_ERR "via_pmu_interrupt: unknown state %d?\n",
+		       pmu_state);
+	}
+	return NULL;
+}
+
+static irqreturn_t __pmac
+via_pmu_interrupt(int irq, void *arg, struct pt_regs *regs)
+{
+	unsigned long flags;
+	int intr;
+	int nloop = 0;
+	int int_data = -1;
+	struct adb_request *req = NULL;
+	int handled = 0;
+
+	/* This is a bit brutal, we can probably do better */
+	spin_lock_irqsave(&pmu_lock, flags);
+	++disable_poll;
+	
+	for (;;) {
+		intr = in_8(&via[IFR]) & (SR_INT | CB1_INT);
+		if (intr == 0)
+			break;
+		handled = 1;
+		if (++nloop > 1000) {
+			printk(KERN_DEBUG "PMU: stuck in intr loop, "
+			       "intr=%x, ier=%x pmu_state=%d\n",
+			       intr, in_8(&via[IER]), pmu_state);
+			break;
+		}
+		out_8(&via[IFR], intr);
+		if (intr & CB1_INT) {
+			adb_int_pending = 1;
+			pmu_irq_stats[0]++;
+		}
+		if (intr & SR_INT) {
+			req = pmu_sr_intr(regs);
+			if (req)
+				break;
+		}
+	}
+
+recheck:
+	if (pmu_state == idle) {
+		if (adb_int_pending) {
+			if (int_data_state[0] == int_data_empty)
+				int_data_last = 0;
+			else if (int_data_state[1] == int_data_empty)
+				int_data_last = 1;
+			else
+				goto no_free_slot;
+			pmu_state = intack;
+			int_data_state[int_data_last] = int_data_fill;
+			/* Sounds safer to make sure ACK is high before writing.
+			 * This helped kill a problem with ADB and some iBooks
+			 */
+			wait_for_ack();
+			send_byte(PMU_INT_ACK);
+			adb_int_pending = 0;
+		} else if (current_req)
+			pmu_start();
+	}
+no_free_slot:			
+	/* Mark the oldest buffer for flushing */
+	if (int_data_state[!int_data_last] == int_data_ready) {
+		int_data_state[!int_data_last] = int_data_flush;
+		int_data = !int_data_last;
+	} else if (int_data_state[int_data_last] == int_data_ready) {
+		int_data_state[int_data_last] = int_data_flush;
+		int_data = int_data_last;
+	}
+	--disable_poll;
+	spin_unlock_irqrestore(&pmu_lock, flags);
+
+	/* Deal with completed PMU requests outside of the lock */
+	if (req) {
+		pmu_done(req);
+		req = NULL;
+	}
+		
+	/* Deal with interrupt datas outside of the lock */
+	if (int_data >= 0) {
+		pmu_handle_data(interrupt_data[int_data], interrupt_data_len[int_data], regs);
+		spin_lock_irqsave(&pmu_lock, flags);
+		++disable_poll;
+		int_data_state[int_data] = int_data_empty;
+		int_data = -1;
+		goto recheck;
+	}
+
+	return IRQ_RETVAL(handled);
+}
+
+void __pmac
+pmu_unlock(void)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&pmu_lock, flags);
+	if (pmu_state == locked)
+		pmu_state = idle;
+	adb_int_pending = 1;
+	spin_unlock_irqrestore(&pmu_lock, flags);
+}
+
+
+static irqreturn_t __pmac
+gpio1_interrupt(int irq, void *arg, struct pt_regs *regs)
+{
+	unsigned long flags;
+
+	if ((in_8(gpio_reg + 0x9) & 0x02) == 0) {
+		spin_lock_irqsave(&pmu_lock, flags);
+		if (gpio_irq_enabled > 0) {
+			disable_irq_nosync(gpio_irq);
+			gpio_irq_enabled = 0;
+		}
+		pmu_irq_stats[1]++;
+		adb_int_pending = 1;
+		spin_unlock_irqrestore(&pmu_lock, flags);
+		via_pmu_interrupt(0, NULL, NULL);
+		return IRQ_HANDLED;
+	}
+	return IRQ_NONE;
+}
+
+#ifdef CONFIG_PMAC_BACKLIGHT
+static int backlight_to_bright[] __pmacdata = {
+	0x7f, 0x46, 0x42, 0x3e, 0x3a, 0x36, 0x32, 0x2e,
+	0x2a, 0x26, 0x22, 0x1e, 0x1a, 0x16, 0x12, 0x0e
+};
+ 
+static int __openfirmware
+pmu_set_backlight_enable(int on, int level, void* data)
+{
+	struct adb_request req;
+	
+	if (vias == NULL)
+		return -ENODEV;
+
+	if (on) {
+		pmu_request(&req, NULL, 2, PMU_BACKLIGHT_BRIGHT,
+			    backlight_to_bright[level]);
+		pmu_wait_complete(&req);
+	}
+	pmu_request(&req, NULL, 2, PMU_POWER_CTRL,
+		    PMU_POW_BACKLIGHT | (on ? PMU_POW_ON : PMU_POW_OFF));
+       	pmu_wait_complete(&req);
+
+	return 0;
+}
+
+static void __openfirmware
+pmu_bright_complete(struct adb_request *req)
+{
+	if (req == &bright_req_1)
+		clear_bit(1, &async_req_locks);
+	if (req == &bright_req_2)
+		clear_bit(2, &async_req_locks);
+}
+
+static int __openfirmware
+pmu_set_backlight_level(int level, void* data)
+{
+	if (vias == NULL)
+		return -ENODEV;
+
+	if (test_and_set_bit(1, &async_req_locks))
+		return -EAGAIN;
+	pmu_request(&bright_req_1, pmu_bright_complete, 2, PMU_BACKLIGHT_BRIGHT,
+		backlight_to_bright[level]);
+	if (test_and_set_bit(2, &async_req_locks))
+		return -EAGAIN;
+	pmu_request(&bright_req_2, pmu_bright_complete, 2, PMU_POWER_CTRL,
+		    PMU_POW_BACKLIGHT | (level > BACKLIGHT_OFF ?
+					 PMU_POW_ON : PMU_POW_OFF));
+
+	return 0;
+}
+#endif /* CONFIG_PMAC_BACKLIGHT */
+
+void __pmac
+pmu_enable_irled(int on)
+{
+	struct adb_request req;
+
+	if (vias == NULL)
+		return ;
+	if (pmu_kind == PMU_KEYLARGO_BASED)
+		return ;
+
+	pmu_request(&req, NULL, 2, PMU_POWER_CTRL, PMU_POW_IRLED |
+	    (on ? PMU_POW_ON : PMU_POW_OFF));
+	pmu_wait_complete(&req);
+}
+
+void __pmac
+pmu_restart(void)
+{
+	struct adb_request req;
+
+	local_irq_disable();
+
+	drop_interrupts = 1;
+	
+	if (pmu_kind != PMU_KEYLARGO_BASED) {
+		pmu_request(&req, NULL, 2, PMU_SET_INTR_MASK, PMU_INT_ADB |
+						PMU_INT_TICK );
+		while(!req.complete)
+			pmu_poll();
+	}
+
+	pmu_request(&req, NULL, 1, PMU_RESET);
+	pmu_wait_complete(&req);
+	for (;;)
+		;
+}
+
+void __pmac
+pmu_shutdown(void)
+{
+	struct adb_request req;
+
+	local_irq_disable();
+
+	drop_interrupts = 1;
+
+	if (pmu_kind != PMU_KEYLARGO_BASED) {
+		pmu_request(&req, NULL, 2, PMU_SET_INTR_MASK, PMU_INT_ADB |
+						PMU_INT_TICK );
+		pmu_wait_complete(&req);
+	} else {
+		/* Disable server mode on shutdown or we'll just
+		 * wake up again
+		 */
+		pmu_set_server_mode(0);
+	}
+
+	pmu_request(&req, NULL, 5, PMU_SHUTDOWN,
+		    'M', 'A', 'T', 'T');
+	pmu_wait_complete(&req);
+	for (;;)
+		;
+}
+
+int
+pmu_present(void)
+{
+	return via != 0;
+}
+
+struct pmu_i2c_hdr {
+	u8	bus;
+	u8	mode;
+	u8	bus2;
+	u8	address;
+	u8	sub_addr;
+	u8	comb_addr;
+	u8	count;
+};
+
+int
+pmu_i2c_combined_read(int bus, int addr, int subaddr,  u8* data, int len)
+{
+	struct adb_request	req;
+	struct pmu_i2c_hdr	*hdr = (struct pmu_i2c_hdr *)&req.data[1];
+	int retry;
+	int rc;
+
+	for (retry=0; retry<16; retry++) {
+		memset(&req, 0, sizeof(req));
+
+		hdr->bus = bus;
+		hdr->address = addr & 0xfe;
+		hdr->mode = PMU_I2C_MODE_COMBINED;
+		hdr->bus2 = 0;
+		hdr->sub_addr = subaddr;
+		hdr->comb_addr = addr | 1;
+		hdr->count = len;
+		
+		req.nbytes = sizeof(struct pmu_i2c_hdr) + 1;
+		req.reply_expected = 0;
+		req.reply_len = 0;
+		req.data[0] = PMU_I2C_CMD;
+		req.reply[0] = 0xff;
+		rc = pmu_queue_request(&req);
+		if (rc)
+			return rc;
+		while(!req.complete)
+			pmu_poll();
+		if (req.reply[0] == PMU_I2C_STATUS_OK)
+			break;
+		mdelay(15);
+	}
+	if (req.reply[0] != PMU_I2C_STATUS_OK)
+		return -1;
+
+	for (retry=0; retry<16; retry++) {
+		memset(&req, 0, sizeof(req));
+
+		mdelay(15);
+
+		hdr->bus = PMU_I2C_BUS_STATUS;
+		req.reply[0] = 0xff;
+		
+		req.nbytes = 2;
+		req.reply_expected = 0;
+		req.reply_len = 0;
+		req.data[0] = PMU_I2C_CMD;
+		rc = pmu_queue_request(&req);
+		if (rc)
+			return rc;
+		while(!req.complete)
+			pmu_poll();
+		if (req.reply[0] == PMU_I2C_STATUS_DATAREAD) {
+			memcpy(data, &req.reply[1], req.reply_len - 1);
+			return req.reply_len - 1;
+		}
+	}
+	return -1;
+}
+
+int
+pmu_i2c_stdsub_write(int bus, int addr, int subaddr,  u8* data, int len)
+{
+	struct adb_request	req;
+	struct pmu_i2c_hdr	*hdr = (struct pmu_i2c_hdr *)&req.data[1];
+	int retry;
+	int rc;
+
+	for (retry=0; retry<16; retry++) {
+		memset(&req, 0, sizeof(req));
+
+		hdr->bus = bus;
+		hdr->address = addr & 0xfe;
+		hdr->mode = PMU_I2C_MODE_STDSUB;
+		hdr->bus2 = 0;
+		hdr->sub_addr = subaddr;
+		hdr->comb_addr = addr & 0xfe;
+		hdr->count = len;
+
+		req.data[0] = PMU_I2C_CMD;
+		memcpy(&req.data[sizeof(struct pmu_i2c_hdr) + 1], data, len);
+		req.nbytes = sizeof(struct pmu_i2c_hdr) + len + 1;
+		req.reply_expected = 0;
+		req.reply_len = 0;
+		req.reply[0] = 0xff;
+		rc = pmu_queue_request(&req);
+		if (rc)
+			return rc;
+		while(!req.complete)
+			pmu_poll();
+		if (req.reply[0] == PMU_I2C_STATUS_OK)
+			break;
+		mdelay(15);
+	}
+	if (req.reply[0] != PMU_I2C_STATUS_OK)
+		return -1;
+
+	for (retry=0; retry<16; retry++) {
+		memset(&req, 0, sizeof(req));
+
+		mdelay(15);
+
+		hdr->bus = PMU_I2C_BUS_STATUS;
+		req.reply[0] = 0xff;
+		
+		req.nbytes = 2;
+		req.reply_expected = 0;
+		req.reply_len = 0;
+		req.data[0] = PMU_I2C_CMD;
+		rc = pmu_queue_request(&req);
+		if (rc)
+			return rc;
+		while(!req.complete)
+			pmu_poll();
+		if (req.reply[0] == PMU_I2C_STATUS_OK)
+			return len;
+	}
+	return -1;
+}
+
+int
+pmu_i2c_simple_read(int bus, int addr,  u8* data, int len)
+{
+	struct adb_request	req;
+	struct pmu_i2c_hdr	*hdr = (struct pmu_i2c_hdr *)&req.data[1];
+	int retry;
+	int rc;
+
+	for (retry=0; retry<16; retry++) {
+		memset(&req, 0, sizeof(req));
+
+		hdr->bus = bus;
+		hdr->address = addr | 1;
+		hdr->mode = PMU_I2C_MODE_SIMPLE;
+		hdr->bus2 = 0;
+		hdr->sub_addr = 0;
+		hdr->comb_addr = 0;
+		hdr->count = len;
+
+		req.data[0] = PMU_I2C_CMD;
+		req.nbytes = sizeof(struct pmu_i2c_hdr) + 1;
+		req.reply_expected = 0;
+		req.reply_len = 0;
+		req.reply[0] = 0xff;
+		rc = pmu_queue_request(&req);
+		if (rc)
+			return rc;
+		while(!req.complete)
+			pmu_poll();
+		if (req.reply[0] == PMU_I2C_STATUS_OK)
+			break;
+		mdelay(15);
+	}
+	if (req.reply[0] != PMU_I2C_STATUS_OK)
+		return -1;
+
+	for (retry=0; retry<16; retry++) {
+		memset(&req, 0, sizeof(req));
+
+		mdelay(15);
+
+		hdr->bus = PMU_I2C_BUS_STATUS;
+		req.reply[0] = 0xff;
+		
+		req.nbytes = 2;
+		req.reply_expected = 0;
+		req.reply_len = 0;
+		req.data[0] = PMU_I2C_CMD;
+		rc = pmu_queue_request(&req);
+		if (rc)
+			return rc;
+		while(!req.complete)
+			pmu_poll();
+		if (req.reply[0] == PMU_I2C_STATUS_DATAREAD) {
+			memcpy(data, &req.reply[1], req.reply_len - 1);
+			return req.reply_len - 1;
+		}
+	}
+	return -1;
+}
+
+int
+pmu_i2c_simple_write(int bus, int addr,  u8* data, int len)
+{
+	struct adb_request	req;
+	struct pmu_i2c_hdr	*hdr = (struct pmu_i2c_hdr *)&req.data[1];
+	int retry;
+	int rc;
+
+	for (retry=0; retry<16; retry++) {
+		memset(&req, 0, sizeof(req));
+
+		hdr->bus = bus;
+		hdr->address = addr & 0xfe;
+		hdr->mode = PMU_I2C_MODE_SIMPLE;
+		hdr->bus2 = 0;
+		hdr->sub_addr = 0;
+		hdr->comb_addr = 0;
+		hdr->count = len;
+
+		req.data[0] = PMU_I2C_CMD;
+		memcpy(&req.data[sizeof(struct pmu_i2c_hdr) + 1], data, len);
+		req.nbytes = sizeof(struct pmu_i2c_hdr) + len + 1;
+		req.reply_expected = 0;
+		req.reply_len = 0;
+		req.reply[0] = 0xff;
+		rc = pmu_queue_request(&req);
+		if (rc)
+			return rc;
+		while(!req.complete)
+			pmu_poll();
+		if (req.reply[0] == PMU_I2C_STATUS_OK)
+			break;
+		mdelay(15);
+	}
+	if (req.reply[0] != PMU_I2C_STATUS_OK)
+		return -1;
+
+	for (retry=0; retry<16; retry++) {
+		memset(&req, 0, sizeof(req));
+
+		mdelay(15);
+
+		hdr->bus = PMU_I2C_BUS_STATUS;
+		req.reply[0] = 0xff;
+		
+		req.nbytes = 2;
+		req.reply_expected = 0;
+		req.reply_len = 0;
+		req.data[0] = PMU_I2C_CMD;
+		rc = pmu_queue_request(&req);
+		if (rc)
+			return rc;
+		while(!req.complete)
+			pmu_poll();
+		if (req.reply[0] == PMU_I2C_STATUS_OK)
+			return len;
+	}
+	return -1;
+}
+
+#ifdef CONFIG_PMAC_PBOOK
+
+static LIST_HEAD(sleep_notifiers);
+
+int
+pmu_register_sleep_notifier(struct pmu_sleep_notifier *n)
+{
+	struct list_head *list;
+	struct pmu_sleep_notifier *notifier;
+
+	for (list = sleep_notifiers.next; list != &sleep_notifiers;
+	     list = list->next) {
+		notifier = list_entry(list, struct pmu_sleep_notifier, list);
+		if (n->priority > notifier->priority)
+			break;
+	}
+	__list_add(&n->list, list->prev, list);
+	return 0;
+}
+
+int
+pmu_unregister_sleep_notifier(struct pmu_sleep_notifier* n)
+{
+	if (n->list.next == 0)
+		return -ENOENT;
+	list_del(&n->list);
+	n->list.next = NULL;
+	return 0;
+}
+
+/* Sleep is broadcast last-to-first */
+static int __pmac
+broadcast_sleep(int when, int fallback)
+{
+	int ret = PBOOK_SLEEP_OK;
+	struct list_head *list;
+	struct pmu_sleep_notifier *notifier;
+
+	for (list = sleep_notifiers.prev; list != &sleep_notifiers;
+	     list = list->prev) {
+		notifier = list_entry(list, struct pmu_sleep_notifier, list);
+		ret = notifier->notifier_call(notifier, when);
+		if (ret != PBOOK_SLEEP_OK) {
+			printk(KERN_DEBUG "sleep %d rejected by %p (%p)\n",
+			       when, notifier, notifier->notifier_call);
+			for (; list != &sleep_notifiers; list = list->next) {
+				notifier = list_entry(list, struct pmu_sleep_notifier, list);
+				notifier->notifier_call(notifier, fallback);
+			}
+			return ret;
+		}
+	}
+	return ret;
+}
+
+/* Wake is broadcast first-to-last */
+static int __pmac
+broadcast_wake(void)
+{
+	int ret = PBOOK_SLEEP_OK;
+	struct list_head *list;
+	struct pmu_sleep_notifier *notifier;
+
+	for (list = sleep_notifiers.next; list != &sleep_notifiers;
+	     list = list->next) {
+		notifier = list_entry(list, struct pmu_sleep_notifier, list);
+		notifier->notifier_call(notifier, PBOOK_WAKE);
+	}
+	return ret;
+}
+
+/*
+ * This struct is used to store config register values for
+ * 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 pbook_npci_saves;
+
+static void __pmac
+pbook_alloc_pci_save(void)
+{
+	int npci;
+	struct pci_dev *pd = NULL;
+
+	npci = 0;
+	while ((pd = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, pd)) != NULL) {
+		++npci;
+	}
+	if (npci == 0)
+		return;
+	pbook_pci_saves = (struct pci_save *)
+		kmalloc(npci * sizeof(struct pci_save), GFP_KERNEL);
+	pbook_npci_saves = npci;
+}
+
+static void __pmac
+pbook_free_pci_save(void)
+{
+	if (pbook_pci_saves == NULL)
+		return;
+	kfree(pbook_pci_saves);
+	pbook_pci_saves = NULL;
+	pbook_npci_saves = 0;
+}
+
+static void __pmac
+pbook_pci_save(void)
+{
+	struct pci_save *ps = pbook_pci_saves;
+	struct pci_dev *pd = NULL;
+	int npci = pbook_npci_saves;
+	
+	if (ps == NULL)
+		return;
+
+	while ((pd = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, pd)) != NULL) {
+		if (npci-- == 0)
+			return;
+#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 __pmac
+pbook_pci_restore(void)
+{
+	u16 cmd;
+	struct pci_save *ps = pbook_pci_saves - 1;
+	struct pci_dev *pd = NULL;
+	int npci = pbook_npci_saves;
+	int j;
+
+	while ((pd = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, pd)) != NULL) {
+#ifdef HACKED_PCI_SAVE
+		int i;
+		if (npci-- == 0)
+			return;
+		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 (npci-- == 0)
+			return;
+		ps++;
+		if (ps->command == 0)
+			continue;
+		pci_read_config_word(pd, PCI_COMMAND, &cmd);
+		if ((ps->command & ~cmd) == 0)
+			continue;
+		switch (pd->hdr_type) {
+		case PCI_HEADER_TYPE_NORMAL:
+			for (j = 0; j < 6; ++j)
+				pci_write_config_dword(pd,
+					PCI_BASE_ADDRESS_0 + j*4,
+					pd->resource[j].start);
+			pci_write_config_dword(pd, PCI_ROM_ADDRESS,
+				ps->rom_address);
+			pci_write_config_word(pd, PCI_CACHE_LINE_SIZE,
+				ps->cache_lat);
+			pci_write_config_word(pd, PCI_INTERRUPT_LINE,
+				ps->intr);
+			pci_write_config_word(pd, PCI_COMMAND, ps->command);
+			break;
+		}
+#endif	
+	}
+}
+
+#ifdef DEBUG_SLEEP
+/* N.B. This doesn't work on the 3400 */
+void  __pmac
+pmu_blink(int n)
+{
+	struct adb_request req;
+
+	memset(&req, 0, sizeof(req));
+
+	for (; n > 0; --n) {
+		req.nbytes = 4;
+		req.done = NULL;
+		req.data[0] = 0xee;
+		req.data[1] = 4;
+		req.data[2] = 0;
+		req.data[3] = 1;
+		req.reply[0] = ADB_RET_OK;
+		req.reply_len = 1;
+		req.reply_expected = 0;
+		pmu_polled_request(&req);
+		mdelay(50);
+		req.nbytes = 4;
+		req.done = NULL;
+		req.data[0] = 0xee;
+		req.data[1] = 4;
+		req.data[2] = 0;
+		req.data[3] = 0;
+		req.reply[0] = ADB_RET_OK;
+		req.reply_len = 1;
+		req.reply_expected = 0;
+		pmu_polled_request(&req);
+		mdelay(50);
+	}
+	mdelay(50);
+}
+#endif
+
+/*
+ * Put the powerbook to sleep.
+ */
+ 
+static u32 save_via[8] __pmacdata;
+
+static void __pmac
+save_via_state(void)
+{
+	save_via[0] = in_8(&via[ANH]);
+	save_via[1] = in_8(&via[DIRA]);
+	save_via[2] = in_8(&via[B]);
+	save_via[3] = in_8(&via[DIRB]);
+	save_via[4] = in_8(&via[PCR]);
+	save_via[5] = in_8(&via[ACR]);
+	save_via[6] = in_8(&via[T1CL]);
+	save_via[7] = in_8(&via[T1CH]);
+}
+static void __pmac
+restore_via_state(void)
+{
+	out_8(&via[ANH], save_via[0]);
+	out_8(&via[DIRA], save_via[1]);
+	out_8(&via[B], save_via[2]);
+	out_8(&via[DIRB], save_via[3]);
+	out_8(&via[PCR], save_via[4]);
+	out_8(&via[ACR], save_via[5]);
+	out_8(&via[T1CL], save_via[6]);
+	out_8(&via[T1CH], save_via[7]);
+	out_8(&via[IER], IER_CLR | 0x7f);	/* disable all intrs */
+	out_8(&via[IFR], 0x7f);				/* clear IFR */
+	out_8(&via[IER], IER_SET | SR_INT | CB1_INT);
+}
+
+static int __pmac
+pmac_suspend_devices(void)
+{
+	int ret;
+	
+	pm_prepare_console();
+	
+	/* Notify old-style device drivers & userland */
+	ret = broadcast_sleep(PBOOK_SLEEP_REQUEST, PBOOK_SLEEP_REJECT);
+	if (ret != PBOOK_SLEEP_OK) {
+		printk(KERN_ERR "Sleep rejected by drivers\n");
+		return -EBUSY;
+	}
+
+	/* Sync the disks. */
+	/* XXX It would be nice to have some way to ensure that
+	 * nobody is dirtying any new buffers while we wait. That
+	 * could be acheived using the refrigerator for processes
+	 * that swsusp uses
+	 */
+	sys_sync();
+
+	/* 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(KERN_ERR "Driver sleep failed\n");
+		return -EBUSY;
+	}
+
+	/* Send suspend call to devices, hold the device core's dpm_sem */
+	ret = device_suspend(PM_SUSPEND_MEM);
+	if (ret) {
+		printk(KERN_ERR "Driver sleep failed\n");
+		broadcast_wake();
+		return -EBUSY;
+	}
+	
+	preempt_disable();
+	
+	/* Make sure the decrementer won't interrupt us */
+	asm volatile("mtdec %0" : : "r" (0x7fffffff));
+	/* Make sure any pending DEC interrupt occurring while we did
+	 * the above didn't re-enable the DEC */
+	mb();
+	asm volatile("mtdec %0" : : "r" (0x7fffffff));
+
+	/* We can now disable MSR_EE. This code of course works properly only
+	 * on UP machines... For SMP, if we ever implement sleep, we'll have to
+	 * stop the "other" CPUs way before we do all that stuff.
+	 */
+	local_irq_disable();
+
+	/* Broadcast power down irq
+	 * This isn't that useful in most cases (only directly wired devices can
+	 * use this but still... This will take care of sysdev's as well, so
+	 * we exit from here with local irqs disabled and PIC off.
+	 */
+	ret = device_power_down(PM_SUSPEND_MEM);
+	if (ret) {
+		wakeup_decrementer();
+		local_irq_enable();
+		preempt_enable();
+		device_resume();
+		broadcast_wake();
+		printk(KERN_ERR "Driver powerdown failed\n");
+		return -EBUSY;
+	}
+
+	/* Wait for completion of async backlight requests */
+	while (!bright_req_1.complete || !bright_req_2.complete ||
+
+			!batt_req.complete)
+		pmu_poll();
+
+	/* Giveup the lazy FPU & vec so we don't have to back them
+	 * up from the low level code
+	 */
+	enable_kernel_fp();
+
+#ifdef CONFIG_ALTIVEC
+	if (cur_cpu_spec[0]->cpu_features & CPU_FTR_ALTIVEC)
+		enable_kernel_altivec();
+#endif /* CONFIG_ALTIVEC */
+
+	return 0;
+}
+
+static int __pmac
+pmac_wakeup_devices(void)
+{
+	mdelay(100);
+
+	/* Power back up system devices (including the PIC) */
+	device_power_up();
+
+	pmu_blink(1);
+
+	/* Force a poll of ADB interrupts */
+	adb_int_pending = 1;
+	via_pmu_interrupt(0, NULL, NULL);
+
+	/* Restart jiffies & scheduling */
+	wakeup_decrementer();
+
+	/* Re-enable local CPU interrupts */
+	local_irq_enable();
+
+	pmu_blink(1);
+
+	preempt_enable();
+
+	/* Resume devices */
+	device_resume();
+
+	/* Notify old style drivers */
+	broadcast_wake();
+
+	pm_restore_console();
+
+	return 0;
+}
+
+#define	GRACKLE_PM	(1<<7)
+#define GRACKLE_DOZE	(1<<5)
+#define	GRACKLE_NAP	(1<<4)
+#define	GRACKLE_SLEEP	(1<<3)
+
+int __pmac
+powerbook_sleep_grackle(void)
+{
+	unsigned long save_l2cr;
+	unsigned short pmcr1;
+	struct adb_request req;
+	int ret;
+	struct pci_dev *grackle;
+
+	grackle = pci_find_slot(0, 0);
+	if (!grackle)
+		return -ENODEV;
+
+	ret = pmac_suspend_devices();
+	if (ret) {
+		printk(KERN_ERR "Sleep rejected by devices\n");
+		return ret;
+	}
+	
+	/* Turn off various things. Darwin does some retry tests here... */
+	pmu_request(&req, NULL, 2, PMU_POWER_CTRL0, PMU_POW0_OFF|PMU_POW0_HARD_DRIVE);
+	pmu_wait_complete(&req);
+	pmu_request(&req, NULL, 2, PMU_POWER_CTRL,
+		PMU_POW_OFF|PMU_POW_BACKLIGHT|PMU_POW_IRLED|PMU_POW_MEDIABAY);
+	pmu_wait_complete(&req);
+
+	/* For 750, save backside cache setting and disable it */
+	save_l2cr = _get_L2CR();	/* (returns -1 if not available) */
+	if (save_l2cr != 0xffffffff && (save_l2cr & L2CR_L2E) != 0)
+		_set_L2CR(save_l2cr & 0x7fffffff);
+
+	if (!__fake_sleep) {
+		/* Ask the PMU to put us to sleep */
+		pmu_request(&req, NULL, 5, PMU_SLEEP, 'M', 'A', 'T', 'T');
+		pmu_wait_complete(&req);
+	}
+
+	/* The VIA is supposed not to be restored correctly*/
+	save_via_state();
+	/* We shut down some HW */
+	pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,1);
+
+	pci_read_config_word(grackle, 0x70, &pmcr1);
+	/* Apparently, MacOS uses NAP mode for Grackle ??? */
+	pmcr1 &= ~(GRACKLE_DOZE|GRACKLE_SLEEP); 
+	pmcr1 |= GRACKLE_PM|GRACKLE_NAP;
+	pci_write_config_word(grackle, 0x70, pmcr1);
+
+	/* Call low-level ASM sleep handler */
+	if (__fake_sleep)
+		mdelay(5000);
+	else
+		low_sleep_handler();
+
+	/* We're awake again, stop grackle PM */
+	pci_read_config_word(grackle, 0x70, &pmcr1);
+	pmcr1 &= ~(GRACKLE_PM|GRACKLE_DOZE|GRACKLE_SLEEP|GRACKLE_NAP); 
+	pci_write_config_word(grackle, 0x70, pmcr1);
+
+	/* Make sure the PMU is idle */
+	pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,0);
+	restore_via_state();
+	
+	/* Restore L2 cache */
+	if (save_l2cr != 0xffffffff && (save_l2cr & L2CR_L2E) != 0)
+ 		_set_L2CR(save_l2cr);
+	
+	/* Restore userland MMU context */
+	set_context(current->active_mm->context, current->active_mm->pgd);
+
+	/* Power things up */
+	pmu_unlock();
+	pmu_request(&req, NULL, 2, PMU_SET_INTR_MASK, pmu_intr_mask);
+	pmu_wait_complete(&req);
+	pmu_request(&req, NULL, 2, PMU_POWER_CTRL0,
+			PMU_POW0_ON|PMU_POW0_HARD_DRIVE);
+	pmu_wait_complete(&req);
+	pmu_request(&req, NULL, 2, PMU_POWER_CTRL,
+			PMU_POW_ON|PMU_POW_BACKLIGHT|PMU_POW_CHARGER|PMU_POW_IRLED|PMU_POW_MEDIABAY);
+	pmu_wait_complete(&req);
+
+	pmac_wakeup_devices();
+
+	return 0;
+}
+
+static int __pmac
+powerbook_sleep_Core99(void)
+{
+	unsigned long save_l2cr;
+	unsigned long save_l3cr;
+	struct adb_request req;
+	int ret;
+	
+	if (!can_sleep) {
+		printk(KERN_ERR "Sleep mode not supported on this machine\n");
+		return -ENOSYS;
+	}
+	
+	ret = pmac_suspend_devices();
+	if (ret) {
+		printk(KERN_ERR "Sleep rejected by devices\n");
+		return ret;
+	}
+	
+	/* Tell PMU what events will wake us up */
+	pmu_request(&req, NULL, 4, PMU_POWER_EVENTS, PMU_PWR_CLR_WAKEUP_EVENTS,
+		0xff, 0xff);
+	pmu_wait_complete(&req);
+	pmu_request(&req, NULL, 4, PMU_POWER_EVENTS, PMU_PWR_SET_WAKEUP_EVENTS,
+		0, PMU_PWR_WAKEUP_KEY |
+		(option_lid_wakeup ? PMU_PWR_WAKEUP_LID_OPEN : 0));
+	pmu_wait_complete(&req);
+
+	/* Save & disable L2 and L3 caches*/
+	save_l3cr = _get_L3CR();	/* (returns -1 if not available) */
+	save_l2cr = _get_L2CR();	/* (returns -1 if not available) */
+	if (save_l3cr != 0xffffffff && (save_l3cr & L3CR_L3E) != 0)
+		_set_L3CR(save_l3cr & 0x7fffffff);
+	if (save_l2cr != 0xffffffff && (save_l2cr & L2CR_L2E) != 0)
+		_set_L2CR(save_l2cr & 0x7fffffff);
+
+	/* Save the state of PCI config space for some slots */
+	//pbook_pci_save();
+
+	if (!__fake_sleep) {
+		/* Ask the PMU to put us to sleep */
+		pmu_request(&req, NULL, 5, PMU_SLEEP, 'M', 'A', 'T', 'T');
+		pmu_wait_complete(&req);
+	}
+
+	/* The VIA is supposed not to be restored correctly*/
+	save_via_state();
+
+	/* Shut down various ASICs. There's a chance that we can no longer
+	 * talk to the PMU after this, so I moved it to _after_ sending the
+	 * sleep command to it. Still need to be checked.
+	 */
+	pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,1);
+
+	/* Call low-level ASM sleep handler */
+	if (__fake_sleep)
+		mdelay(5000);
+	else
+		low_sleep_handler();
+
+	/* Restore Apple core ASICs state */
+	pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,0);
+
+	/* Restore VIA */
+	restore_via_state();
+
+	/* Restore PCI config space. This should be overridable by PCI device
+	 * drivers as some of them may need special restore code. That's yet
+	 * another issue that should be handled by the common code properly,
+	 * maybe one day ?
+	 */
+	/* Don't restore PCI for now, it crashes. Maybe unnecessary on pbook */
+	//pbook_pci_restore();
+
+	/* Restore L2 cache */
+	if (save_l2cr != 0xffffffff && (save_l2cr & L2CR_L2E) != 0)
+ 		_set_L2CR(save_l2cr);
+	/* Restore L3 cache */
+	if (save_l3cr != 0xffffffff && (save_l3cr & L3CR_L3E) != 0)
+ 		_set_L3CR(save_l3cr);
+	
+	/* Restore userland MMU context */
+	set_context(current->active_mm->context, current->active_mm->pgd);
+
+	/* Tell PMU we are ready */
+	pmu_unlock();
+	pmu_request(&req, NULL, 2, PMU_SYSTEM_READY, 2);
+	pmu_wait_complete(&req);
+	pmu_request(&req, NULL, 2, PMU_SET_INTR_MASK, pmu_intr_mask);
+	pmu_wait_complete(&req);
+
+	pmu_blink(1);
+
+	pmac_wakeup_devices();
+
+	return 0;
+}
+
+#define PB3400_MEM_CTRL		0xf8000000
+#define PB3400_MEM_CTRL_SLEEP	0x70
+
+static int __pmac
+powerbook_sleep_3400(void)
+{
+	int ret, i, x;
+	unsigned int hid0;
+	unsigned long p;
+	struct adb_request sleep_req;
+	char *mem_ctrl;
+	unsigned int *mem_ctrl_sleep;
+
+	/* first map in the memory controller registers */
+	mem_ctrl = ioremap(PB3400_MEM_CTRL, 0x100);
+	if (mem_ctrl == NULL) {
+		printk("powerbook_sleep_3400: ioremap failed\n");
+		return -ENOMEM;
+	}
+	mem_ctrl_sleep = (unsigned int *) (mem_ctrl + PB3400_MEM_CTRL_SLEEP);
+
+	/* Allocate room for PCI save */
+	pbook_alloc_pci_save();
+
+	ret = pmac_suspend_devices();
+	if (ret) {
+		pbook_free_pci_save();
+		printk(KERN_ERR "Sleep rejected by devices\n");
+		return ret;
+	}
+
+	/* Save the state of PCI config space for some slots */
+	pbook_pci_save();
+
+	/* Set the memory controller to keep the memory refreshed
+	   while we're asleep */
+	for (i = 0x403f; i >= 0x4000; --i) {
+		out_be32(mem_ctrl_sleep, i);
+		do {
+			x = (in_be32(mem_ctrl_sleep) >> 16) & 0x3ff;
+		} while (x == 0);
+		if (x >= 0x100)
+			break;
+	}
+
+	/* Ask the PMU to put us to sleep */
+	pmu_request(&sleep_req, NULL, 5, PMU_SLEEP, 'M', 'A', 'T', 'T');
+	while (!sleep_req.complete)
+		mb();
+
+	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;
+	asleep = 1;
+
+	/* Put the CPU into sleep mode */
+	asm volatile("mfspr %0,1008" : "=r" (hid0) :);
+	hid0 = (hid0 & ~(HID0_NAP | HID0_DOZE)) | HID0_SLEEP;
+	asm volatile("mtspr 1008,%0" : : "r" (hid0));
+	_nmask_and_or_msr(0, MSR_POW | MSR_EE);
+	udelay(10);
+
+	/* 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();
+	pmu_unlock();
+
+	/* wait for the PMU interrupt sequence to complete */
+	while (asleep)
+		mb();
+
+	pmac_wakeup_devices();
+	pbook_free_pci_save();
+	iounmap(mem_ctrl);
+
+	return 0;
+}
+
+/*
+ * Support for /dev/pmu device
+ */
+#define RB_SIZE		0x10
+struct pmu_private {
+	struct list_head list;
+	int	rb_get;
+	int	rb_put;
+	struct rb_entry {
+		unsigned short len;
+		unsigned char data[16];
+	}	rb_buf[RB_SIZE];
+	wait_queue_head_t wait;
+	spinlock_t lock;
+#if defined(CONFIG_INPUT_ADBHID) && defined(CONFIG_PMAC_BACKLIGHT)
+	int	backlight_locker;
+#endif /* defined(CONFIG_INPUT_ADBHID) && defined(CONFIG_PMAC_BACKLIGHT) */	
+};
+
+static LIST_HEAD(all_pmu_pvt);
+static spinlock_t all_pvt_lock __pmacdata = SPIN_LOCK_UNLOCKED;
+
+static void __pmac
+pmu_pass_intr(unsigned char *data, int len)
+{
+	struct pmu_private *pp;
+	struct list_head *list;
+	int i;
+	unsigned long flags;
+
+	if (len > sizeof(pp->rb_buf[0].data))
+		len = sizeof(pp->rb_buf[0].data);
+	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;
+		if (i != pp->rb_get) {
+			struct rb_entry *rp = &pp->rb_buf[pp->rb_put];
+			rp->len = len;
+			memcpy(rp->data, data, len);
+			pp->rb_put = i;
+			wake_up_interruptible(&pp->wait);
+		}
+		spin_unlock(&pp->lock);
+	}
+	spin_unlock_irqrestore(&all_pvt_lock, flags);
+}
+
+static int __pmac
+pmu_open(struct inode *inode, struct file *file)
+{
+	struct pmu_private *pp;
+	unsigned long flags;
+
+	pp = kmalloc(sizeof(struct pmu_private), GFP_KERNEL);
+	if (pp == 0)
+		return -ENOMEM;
+	pp->rb_get = pp->rb_put = 0;
+	spin_lock_init(&pp->lock);
+	init_waitqueue_head(&pp->wait);
+	spin_lock_irqsave(&all_pvt_lock, flags);
+#if defined(CONFIG_INPUT_ADBHID) && defined(CONFIG_PMAC_BACKLIGHT)
+	pp->backlight_locker = 0;
+#endif /* defined(CONFIG_INPUT_ADBHID) && defined(CONFIG_PMAC_BACKLIGHT) */	
+	list_add(&pp->list, &all_pmu_pvt);
+	spin_unlock_irqrestore(&all_pvt_lock, flags);
+	file->private_data = pp;
+	return 0;
+}
+
+static ssize_t  __pmac
+pmu_read(struct file *file, char __user *buf,
+			size_t count, loff_t *ppos)
+{
+	struct pmu_private *pp = file->private_data;
+	DECLARE_WAITQUEUE(wait, current);
+	unsigned long flags;
+	int ret;
+
+	if (count < 1 || pp == 0)
+		return -EINVAL;
+	ret = verify_area(VERIFY_WRITE, buf, count);
+	if (ret)
+		return ret;
+
+	spin_lock_irqsave(&pp->lock, flags);
+	add_wait_queue(&pp->wait, &wait);
+	current->state = TASK_INTERRUPTIBLE;
+
+	for (;;) {
+		ret = -EAGAIN;
+		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;
+		}
+		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;
+}
+
+static ssize_t __pmac
+pmu_write(struct file *file, const char __user *buf,
+			 size_t count, loff_t *ppos)
+{
+	return 0;
+}
+
+static unsigned int __pmac
+pmu_fpoll(struct file *filp, poll_table *wait)
+{
+	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_irqsave(&pp->lock, flags);
+	if (pp->rb_get != pp->rb_put)
+		mask |= POLLIN;
+	spin_unlock_irqrestore(&pp->lock, flags);
+	return mask;
+}
+
+static int __pmac
+pmu_release(struct inode *inode, struct file *file)
+{
+	struct pmu_private *pp = file->private_data;
+	unsigned long flags;
+
+	lock_kernel();
+	if (pp != 0) {
+		file->private_data = NULL;
+		spin_lock_irqsave(&all_pvt_lock, flags);
+		list_del(&pp->list);
+		spin_unlock_irqrestore(&all_pvt_lock, flags);
+#if defined(CONFIG_INPUT_ADBHID) && defined(CONFIG_PMAC_BACKLIGHT)
+		if (pp->backlight_locker) {
+			spin_lock_irqsave(&pmu_lock, flags);
+			disable_kernel_backlight--;
+			spin_unlock_irqrestore(&pmu_lock, flags);
+		}
+#endif /* defined(CONFIG_INPUT_ADBHID) && defined(CONFIG_PMAC_BACKLIGHT) */
+		kfree(pp);
+	}
+	unlock_kernel();
+	return 0;
+}
+
+/* Note: removed __openfirmware here since it causes link errors */
+static int __pmac
+pmu_ioctl(struct inode * inode, struct file *filp,
+		     u_int cmd, u_long arg)
+{
+	struct pmu_private *pp = filp->private_data;
+	__u32 __user *argp = (__u32 __user *)arg;
+	int error;
+
+	switch (cmd) {
+	case PMU_IOC_SLEEP:
+		if (!capable(CAP_SYS_ADMIN))
+			return -EACCES;
+		if (sleep_in_progress)
+			return -EBUSY;
+		sleep_in_progress = 1;
+		switch (pmu_kind) {
+		case PMU_OHARE_BASED:
+			error = powerbook_sleep_3400();
+			break;
+		case PMU_HEATHROW_BASED:
+		case PMU_PADDINGTON_BASED:
+			error = powerbook_sleep_grackle();
+			break;
+		case PMU_KEYLARGO_BASED:
+			error = powerbook_sleep_Core99();
+			break;
+		default:
+			error = -ENOSYS;
+		}
+		sleep_in_progress = 0;
+		return error;
+	case PMU_IOC_CAN_SLEEP:
+		return put_user((u32)can_sleep, argp);
+
+#ifdef CONFIG_PMAC_BACKLIGHT
+	/* Backlight should have its own device or go via
+	 * the fbdev
+	 */
+	case PMU_IOC_GET_BACKLIGHT:
+		if (sleep_in_progress)
+			return -EBUSY;
+		error = get_backlight_level();
+		if (error < 0)
+			return error;
+		return put_user(error, argp);
+	case PMU_IOC_SET_BACKLIGHT:
+	{
+		__u32 value;
+		if (sleep_in_progress)
+			return -EBUSY;
+		error = get_user(value, argp);
+		if (!error)
+			error = set_backlight_level(value);
+		return error;
+	}
+#ifdef CONFIG_INPUT_ADBHID
+	case PMU_IOC_GRAB_BACKLIGHT: {
+		unsigned long flags;
+		if (pp->backlight_locker)
+			return 0;
+		pp->backlight_locker = 1;
+		spin_lock_irqsave(&pmu_lock, flags);
+		disable_kernel_backlight++;
+		spin_unlock_irqrestore(&pmu_lock, flags);
+		return 0;
+	}
+#endif /* CONFIG_INPUT_ADBHID */
+#endif /* CONFIG_PMAC_BACKLIGHT */
+	case PMU_IOC_GET_MODEL:
+	    	return put_user(pmu_kind, argp);
+	case PMU_IOC_HAS_ADB:
+		return put_user(pmu_has_adb, argp);
+	}
+	return -EINVAL;
+}
+
+static struct file_operations pmu_device_fops __pmacdata = {
+	.read		= pmu_read,
+	.write		= pmu_write,
+	.poll		= pmu_fpoll,
+	.ioctl		= pmu_ioctl,
+	.open		= pmu_open,
+	.release	= pmu_release,
+};
+
+static struct miscdevice pmu_device __pmacdata = {
+	PMU_MINOR, "pmu", &pmu_device_fops
+};
+
+void pmu_device_init(void)
+{
+	if (!via)
+		return;
+	if (misc_register(&pmu_device) < 0)
+		printk(KERN_ERR "via-pmu: cannot register misc device.\n");
+}
+#endif /* CONFIG_PMAC_PBOOK */
+
+#ifdef DEBUG_SLEEP
+static inline void  __pmac
+polled_handshake(volatile unsigned char *via)
+{
+	via[B] &= ~TREQ; eieio();
+	while ((via[B] & TACK) != 0)
+		;
+	via[B] |= TREQ; eieio();
+	while ((via[B] & TACK) == 0)
+		;
+}
+
+static inline void  __pmac
+polled_send_byte(volatile unsigned char *via, int x)
+{
+	via[ACR] |= SR_OUT | SR_EXT; eieio();
+	via[SR] = x; eieio();
+	polled_handshake(via);
+}
+
+static inline int __pmac
+polled_recv_byte(volatile unsigned char *via)
+{
+	int x;
+
+	via[ACR] = (via[ACR] & ~SR_OUT) | SR_EXT; eieio();
+	x = via[SR]; eieio();
+	polled_handshake(via);
+	x = via[SR]; eieio();
+	return x;
+}
+
+int __pmac
+pmu_polled_request(struct adb_request *req)
+{
+	unsigned long flags;
+	int i, l, c;
+	volatile unsigned char *v = via;
+
+	req->complete = 1;
+	c = req->data[0];
+	l = pmu_data_len[c][0];
+	if (l >= 0 && req->nbytes != l + 1)
+		return -EINVAL;
+
+	local_irq_save(flags);
+	while (pmu_state != idle)
+		pmu_poll();
+
+	while ((via[B] & TACK) == 0)
+		;
+	polled_send_byte(v, c);
+	if (l < 0) {
+		l = req->nbytes - 1;
+		polled_send_byte(v, l);
+	}
+	for (i = 1; i <= l; ++i)
+		polled_send_byte(v, req->data[i]);
+
+	l = pmu_data_len[c][1];
+	if (l < 0)
+		l = polled_recv_byte(v);
+	for (i = 0; i < l; ++i)
+		req->reply[i + req->reply_len] = polled_recv_byte(v);
+
+	if (req->done)
+		(*req->done)(req);
+
+	local_irq_restore(flags);
+	return 0;
+}
+#endif /* DEBUG_SLEEP */
+
+EXPORT_SYMBOL(pmu_request);
+EXPORT_SYMBOL(pmu_poll);
+EXPORT_SYMBOL(pmu_poll_adb);
+EXPORT_SYMBOL(pmu_wait_complete);
+EXPORT_SYMBOL(pmu_suspend);
+EXPORT_SYMBOL(pmu_resume);
+EXPORT_SYMBOL(pmu_unlock);
+EXPORT_SYMBOL(pmu_i2c_combined_read);
+EXPORT_SYMBOL(pmu_i2c_stdsub_write);
+EXPORT_SYMBOL(pmu_i2c_simple_read);
+EXPORT_SYMBOL(pmu_i2c_simple_write);
+#ifdef CONFIG_PMAC_PBOOK
+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 -ubw -Naur linux-2.6.8.orig/drivers/video/aty/radeon_pm.c linux-2.6.8/drivers/video/aty/radeon_pm.c
--- linux-2.6.8.orig/drivers/video/aty/radeon_pm.c	2004-08-14 07:37:26.000000000 +0200
+++ linux-2.6.8/drivers/video/aty/radeon_pm.c	2004-08-14 18:23:39.995577712 +0200
@@ -859,6 +859,8 @@
 	 * know we'll be rebooted, ...
 	 */
 
+	if (state != PM_SUSPEND_MEM)
+		return 0;
 	printk(KERN_DEBUG "radeonfb: suspending to state: %d...\n", state);
 	
 	acquire_console_sem();
diff -ubw -Naur linux-2.6.8.orig/drivers/video/aty/radeon_pm.c.orig linux-2.6.8/drivers/video/aty/radeon_pm.c.orig
--- linux-2.6.8.orig/drivers/video/aty/radeon_pm.c.orig	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.8/drivers/video/aty/radeon_pm.c.orig	2004-08-14 07:37:26.000000000 +0200
@@ -0,0 +1,943 @@
+#include "radeonfb.h"
+
+#include <linux/console.h>
+#include <linux/agp_backend.h>
+
+/*
+ * Currently, only PowerMac do D2 state
+ */
+#define CONFIG_RADEON_HAS_D2	CONFIG_PPC_PMAC
+
+#ifdef CONFIG_RADEON_HAS_D2
+/*
+ * On PowerMac, we assume any mobility chip based machine does D2
+ */
+#ifdef CONFIG_PPC_PMAC
+static inline int radeon_suspend_to_d2(struct radeonfb_info *rinfo, u32 state)
+{
+	return rinfo->is_mobility;
+}
+#else
+static inline int radeon_suspend_to_d2(struct radeonfb_info *rinfo, u32 state)
+{
+	return 0;
+}
+#endif
+
+#endif /* CONFIG_RADEON_HAS_D2 */
+
+/*
+ * Radeon M6, M7 and M9 Power Management code. This code currently
+ * only supports the mobile chips in D2 mode, that is typically what
+ * is used on Apple laptops, it's based from some informations provided
+ * by ATI along with hours of tracing of MacOS drivers.
+ * 
+ * New version of this code almost totally rewritten by ATI, many thanks
+ * for their support.
+ */
+
+void radeon_pm_disable_dynamic_mode(struct radeonfb_info *rinfo)
+{
+
+	u32 sclk_cntl;
+	u32 mclk_cntl;
+	u32 sclk_more_cntl;
+	
+	u32 vclk_ecp_cntl;
+	u32 pixclks_cntl;
+
+	/* Mobility chips only, untested on M9+/M10/11 */
+	if (!rinfo->is_mobility)
+		return;
+	if (rinfo->family > CHIP_FAMILY_RV250)
+		return;
+	
+	/* Force Core Clocks */
+	sclk_cntl = INPLL( pllSCLK_CNTL_M6);
+	sclk_cntl |= 	SCLK_CNTL_M6__FORCE_CP|
+			SCLK_CNTL_M6__FORCE_HDP|
+			SCLK_CNTL_M6__FORCE_DISP1|
+			SCLK_CNTL_M6__FORCE_DISP2|
+			SCLK_CNTL_M6__FORCE_TOP|
+			SCLK_CNTL_M6__FORCE_E2|
+			SCLK_CNTL_M6__FORCE_SE|
+			SCLK_CNTL_M6__FORCE_IDCT|
+			SCLK_CNTL_M6__FORCE_VIP|
+			SCLK_CNTL_M6__FORCE_RE|
+			SCLK_CNTL_M6__FORCE_PB|
+			SCLK_CNTL_M6__FORCE_TAM|
+			SCLK_CNTL_M6__FORCE_TDM|
+			SCLK_CNTL_M6__FORCE_RB|
+			SCLK_CNTL_M6__FORCE_TV_SCLK|
+			SCLK_CNTL_M6__FORCE_SUBPIC|
+			SCLK_CNTL_M6__FORCE_OV0;
+    	OUTPLL( pllSCLK_CNTL_M6, sclk_cntl);
+	
+	
+	
+	sclk_more_cntl = INPLL(pllSCLK_MORE_CNTL);
+	sclk_more_cntl |= 	SCLK_MORE_CNTL__FORCE_DISPREGS|
+				SCLK_MORE_CNTL__FORCE_MC_GUI|
+				SCLK_MORE_CNTL__FORCE_MC_HOST;	
+	OUTPLL(pllSCLK_MORE_CNTL, sclk_more_cntl);
+	
+	/* Force Display clocks	*/
+	vclk_ecp_cntl = INPLL( pllVCLK_ECP_CNTL);
+	vclk_ecp_cntl &= ~(	VCLK_ECP_CNTL__PIXCLK_ALWAYS_ONb |
+			 	VCLK_ECP_CNTL__PIXCLK_DAC_ALWAYS_ONb);
+
+	OUTPLL( pllVCLK_ECP_CNTL, vclk_ecp_cntl);
+	
+	pixclks_cntl = INPLL( pllPIXCLKS_CNTL);
+	pixclks_cntl &= ~(	PIXCLKS_CNTL__PIXCLK_GV_ALWAYS_ONb |
+			 	PIXCLKS_CNTL__PIXCLK_BLEND_ALWAYS_ONb|
+				PIXCLKS_CNTL__PIXCLK_DIG_TMDS_ALWAYS_ONb |
+				PIXCLKS_CNTL__PIXCLK_LVDS_ALWAYS_ONb|
+				PIXCLKS_CNTL__PIXCLK_TMDS_ALWAYS_ONb|
+				PIXCLKS_CNTL__PIX2CLK_ALWAYS_ONb|
+				PIXCLKS_CNTL__PIX2CLK_DAC_ALWAYS_ONb);
+						
+ 	OUTPLL( pllPIXCLKS_CNTL, pixclks_cntl);
+
+	/* Force Memory Clocks */
+	mclk_cntl = INPLL( pllMCLK_CNTL_M6);
+	mclk_cntl &= ~(	MCLK_CNTL_M6__FORCE_MCLKA |  
+			MCLK_CNTL_M6__FORCE_MCLKB |
+			MCLK_CNTL_M6__FORCE_YCLKA |
+			MCLK_CNTL_M6__FORCE_YCLKB );
+    	OUTPLL( pllMCLK_CNTL_M6, mclk_cntl);
+}
+
+void radeon_pm_enable_dynamic_mode(struct radeonfb_info *rinfo)
+{
+	u32 clk_pwrmgt_cntl;
+	u32 sclk_cntl;
+	u32 sclk_more_cntl;
+	u32 clk_pin_cntl;
+	u32 pixclks_cntl;
+	u32 vclk_ecp_cntl;
+	u32 mclk_cntl;
+	u32 mclk_misc;
+
+	/* Mobility chips only, untested on M9+/M10/11 */
+	if (!rinfo->is_mobility)
+		return;
+	if (rinfo->family > CHIP_FAMILY_RV250)
+		return;
+	
+	/* Set Latencies */
+	clk_pwrmgt_cntl = INPLL( pllCLK_PWRMGT_CNTL_M6);
+	
+	clk_pwrmgt_cntl &= ~(	 CLK_PWRMGT_CNTL_M6__ENGINE_DYNCLK_MODE_MASK|
+				 CLK_PWRMGT_CNTL_M6__ACTIVE_HILO_LAT_MASK|
+				 CLK_PWRMGT_CNTL_M6__DISP_DYN_STOP_LAT_MASK|
+				 CLK_PWRMGT_CNTL_M6__DYN_STOP_MODE_MASK);
+	/* Mode 1 */
+	clk_pwrmgt_cntl = 	CLK_PWRMGT_CNTL_M6__MC_CH_MODE|
+				CLK_PWRMGT_CNTL_M6__ENGINE_DYNCLK_MODE | 
+				(1<<CLK_PWRMGT_CNTL_M6__ACTIVE_HILO_LAT__SHIFT) |
+				(0<<CLK_PWRMGT_CNTL_M6__DISP_DYN_STOP_LAT__SHIFT)|
+				(0<<CLK_PWRMGT_CNTL_M6__DYN_STOP_MODE__SHIFT);
+
+	OUTPLL( pllCLK_PWRMGT_CNTL_M6, clk_pwrmgt_cntl);
+						
+
+	clk_pin_cntl = INPLL( pllCLK_PIN_CNTL);
+	clk_pin_cntl |= CLK_PIN_CNTL__SCLK_DYN_START_CNTL;
+	 
+	OUTPLL( pllCLK_PIN_CNTL, clk_pin_cntl);
+
+	/* Enable Dyanmic mode for SCLK */
+
+	sclk_cntl = INPLL( pllSCLK_CNTL_M6);	
+	sclk_cntl &= SCLK_CNTL_M6__SCLK_SRC_SEL_MASK;
+	sclk_cntl |= SCLK_CNTL_M6__FORCE_VIP;		
+
+	OUTPLL( pllSCLK_CNTL_M6, sclk_cntl);
+
+
+	sclk_more_cntl = INPLL(pllSCLK_MORE_CNTL);
+	sclk_more_cntl &= ~(SCLK_MORE_CNTL__FORCE_DISPREGS);
+				                    
+	OUTPLL(pllSCLK_MORE_CNTL, sclk_more_cntl);
+
+	
+	/* Enable Dynamic mode for PIXCLK & PIX2CLK */
+
+	pixclks_cntl = INPLL( pllPIXCLKS_CNTL);
+	
+	pixclks_cntl|=  PIXCLKS_CNTL__PIX2CLK_ALWAYS_ONb | 
+			PIXCLKS_CNTL__PIX2CLK_DAC_ALWAYS_ONb|
+			PIXCLKS_CNTL__PIXCLK_BLEND_ALWAYS_ONb|
+			PIXCLKS_CNTL__PIXCLK_GV_ALWAYS_ONb|
+			PIXCLKS_CNTL__PIXCLK_DIG_TMDS_ALWAYS_ONb|
+			PIXCLKS_CNTL__PIXCLK_LVDS_ALWAYS_ONb|
+			PIXCLKS_CNTL__PIXCLK_TMDS_ALWAYS_ONb;
+
+	OUTPLL( pllPIXCLKS_CNTL, pixclks_cntl);
+		
+		
+	vclk_ecp_cntl = INPLL( pllVCLK_ECP_CNTL);
+	
+	vclk_ecp_cntl|=  VCLK_ECP_CNTL__PIXCLK_ALWAYS_ONb | 
+			 VCLK_ECP_CNTL__PIXCLK_DAC_ALWAYS_ONb;
+
+	OUTPLL( pllVCLK_ECP_CNTL, vclk_ecp_cntl);
+
+
+	/* Enable Dynamic mode for MCLK	*/
+
+	mclk_cntl  = INPLL( pllMCLK_CNTL_M6);
+	mclk_cntl |= 	MCLK_CNTL_M6__FORCE_MCLKA|  
+			MCLK_CNTL_M6__FORCE_MCLKB|	
+			MCLK_CNTL_M6__FORCE_YCLKA|
+			MCLK_CNTL_M6__FORCE_YCLKB;
+			
+    	OUTPLL( pllMCLK_CNTL_M6, mclk_cntl);
+
+	mclk_misc = INPLL(pllMCLK_MISC);
+	mclk_misc |= 	MCLK_MISC__MC_MCLK_MAX_DYN_STOP_LAT|
+			MCLK_MISC__IO_MCLK_MAX_DYN_STOP_LAT|
+			MCLK_MISC__MC_MCLK_DYN_ENABLE|
+			MCLK_MISC__IO_MCLK_DYN_ENABLE;	
+	
+	OUTPLL(pllMCLK_MISC, mclk_misc);
+}
+
+#ifdef CONFIG_PM
+
+static void OUTMC( struct radeonfb_info *rinfo, u8 indx, u32 value)
+{
+	OUTREG( MC_IND_INDEX, indx | MC_IND_INDEX__MC_IND_WR_EN);	
+	OUTREG( MC_IND_DATA, value);		
+}
+
+static u32 INMC(struct radeonfb_info *rinfo, u8 indx)
+{
+	OUTREG( MC_IND_INDEX, indx);					
+	return INREG( MC_IND_DATA);
+}
+
+static void radeon_pm_save_regs(struct radeonfb_info *rinfo)
+{
+	rinfo->save_regs[0] = INPLL(PLL_PWRMGT_CNTL);
+	rinfo->save_regs[1] = INPLL(CLK_PWRMGT_CNTL);
+	rinfo->save_regs[2] = INPLL(MCLK_CNTL);
+	rinfo->save_regs[3] = INPLL(SCLK_CNTL);
+	rinfo->save_regs[4] = INPLL(CLK_PIN_CNTL);
+	rinfo->save_regs[5] = INPLL(VCLK_ECP_CNTL);
+	rinfo->save_regs[6] = INPLL(PIXCLKS_CNTL);
+	rinfo->save_regs[7] = INPLL(MCLK_MISC);
+	rinfo->save_regs[8] = INPLL(P2PLL_CNTL);
+	
+	rinfo->save_regs[9] = INREG(DISP_MISC_CNTL);
+	rinfo->save_regs[10] = INREG(DISP_PWR_MAN);
+	rinfo->save_regs[11] = INREG(LVDS_GEN_CNTL);
+	rinfo->save_regs[12] = INREG(LVDS_PLL_CNTL);
+	rinfo->save_regs[13] = INREG(TV_DAC_CNTL);
+	rinfo->save_regs[14] = INREG(BUS_CNTL1);
+	rinfo->save_regs[15] = INREG(CRTC_OFFSET_CNTL);
+	rinfo->save_regs[16] = INREG(AGP_CNTL);
+	rinfo->save_regs[17] = (INREG(CRTC_GEN_CNTL) & 0xfdffffff) | 0x04000000;
+	rinfo->save_regs[18] = (INREG(CRTC2_GEN_CNTL) & 0xfdffffff) | 0x04000000;
+	rinfo->save_regs[19] = INREG(GPIOPAD_A);
+	rinfo->save_regs[20] = INREG(GPIOPAD_EN);
+	rinfo->save_regs[21] = INREG(GPIOPAD_MASK);
+	rinfo->save_regs[22] = INREG(ZV_LCDPAD_A);
+	rinfo->save_regs[23] = INREG(ZV_LCDPAD_EN);
+	rinfo->save_regs[24] = INREG(ZV_LCDPAD_MASK);
+	rinfo->save_regs[25] = INREG(GPIO_VGA_DDC);
+	rinfo->save_regs[26] = INREG(GPIO_DVI_DDC);
+	rinfo->save_regs[27] = INREG(GPIO_MONID);
+	rinfo->save_regs[28] = INREG(GPIO_CRT2_DDC);
+
+	rinfo->save_regs[29] = INREG(SURFACE_CNTL);
+	rinfo->save_regs[30] = INREG(MC_FB_LOCATION);
+	rinfo->save_regs[31] = INREG(DISPLAY_BASE_ADDR);
+	rinfo->save_regs[32] = INREG(MC_AGP_LOCATION);
+	rinfo->save_regs[33] = INREG(CRTC2_DISPLAY_BASE_ADDR);
+}
+
+static void radeon_pm_restore_regs(struct radeonfb_info *rinfo)
+{
+	OUTPLL(P2PLL_CNTL, rinfo->save_regs[8] & 0xFFFFFFFE); /* First */
+	
+	OUTPLL(PLL_PWRMGT_CNTL, rinfo->save_regs[0]);
+	OUTPLL(CLK_PWRMGT_CNTL, rinfo->save_regs[1]);
+	OUTPLL(MCLK_CNTL, rinfo->save_regs[2]);
+	OUTPLL(SCLK_CNTL, rinfo->save_regs[3]);
+	OUTPLL(CLK_PIN_CNTL, rinfo->save_regs[4]);
+	OUTPLL(VCLK_ECP_CNTL, rinfo->save_regs[5]);
+	OUTPLL(PIXCLKS_CNTL, rinfo->save_regs[6]);
+	OUTPLL(MCLK_MISC, rinfo->save_regs[7]);
+	
+	OUTREG(SURFACE_CNTL, rinfo->save_regs[29]);
+	OUTREG(MC_FB_LOCATION, rinfo->save_regs[30]);
+	OUTREG(DISPLAY_BASE_ADDR, rinfo->save_regs[31]);
+	OUTREG(MC_AGP_LOCATION, rinfo->save_regs[32]);
+	OUTREG(CRTC2_DISPLAY_BASE_ADDR, rinfo->save_regs[33]);
+
+	OUTREG(DISP_MISC_CNTL, rinfo->save_regs[9]);
+	OUTREG(DISP_PWR_MAN, rinfo->save_regs[10]);
+	OUTREG(LVDS_GEN_CNTL, rinfo->save_regs[11]);
+	OUTREG(LVDS_PLL_CNTL,rinfo->save_regs[12]);
+	OUTREG(TV_DAC_CNTL, rinfo->save_regs[13]);
+	OUTREG(BUS_CNTL1, rinfo->save_regs[14]);
+	OUTREG(CRTC_OFFSET_CNTL, rinfo->save_regs[15]);
+	OUTREG(AGP_CNTL, rinfo->save_regs[16]);
+	OUTREG(CRTC_GEN_CNTL, rinfo->save_regs[17]);
+	OUTREG(CRTC2_GEN_CNTL, rinfo->save_regs[18]);
+
+	// wait VBL before that one  ?
+	OUTPLL(P2PLL_CNTL, rinfo->save_regs[8]);
+	
+	OUTREG(GPIOPAD_A, rinfo->save_regs[19]);
+	OUTREG(GPIOPAD_EN, rinfo->save_regs[20]);
+	OUTREG(GPIOPAD_MASK, rinfo->save_regs[21]);
+	OUTREG(ZV_LCDPAD_A, rinfo->save_regs[22]);
+	OUTREG(ZV_LCDPAD_EN, rinfo->save_regs[23]);
+	OUTREG(ZV_LCDPAD_MASK, rinfo->save_regs[24]);
+	OUTREG(GPIO_VGA_DDC, rinfo->save_regs[25]);
+	OUTREG(GPIO_DVI_DDC, rinfo->save_regs[26]);
+	OUTREG(GPIO_MONID, rinfo->save_regs[27]);
+	OUTREG(GPIO_CRT2_DDC, rinfo->save_regs[28]);
+}
+
+static void radeon_pm_disable_iopad(struct radeonfb_info *rinfo)
+{		
+	OUTREG(GPIOPAD_MASK, 0x0001ffff);
+	OUTREG(GPIOPAD_EN, 0x00000400);
+	OUTREG(GPIOPAD_A, 0x00000000);		
+        OUTREG(ZV_LCDPAD_MASK, 0x00000000);
+        OUTREG(ZV_LCDPAD_EN, 0x00000000);
+      	OUTREG(ZV_LCDPAD_A, 0x00000000); 	
+	OUTREG(GPIO_VGA_DDC, 0x00030000);
+	OUTREG(GPIO_DVI_DDC, 0x00000000);
+	OUTREG(GPIO_MONID, 0x00030000);
+	OUTREG(GPIO_CRT2_DDC, 0x00000000);
+}
+
+static void radeon_pm_program_v2clk(struct radeonfb_info *rinfo)
+{
+	/* we use __INPLL and _OUTPLL and do the locking ourselves... */
+	unsigned long flags;
+	spin_lock_irqsave(&rinfo->reg_lock, flags);
+	/* Set v2clk to 65MHz */
+  	__OUTPLL(pllPIXCLKS_CNTL,
+  		__INPLL(rinfo, pllPIXCLKS_CNTL) & ~PIXCLKS_CNTL__PIX2CLK_SRC_SEL_MASK);
+	 
+  	__OUTPLL(pllP2PLL_REF_DIV, 0x0000000c);
+	__OUTPLL(pllP2PLL_CNTL, 0x0000bf00);
+	__OUTPLL(pllP2PLL_DIV_0, 0x00020074 | P2PLL_DIV_0__P2PLL_ATOMIC_UPDATE_W);
+	
+	__OUTPLL(pllP2PLL_CNTL,
+		__INPLL(rinfo, pllP2PLL_CNTL) & ~P2PLL_CNTL__P2PLL_SLEEP);
+	mdelay(1);
+
+	__OUTPLL(pllP2PLL_CNTL,
+		__INPLL(rinfo, pllP2PLL_CNTL) & ~P2PLL_CNTL__P2PLL_RESET);
+	mdelay( 1);
+
+  	__OUTPLL(pllPIXCLKS_CNTL,
+  		(__INPLL(rinfo, pllPIXCLKS_CNTL) & ~PIXCLKS_CNTL__PIX2CLK_SRC_SEL_MASK)
+  		| (0x03 << PIXCLKS_CNTL__PIX2CLK_SRC_SEL__SHIFT));
+	mdelay( 1);	
+	spin_unlock_irqrestore(&rinfo->reg_lock, flags);
+}
+
+static void radeon_pm_low_current(struct radeonfb_info *rinfo)
+{
+	u32 reg;
+
+	reg  = INREG(BUS_CNTL1);
+	reg &= ~BUS_CNTL1_MOBILE_PLATFORM_SEL_MASK;
+	reg |= BUS_CNTL1_AGPCLK_VALID | (1<<BUS_CNTL1_MOBILE_PLATFORM_SEL_SHIFT);
+	OUTREG(BUS_CNTL1, reg);
+	
+	reg  = INPLL(PLL_PWRMGT_CNTL);
+	reg |= PLL_PWRMGT_CNTL_SPLL_TURNOFF | PLL_PWRMGT_CNTL_PPLL_TURNOFF |
+		PLL_PWRMGT_CNTL_P2PLL_TURNOFF | PLL_PWRMGT_CNTL_TVPLL_TURNOFF;
+	reg &= ~PLL_PWRMGT_CNTL_SU_MCLK_USE_BCLK;
+	reg &= ~PLL_PWRMGT_CNTL_MOBILE_SU;
+	OUTPLL(PLL_PWRMGT_CNTL, reg);
+	
+	reg  = INREG(TV_DAC_CNTL);
+	reg &= ~(TV_DAC_CNTL_BGADJ_MASK |TV_DAC_CNTL_DACADJ_MASK);
+	reg |=TV_DAC_CNTL_BGSLEEP | TV_DAC_CNTL_RDACPD | TV_DAC_CNTL_GDACPD |
+		TV_DAC_CNTL_BDACPD |
+		(8<<TV_DAC_CNTL_BGADJ__SHIFT) | (8<<TV_DAC_CNTL_DACADJ__SHIFT);
+	OUTREG(TV_DAC_CNTL, reg);
+	
+	reg  = INREG(TMDS_TRANSMITTER_CNTL);
+	reg &= ~(TMDS_PLL_EN | TMDS_PLLRST);
+	OUTREG(TMDS_TRANSMITTER_CNTL, reg);
+
+	reg = INREG(DAC_CNTL);
+	reg &= ~DAC_CMP_EN;
+	OUTREG(DAC_CNTL, reg);
+
+	reg = INREG(DAC_CNTL2);
+	reg &= ~DAC2_CMP_EN;
+	OUTREG(DAC_CNTL2, reg);
+	
+	reg  = INREG(TV_DAC_CNTL);
+	reg &= ~TV_DAC_CNTL_DETECT;
+	OUTREG(TV_DAC_CNTL, reg);
+}
+
+static void radeon_pm_setup_for_suspend(struct radeonfb_info *rinfo)
+{
+
+	u32 sclk_cntl, mclk_cntl, sclk_more_cntl;
+
+	u32 pll_pwrmgt_cntl;
+	u32 clk_pwrmgt_cntl;
+	u32 clk_pin_cntl;
+	u32 vclk_ecp_cntl; 
+	u32 pixclks_cntl;
+	u32 disp_mis_cntl;
+	u32 disp_pwr_man;
+	u32 tmp;
+	
+	/* Force Core Clocks */
+	sclk_cntl = INPLL( pllSCLK_CNTL_M6);
+	sclk_cntl |= 	SCLK_CNTL_M6__IDCT_MAX_DYN_STOP_LAT|
+			SCLK_CNTL_M6__VIP_MAX_DYN_STOP_LAT|
+			SCLK_CNTL_M6__RE_MAX_DYN_STOP_LAT|
+			SCLK_CNTL_M6__PB_MAX_DYN_STOP_LAT|
+			SCLK_CNTL_M6__TAM_MAX_DYN_STOP_LAT|
+			SCLK_CNTL_M6__TDM_MAX_DYN_STOP_LAT|
+			SCLK_CNTL_M6__RB_MAX_DYN_STOP_LAT|
+			
+			SCLK_CNTL_M6__FORCE_DISP2|
+			SCLK_CNTL_M6__FORCE_CP|
+			SCLK_CNTL_M6__FORCE_HDP|
+			SCLK_CNTL_M6__FORCE_DISP1|
+			SCLK_CNTL_M6__FORCE_TOP|
+			SCLK_CNTL_M6__FORCE_E2|
+			SCLK_CNTL_M6__FORCE_SE|
+			SCLK_CNTL_M6__FORCE_IDCT|
+			SCLK_CNTL_M6__FORCE_VIP|
+			
+			SCLK_CNTL_M6__FORCE_RE|
+			SCLK_CNTL_M6__FORCE_PB|
+			SCLK_CNTL_M6__FORCE_TAM|
+			SCLK_CNTL_M6__FORCE_TDM|
+			SCLK_CNTL_M6__FORCE_RB|
+			SCLK_CNTL_M6__FORCE_TV_SCLK|
+			SCLK_CNTL_M6__FORCE_SUBPIC|
+			SCLK_CNTL_M6__FORCE_OV0;
+
+	OUTPLL( pllSCLK_CNTL_M6, sclk_cntl);
+
+	sclk_more_cntl = INPLL(pllSCLK_MORE_CNTL);
+	sclk_more_cntl |= 	SCLK_MORE_CNTL__FORCE_DISPREGS |
+				SCLK_MORE_CNTL__FORCE_MC_GUI |
+				SCLK_MORE_CNTL__FORCE_MC_HOST;
+
+	OUTPLL(pllSCLK_MORE_CNTL, sclk_more_cntl);		
+
+	
+	mclk_cntl = INPLL( pllMCLK_CNTL_M6);
+	mclk_cntl &= ~(	MCLK_CNTL_M6__FORCE_MCLKA |  
+			MCLK_CNTL_M6__FORCE_MCLKB |
+			MCLK_CNTL_M6__FORCE_YCLKA | 
+			MCLK_CNTL_M6__FORCE_YCLKB | 
+			MCLK_CNTL_M6__FORCE_MC
+		      );	
+    	OUTPLL( pllMCLK_CNTL_M6, mclk_cntl);
+	
+	/* Force Display clocks	*/
+	vclk_ecp_cntl = INPLL( pllVCLK_ECP_CNTL);
+	vclk_ecp_cntl &= ~(VCLK_ECP_CNTL__PIXCLK_ALWAYS_ONb |VCLK_ECP_CNTL__PIXCLK_DAC_ALWAYS_ONb);
+	vclk_ecp_cntl |= VCLK_ECP_CNTL__ECP_FORCE_ON;
+	OUTPLL( pllVCLK_ECP_CNTL, vclk_ecp_cntl);
+	
+	
+	pixclks_cntl = INPLL( pllPIXCLKS_CNTL);
+	pixclks_cntl &= ~(	PIXCLKS_CNTL__PIXCLK_GV_ALWAYS_ONb | 
+				PIXCLKS_CNTL__PIXCLK_BLEND_ALWAYS_ONb|
+				PIXCLKS_CNTL__PIXCLK_DIG_TMDS_ALWAYS_ONb |
+				PIXCLKS_CNTL__PIXCLK_LVDS_ALWAYS_ONb|
+				PIXCLKS_CNTL__PIXCLK_TMDS_ALWAYS_ONb|
+				PIXCLKS_CNTL__PIX2CLK_ALWAYS_ONb|
+				PIXCLKS_CNTL__PIX2CLK_DAC_ALWAYS_ONb);
+						
+ 	OUTPLL( pllPIXCLKS_CNTL, pixclks_cntl);
+
+
+
+	/* Enable System power management */
+	pll_pwrmgt_cntl = INPLL( pllPLL_PWRMGT_CNTL);
+	
+	pll_pwrmgt_cntl |= 	PLL_PWRMGT_CNTL__SPLL_TURNOFF |
+				PLL_PWRMGT_CNTL__MPLL_TURNOFF|
+				PLL_PWRMGT_CNTL__PPLL_TURNOFF|
+				PLL_PWRMGT_CNTL__P2PLL_TURNOFF|
+				PLL_PWRMGT_CNTL__TVPLL_TURNOFF;
+						
+	OUTPLL( pllPLL_PWRMGT_CNTL, pll_pwrmgt_cntl);
+	
+	clk_pwrmgt_cntl	 = INPLL( pllCLK_PWRMGT_CNTL_M6);
+	
+	clk_pwrmgt_cntl &= ~(	CLK_PWRMGT_CNTL_M6__MPLL_PWRMGT_OFF|
+				CLK_PWRMGT_CNTL_M6__SPLL_PWRMGT_OFF|
+				CLK_PWRMGT_CNTL_M6__PPLL_PWRMGT_OFF|
+				CLK_PWRMGT_CNTL_M6__P2PLL_PWRMGT_OFF|
+				CLK_PWRMGT_CNTL_M6__MCLK_TURNOFF|
+				CLK_PWRMGT_CNTL_M6__SCLK_TURNOFF|
+				CLK_PWRMGT_CNTL_M6__PCLK_TURNOFF|
+				CLK_PWRMGT_CNTL_M6__P2CLK_TURNOFF|
+				CLK_PWRMGT_CNTL_M6__TVPLL_PWRMGT_OFF|
+				CLK_PWRMGT_CNTL_M6__GLOBAL_PMAN_EN|
+				CLK_PWRMGT_CNTL_M6__ENGINE_DYNCLK_MODE|
+				CLK_PWRMGT_CNTL_M6__ACTIVE_HILO_LAT_MASK|
+				CLK_PWRMGT_CNTL_M6__CG_NO1_DEBUG_MASK			
+			);
+						
+	clk_pwrmgt_cntl |= CLK_PWRMGT_CNTL_M6__GLOBAL_PMAN_EN | CLK_PWRMGT_CNTL_M6__DISP_PM;
+	
+	OUTPLL( pllCLK_PWRMGT_CNTL_M6, clk_pwrmgt_cntl);	
+	
+	clk_pin_cntl = INPLL( pllCLK_PIN_CNTL);
+	
+	clk_pin_cntl &= ~CLK_PIN_CNTL__ACCESS_REGS_IN_SUSPEND;
+
+	/* because both INPLL and OUTPLL take the same lock, that's why. */
+	tmp = INPLL( pllMCLK_MISC) | MCLK_MISC__EN_MCLK_TRISTATE_IN_SUSPEND;
+	OUTPLL( pllMCLK_MISC, tmp);
+	
+	/* AGP PLL control */
+	OUTREG(BUS_CNTL1, INREG(BUS_CNTL1) |  BUS_CNTL1__AGPCLK_VALID);
+
+	OUTREG(BUS_CNTL1,
+		(INREG(BUS_CNTL1) & ~BUS_CNTL1__MOBILE_PLATFORM_SEL_MASK)
+		| (2<<BUS_CNTL1__MOBILE_PLATFORM_SEL__SHIFT));	// 440BX
+	OUTREG(CRTC_OFFSET_CNTL, (INREG(CRTC_OFFSET_CNTL) & ~CRTC_OFFSET_CNTL__CRTC_STEREO_SYNC_OUT_EN));
+	
+	clk_pin_cntl &= ~CLK_PIN_CNTL__CG_CLK_TO_OUTPIN;
+	clk_pin_cntl |= CLK_PIN_CNTL__XTALIN_ALWAYS_ONb;	
+	OUTPLL( pllCLK_PIN_CNTL, clk_pin_cntl);
+
+	/* Solano2M */
+	OUTREG(AGP_CNTL,
+		(INREG(AGP_CNTL) & ~(AGP_CNTL__MAX_IDLE_CLK_MASK))
+		| (0x20<<AGP_CNTL__MAX_IDLE_CLK__SHIFT));
+
+	/* ACPI mode */
+	/* because both INPLL and OUTPLL take the same lock, that's why. */
+	tmp = INPLL( pllPLL_PWRMGT_CNTL) & ~PLL_PWRMGT_CNTL__PM_MODE_SEL;
+	OUTPLL( pllPLL_PWRMGT_CNTL, tmp);
+
+
+	disp_mis_cntl = INREG(DISP_MISC_CNTL);
+	
+	disp_mis_cntl &= ~(	DISP_MISC_CNTL__SOFT_RESET_GRPH_PP | 
+				DISP_MISC_CNTL__SOFT_RESET_SUBPIC_PP | 
+				DISP_MISC_CNTL__SOFT_RESET_OV0_PP |
+				DISP_MISC_CNTL__SOFT_RESET_GRPH_SCLK|
+				DISP_MISC_CNTL__SOFT_RESET_SUBPIC_SCLK|
+				DISP_MISC_CNTL__SOFT_RESET_OV0_SCLK|
+				DISP_MISC_CNTL__SOFT_RESET_GRPH2_PP|
+				DISP_MISC_CNTL__SOFT_RESET_GRPH2_SCLK|
+				DISP_MISC_CNTL__SOFT_RESET_LVDS|
+				DISP_MISC_CNTL__SOFT_RESET_TMDS|
+				DISP_MISC_CNTL__SOFT_RESET_DIG_TMDS|
+				DISP_MISC_CNTL__SOFT_RESET_TV);
+	
+	OUTREG(DISP_MISC_CNTL, disp_mis_cntl);					
+						
+	disp_pwr_man = INREG(DISP_PWR_MAN);
+	
+	disp_pwr_man &= ~(	DISP_PWR_MAN__DISP_PWR_MAN_D3_CRTC_EN	| 
+						DISP_PWR_MAN__DISP2_PWR_MAN_D3_CRTC2_EN |
+						DISP_PWR_MAN__DISP_PWR_MAN_DPMS_MASK|		
+						DISP_PWR_MAN__DISP_D3_RST|
+						DISP_PWR_MAN__DISP_D3_REG_RST
+					);
+	
+	disp_pwr_man |= DISP_PWR_MAN__DISP_D3_GRPH_RST|
+					DISP_PWR_MAN__DISP_D3_SUBPIC_RST|
+					DISP_PWR_MAN__DISP_D3_OV0_RST|
+					DISP_PWR_MAN__DISP_D1D2_GRPH_RST|
+					DISP_PWR_MAN__DISP_D1D2_SUBPIC_RST|
+					DISP_PWR_MAN__DISP_D1D2_OV0_RST|
+					DISP_PWR_MAN__DIG_TMDS_ENABLE_RST|
+					DISP_PWR_MAN__TV_ENABLE_RST| 
+//					DISP_PWR_MAN__AUTO_PWRUP_EN|
+					0;
+	
+	OUTREG(DISP_PWR_MAN, disp_pwr_man);					
+							
+	clk_pwrmgt_cntl = INPLL( pllCLK_PWRMGT_CNTL_M6);
+	pll_pwrmgt_cntl = INPLL( pllPLL_PWRMGT_CNTL) ;
+	clk_pin_cntl 	= INPLL( pllCLK_PIN_CNTL);
+	disp_pwr_man	= INREG(DISP_PWR_MAN);
+		
+	
+	/* D2 */
+	clk_pwrmgt_cntl |= CLK_PWRMGT_CNTL_M6__DISP_PM;
+	pll_pwrmgt_cntl |= PLL_PWRMGT_CNTL__MOBILE_SU | PLL_PWRMGT_CNTL__SU_SCLK_USE_BCLK;
+	clk_pin_cntl	|= CLK_PIN_CNTL__XTALIN_ALWAYS_ONb;
+	disp_pwr_man 	&= ~(DISP_PWR_MAN__DISP_PWR_MAN_D3_CRTC_EN_MASK | DISP_PWR_MAN__DISP2_PWR_MAN_D3_CRTC2_EN_MASK);							
+						
+
+	OUTPLL( pllCLK_PWRMGT_CNTL_M6, clk_pwrmgt_cntl);
+	OUTPLL( pllPLL_PWRMGT_CNTL, pll_pwrmgt_cntl);
+	OUTPLL( pllCLK_PIN_CNTL, clk_pin_cntl);
+	OUTREG(DISP_PWR_MAN, disp_pwr_man);
+
+	/* disable display request & disable display */
+	OUTREG( CRTC_GEN_CNTL, (INREG( CRTC_GEN_CNTL) & ~CRTC_GEN_CNTL__CRTC_EN) | CRTC_GEN_CNTL__CRTC_DISP_REQ_EN_B);
+	OUTREG( CRTC2_GEN_CNTL, (INREG( CRTC2_GEN_CNTL) & ~CRTC2_GEN_CNTL__CRTC2_EN) | CRTC2_GEN_CNTL__CRTC2_DISP_REQ_EN_B);
+
+	mdelay(17);				   
+
+}
+
+static void radeon_pm_yclk_mclk_sync(struct radeonfb_info *rinfo)
+{
+	u32 mc_chp_io_cntl_a1, mc_chp_io_cntl_b1;
+
+	mc_chp_io_cntl_a1 = INMC( rinfo, ixMC_CHP_IO_CNTL_A1) & ~MC_CHP_IO_CNTL_A1__MEM_SYNC_ENA_MASK;
+	mc_chp_io_cntl_b1 = INMC( rinfo, ixMC_CHP_IO_CNTL_B1) & ~MC_CHP_IO_CNTL_B1__MEM_SYNC_ENB_MASK;
+
+	OUTMC( rinfo, ixMC_CHP_IO_CNTL_A1, mc_chp_io_cntl_a1 | (1<<MC_CHP_IO_CNTL_A1__MEM_SYNC_ENA__SHIFT));
+	OUTMC( rinfo, ixMC_CHP_IO_CNTL_B1, mc_chp_io_cntl_b1 | (1<<MC_CHP_IO_CNTL_B1__MEM_SYNC_ENB__SHIFT));
+
+	/* Wassup ? This doesn't seem to be defined, let's hope we are ok this way --BenH */
+#ifdef MCLK_YCLK_SYNC_ENABLE
+	mc_chp_io_cntl_a1 |= (2<<MC_CHP_IO_CNTL_A1__MEM_SYNC_ENA__SHIFT);
+	mc_chp_io_cntl_b1 |= (2<<MC_CHP_IO_CNTL_B1__MEM_SYNC_ENB__SHIFT);
+#endif
+
+	OUTMC( rinfo, ixMC_CHP_IO_CNTL_A1, mc_chp_io_cntl_a1);
+	OUTMC( rinfo, ixMC_CHP_IO_CNTL_B1, mc_chp_io_cntl_b1);
+
+	mdelay( 1);
+}
+
+static void radeon_pm_program_mode_reg(struct radeonfb_info *rinfo, u16 value, u8 delay_required)
+{  
+	u32 mem_sdram_mode;
+
+	mem_sdram_mode  = INREG( MEM_SDRAM_MODE_REG);
+
+	mem_sdram_mode &= ~MEM_SDRAM_MODE_REG__MEM_MODE_REG_MASK;
+	mem_sdram_mode |= (value<<MEM_SDRAM_MODE_REG__MEM_MODE_REG__SHIFT) | MEM_SDRAM_MODE_REG__MEM_CFG_TYPE;
+	OUTREG( MEM_SDRAM_MODE_REG, mem_sdram_mode);
+
+	mem_sdram_mode |=  MEM_SDRAM_MODE_REG__MEM_SDRAM_RESET;
+	OUTREG( MEM_SDRAM_MODE_REG, mem_sdram_mode);
+
+	mem_sdram_mode &= ~MEM_SDRAM_MODE_REG__MEM_SDRAM_RESET;
+	OUTREG( MEM_SDRAM_MODE_REG, mem_sdram_mode);
+
+	if (delay_required == 1)
+		while( (INREG( MC_STATUS) & (MC_STATUS__MEM_PWRUP_COMPL_A | MC_STATUS__MEM_PWRUP_COMPL_B) ) == 0 )
+			{ }; 	
+}
+
+
+static void radeon_pm_enable_dll(struct radeonfb_info *rinfo)
+{  
+#define DLL_RESET_DELAY 	5
+#define DLL_SLEEP_DELAY		1
+
+	u32 DLL_CKO_Value = INPLL(pllMDLL_CKO)   | MDLL_CKO__MCKOA_SLEEP |  MDLL_CKO__MCKOA_RESET;
+	u32 DLL_CKA_Value = INPLL(pllMDLL_RDCKA) | MDLL_RDCKA__MRDCKA0_SLEEP | MDLL_RDCKA__MRDCKA1_SLEEP | MDLL_RDCKA__MRDCKA0_RESET | MDLL_RDCKA__MRDCKA1_RESET;
+	u32 DLL_CKB_Value = INPLL(pllMDLL_RDCKB) | MDLL_RDCKB__MRDCKB0_SLEEP | MDLL_RDCKB__MRDCKB1_SLEEP | MDLL_RDCKB__MRDCKB0_RESET | MDLL_RDCKB__MRDCKB1_RESET;
+
+	/* Setting up the DLL range for write */
+	OUTPLL(pllMDLL_CKO,   	DLL_CKO_Value);
+	OUTPLL(pllMDLL_RDCKA,  	DLL_CKA_Value);
+	OUTPLL(pllMDLL_RDCKB,	DLL_CKB_Value);
+
+	mdelay( DLL_RESET_DELAY);
+
+	/* Channel A */
+
+	/* Power Up */
+	DLL_CKO_Value &= ~(MDLL_CKO__MCKOA_SLEEP );
+	OUTPLL(pllMDLL_CKO,   	DLL_CKO_Value);
+	mdelay( DLL_SLEEP_DELAY);  		
+   
+	DLL_CKO_Value &= ~(MDLL_CKO__MCKOA_RESET );
+	OUTPLL(pllMDLL_CKO,	DLL_CKO_Value);
+	mdelay( DLL_RESET_DELAY);  		
+
+	/* Power Up */
+	DLL_CKA_Value &= ~(MDLL_RDCKA__MRDCKA0_SLEEP );
+	OUTPLL(pllMDLL_RDCKA,  	DLL_CKA_Value);
+	mdelay( DLL_SLEEP_DELAY);  		
+
+	DLL_CKA_Value &= ~(MDLL_RDCKA__MRDCKA0_RESET );
+	OUTPLL(pllMDLL_RDCKA,	DLL_CKA_Value);
+	mdelay( DLL_RESET_DELAY);  		
+
+	/* Power Up */
+	DLL_CKA_Value &= ~(MDLL_RDCKA__MRDCKA1_SLEEP);
+	OUTPLL(pllMDLL_RDCKA,	DLL_CKA_Value);
+	mdelay( DLL_SLEEP_DELAY);  		
+
+	DLL_CKA_Value &= ~(MDLL_RDCKA__MRDCKA1_RESET);
+	OUTPLL(pllMDLL_RDCKA,	DLL_CKA_Value);
+	mdelay( DLL_RESET_DELAY);  		
+
+
+	/* Channel B */
+
+	/* Power Up */
+	DLL_CKO_Value &= ~(MDLL_CKO__MCKOB_SLEEP );
+	OUTPLL(pllMDLL_CKO,   	DLL_CKO_Value);
+	mdelay( DLL_SLEEP_DELAY);  		
+   
+	DLL_CKO_Value &= ~(MDLL_CKO__MCKOB_RESET );
+	OUTPLL(pllMDLL_CKO,   	DLL_CKO_Value);
+	mdelay( DLL_RESET_DELAY);  		
+
+	/* Power Up */
+	DLL_CKB_Value &= ~(MDLL_RDCKB__MRDCKB0_SLEEP);
+	OUTPLL(pllMDLL_RDCKB,   DLL_CKB_Value);
+	mdelay( DLL_SLEEP_DELAY);  		
+
+	DLL_CKB_Value &= ~(MDLL_RDCKB__MRDCKB0_RESET);
+	OUTPLL(pllMDLL_RDCKB,   DLL_CKB_Value);
+	mdelay( DLL_RESET_DELAY);  		
+
+	/* Power Up */
+	DLL_CKB_Value &= ~(MDLL_RDCKB__MRDCKB1_SLEEP);
+	OUTPLL(pllMDLL_RDCKB,   DLL_CKB_Value);
+	mdelay( DLL_SLEEP_DELAY);  		
+
+	DLL_CKB_Value &= ~(MDLL_RDCKB__MRDCKB1_RESET);
+	OUTPLL(pllMDLL_RDCKB,   DLL_CKB_Value);
+	mdelay( DLL_RESET_DELAY);  		
+
+#undef DLL_RESET_DELAY 
+#undef DLL_SLEEP_DELAY
+}
+
+static void radeon_pm_full_reset_sdram(struct radeonfb_info *rinfo)
+{
+	u32 crtcGenCntl, crtcGenCntl2, memRefreshCntl, crtc_more_cntl, fp_gen_cntl, fp2_gen_cntl;
+ 
+	crtcGenCntl  = INREG( CRTC_GEN_CNTL);
+	crtcGenCntl2 = INREG( CRTC2_GEN_CNTL);
+
+	memRefreshCntl 	= INREG( MEM_REFRESH_CNTL);
+	crtc_more_cntl 	= INREG( CRTC_MORE_CNTL);
+	fp_gen_cntl 	= INREG( FP_GEN_CNTL);
+	fp2_gen_cntl 	= INREG( FP2_GEN_CNTL);
+ 
+
+	OUTREG( CRTC_MORE_CNTL, 	0);
+	OUTREG( FP_GEN_CNTL, 	0);
+	OUTREG( FP2_GEN_CNTL, 	0);
+ 
+	OUTREG( CRTC_GEN_CNTL,  (crtcGenCntl | CRTC_GEN_CNTL__CRTC_DISP_REQ_EN_B) );
+	OUTREG( CRTC2_GEN_CNTL, (crtcGenCntl2 | CRTC2_GEN_CNTL__CRTC2_DISP_REQ_EN_B) );
+  
+	/* Disable refresh */
+	OUTREG( MEM_REFRESH_CNTL, memRefreshCntl | MEM_REFRESH_CNTL__MEM_REFRESH_DIS);
+ 
+	/* Reset memory */
+	OUTREG( MEM_SDRAM_MODE_REG,
+		INREG( MEM_SDRAM_MODE_REG) & ~MEM_SDRAM_MODE_REG__MC_INIT_COMPLETE); // Init  Not Complete
+
+	/* DLL */
+	radeon_pm_enable_dll(rinfo);
+
+	// MLCK /YCLK sync 
+	radeon_pm_yclk_mclk_sync(rinfo);
+
+       	/* M6, M7 and M9 so far ... */
+	if (rinfo->is_mobility && rinfo->family <= CHIP_FAMILY_RV250) {
+		radeon_pm_program_mode_reg(rinfo, 0x2000, 1);   
+		radeon_pm_program_mode_reg(rinfo, 0x2001, 1);   
+		radeon_pm_program_mode_reg(rinfo, 0x2002, 1);   
+		radeon_pm_program_mode_reg(rinfo, 0x0132, 1);   
+		radeon_pm_program_mode_reg(rinfo, 0x0032, 1); 
+	}	
+
+	OUTREG( MEM_SDRAM_MODE_REG,
+		INREG( MEM_SDRAM_MODE_REG) |  MEM_SDRAM_MODE_REG__MC_INIT_COMPLETE); // Init Complete
+
+	OUTREG( MEM_REFRESH_CNTL, 	memRefreshCntl);
+
+	OUTREG( CRTC_GEN_CNTL, 		crtcGenCntl);
+	OUTREG( CRTC2_GEN_CNTL, 	crtcGenCntl2);
+	OUTREG( FP_GEN_CNTL, 		fp_gen_cntl);
+	OUTREG( FP2_GEN_CNTL, 		fp2_gen_cntl);
+
+	OUTREG( CRTC_MORE_CNTL, 	crtc_more_cntl);
+
+	mdelay( 15);
+}
+
+static void radeon_set_suspend(struct radeonfb_info *rinfo, int suspend)
+{
+	u16 pwr_cmd;
+	u32 tmp;
+
+	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) {
+		printk(KERN_DEBUG "radeonfb: switching to D2 state...\n");
+
+		/* Disable dynamic power management of clocks for the
+		 * duration of the suspend/resume process
+		 */
+		radeon_pm_disable_dynamic_mode(rinfo);
+		/* Save some registers */
+		radeon_pm_save_regs(rinfo);
+
+		/* Prepare mobility chips for suspend. Only do that on <= RV250 chips that
+		 * have been tested
+		 */
+		if (rinfo->is_mobility && rinfo->family <= CHIP_FAMILY_RV250) {
+			/* Program V2CLK */
+			radeon_pm_program_v2clk(rinfo);
+		
+			/* Disable IO PADs */
+			radeon_pm_disable_iopad(rinfo);
+
+			/* Set low current */
+			radeon_pm_low_current(rinfo);
+
+			/* Prepare chip for power management */
+			radeon_pm_setup_for_suspend(rinfo);
+
+			/* Reset the MDLL */
+			/* because both INPLL and OUTPLL take the same lock, that's why. */
+			tmp = INPLL( pllMDLL_CKO) | MDLL_CKO__MCKOA_RESET | MDLL_CKO__MCKOB_RESET;
+			OUTPLL( pllMDLL_CKO, tmp );
+		}
+
+		/* Switch PCI power managment to D2. */
+		for (;;) {
+			pci_read_config_word(
+				rinfo->pdev, rinfo->pm_reg+PCI_PM_CTRL,
+				&pwr_cmd);
+			if (pwr_cmd & 2)
+				break;			
+			pci_write_config_word(
+				rinfo->pdev, rinfo->pm_reg+PCI_PM_CTRL,
+				(pwr_cmd & ~PCI_PM_CTRL_STATE_MASK) | 2);
+			mdelay(500);
+		}
+	} else {
+		printk(KERN_DEBUG "radeonfb: switching to D0 state...\n");
+
+		/* Switch back PCI powermanagment to D0 */
+		mdelay(200);
+		pci_write_config_word(rinfo->pdev, rinfo->pm_reg+PCI_PM_CTRL, 0);
+		mdelay(500);
+
+		/* Reset the SDRAM controller  */
+       		radeon_pm_full_reset_sdram(rinfo);
+		
+		/* Restore some registers */
+		radeon_pm_restore_regs(rinfo);
+		radeon_pm_enable_dynamic_mode(rinfo);
+	}
+}
+
+int radeonfb_pci_suspend(struct pci_dev *pdev, u32 state)
+{
+        struct fb_info *info = pci_get_drvdata(pdev);
+        struct radeonfb_info *rinfo = info->par;
+
+	/* We don't do anything but D2, for now we return 0, but
+	 * we may want to change that. How do we know if the BIOS
+	 * can properly take care of D3 ? Also, with swsusp, we
+	 * know we'll be rebooted, ...
+	 */
+
+	printk(KERN_DEBUG "radeonfb: suspending to state: %d...\n", state);
+	
+	acquire_console_sem();
+
+	/* Userland should do this but doesn't... bridge gets suspended
+	 * too late. Unfortunately, that works only when AGP is built-in,
+	 * not for a module.
+	 */
+#ifdef CONFIG_AGP
+	agp_enable(0);
+#endif
+
+	fb_set_suspend(info, 1);
+
+	if (!(info->flags & FBINFO_HWACCEL_DISABLED)) {
+		/* Make sure engine is reset */
+		radeon_engine_idle();
+		radeonfb_engine_reset(rinfo);
+		radeon_engine_idle();
+	}
+
+	/* Blank display and LCD */
+	radeonfb_blank(VESA_POWERDOWN, info);
+
+	/* Sleep */
+	rinfo->asleep = 1;
+	rinfo->lock_blank = 1;
+
+	/* Suspend the chip to D2 state when supported
+	 */
+#ifdef CONFIG_RADEON_HAS_D2
+	if (radeon_suspend_to_d2(rinfo, state))
+		radeon_set_suspend(rinfo, 1);
+#endif /* CONFIG_RADEON_HAS_D2 */
+
+	release_console_sem();
+
+	pdev->dev.power_state = state;
+
+	return 0;
+}
+
+int radeonfb_pci_resume(struct pci_dev *pdev)
+{
+        struct fb_info *info = pci_get_drvdata(pdev);
+        struct radeonfb_info *rinfo = info->par;
+
+	if (pdev->dev.power_state == 0)
+		return 0;
+
+	acquire_console_sem();
+
+	/* Wakeup chip */
+#ifdef CONFIG_RADEON_HAS_D2
+	if (radeon_suspend_to_d2(rinfo, 0))
+		radeon_set_suspend(rinfo, 0);
+#endif /* CONFIG_RADEON_HAS_D2 */
+
+	rinfo->asleep = 0;
+
+	/* Restore display & engine */
+	radeonfb_set_par(info);
+	fb_pan_display(info, &info->var);
+	fb_set_cmap(&info->cmap, info);
+
+	/* Refresh */
+	fb_set_suspend(info, 0);
+
+	/* Unblank */
+	rinfo->lock_blank = 0;
+	radeonfb_blank(0, info);
+
+	release_console_sem();
+
+	pdev->dev.power_state = 0;
+
+	printk(KERN_DEBUG "radeonfb: resumed !\n");
+
+	return 0;
+}
+
+#endif /* CONFIG_PM */
diff -ubw -Naur linux-2.6.8.orig/include/asm-ppc/suspend.h linux-2.6.8/include/asm-ppc/suspend.h
--- linux-2.6.8.orig/include/asm-ppc/suspend.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.8/include/asm-ppc/suspend.h	2004-08-14 18:23:39.997577408 +0200
@@ -0,0 +1,12 @@
+static inline int arch_prepare_suspend(void)
+{
+	return 0;
+}
+
+static inline void save_processor_state(void)
+{
+}
+
+static inline void restore_processor_state(void)
+{
+}
diff -ubw -Naur linux-2.6.8.orig/include/linux/reboot.h linux-2.6.8/include/linux/reboot.h
--- linux-2.6.8.orig/include/linux/reboot.h	2004-08-14 07:38:11.000000000 +0200
+++ linux-2.6.8/include/linux/reboot.h	2004-08-14 18:23:39.999577104 +0200
@@ -42,6 +42,8 @@
 extern int register_reboot_notifier(struct notifier_block *);
 extern int unregister_reboot_notifier(struct notifier_block *);
 
+/* For use by swsusp only */
+extern struct notifier_block *reboot_notifier_list;
 
 /*
  * Architecture-specific implementations of sys_reboot commands.
diff -ubw -Naur linux-2.6.8.orig/include/linux/suspend.h linux-2.6.8/include/linux/suspend.h
--- linux-2.6.8.orig/include/linux/suspend.h	2004-08-14 07:36:32.000000000 +0200
+++ linux-2.6.8/include/linux/suspend.h	2004-08-14 18:23:40.001576800 +0200
@@ -1,7 +1,7 @@
 #ifndef _LINUX_SWSUSP_H
 #define _LINUX_SWSUSP_H
 
-#ifdef CONFIG_X86
+#if (defined  CONFIG_X86) || (defined CONFIG_PPC32)
 #include <asm/suspend.h>
 #endif
 #include <linux/swap.h>
diff -ubw -Naur linux-2.6.8.orig/kernel/power/disk.c linux-2.6.8/kernel/power/disk.c
--- linux-2.6.8.orig/kernel/power/disk.c	2004-08-14 07:36:13.000000000 +0200
+++ linux-2.6.8/kernel/power/disk.c	2004-08-14 18:23:40.004576344 +0200
@@ -17,6 +17,7 @@
 #include <linux/string.h>
 #include <linux/delay.h>
 #include <linux/fs.h>
+#include <linux/reboot.h>
 #include "power.h"
 
 
@@ -45,23 +46,26 @@
 	unsigned long flags;
 	int error = 0;
 
-	local_irq_save(flags);
-	device_power_down(PM_SUSPEND_DISK);
 	switch(mode) {
 	case PM_DISK_PLATFORM:
+		local_irq_save(flags);
 		error = pm_ops->enter(PM_SUSPEND_DISK);
+		local_irq_restore(flags);
 		break;
 	case PM_DISK_SHUTDOWN:
 		printk("Powering off system\n");
+		notifier_call_chain(&reboot_notifier_list, SYS_POWER_OFF, NULL);
+		device_shutdown();
 		machine_power_off();
 		break;
 	case PM_DISK_REBOOT:
+		printk("Rebooting system\n");
+		notifier_call_chain(&reboot_notifier_list, SYS_RESTART, NULL);
+		device_shutdown();
 		machine_restart(NULL);
 		break;
 	}
 	machine_halt();
-	device_power_up();
-	local_irq_restore(flags);
 	return 0;
 }
 
@@ -161,8 +165,10 @@
 
 	pr_debug("PM: snapshotting memory.\n");
 	in_suspend = 1;
-	if ((error = pmdisk_save()))
+	if ((error = pmdisk_save())) {
+		pr_debug("PM: snapshot memory failed !\n");
 		goto Done;
+	}
 
 	if (in_suspend) {
 		pr_debug("PM: writing image.\n");
@@ -223,9 +229,6 @@
 	 * Do it with disabled interrupts for best effect. That way, if some
 	 * driver scheduled DMA, we have good chance for DMA to finish ;-).
 	 */
-	pr_debug("PM: Waiting for DMAs to settle down.\n");
-	mdelay(1000);
-
 	pr_debug("PM: Restoring saved image.\n");
 	pmdisk_restore();
 	pr_debug("PM: Restore failed, recovering.n");
diff -ubw -Naur linux-2.6.8.orig/kernel/power/Kconfig linux-2.6.8/kernel/power/Kconfig
--- linux-2.6.8.orig/kernel/power/Kconfig	2004-08-14 07:37:39.000000000 +0200
+++ linux-2.6.8/kernel/power/Kconfig	2004-08-14 18:23:40.006576040 +0200
@@ -20,7 +20,7 @@
 
 config SOFTWARE_SUSPEND
 	bool "Software Suspend (EXPERIMENTAL)"
-	depends on EXPERIMENTAL && PM && SWAP
+	depends on EXPERIMENTAL && PM && SWAP && !PPC
 	---help---
 	  Enable the possibilty of suspendig machine. It doesn't need APM.
 	  You may suspend your machine by 'swsusp' or 'shutdown -z <time>' 
@@ -44,7 +44,7 @@
 
 config PM_DISK
 	bool "Suspend-to-Disk Support"
-	depends on PM && SWAP && X86 && !X86_64
+	depends on PM && SWAP && ((X86 && !X86_64) || PPC_PMAC)
 	---help---
 	  Suspend-to-disk is a power management state in which the contents
 	  of memory are stored on disk and the entire system is shut down or
diff -ubw -Naur linux-2.6.8.orig/kernel/power/main.c linux-2.6.8/kernel/power/main.c
--- linux-2.6.8.orig/kernel/power/main.c	2004-08-14 07:36:32.000000000 +0200
+++ linux-2.6.8/kernel/power/main.c	2004-08-14 18:23:40.008575736 +0200
@@ -4,7 +4,7 @@
  * Copyright (c) 2003 Patrick Mochel
  * Copyright (c) 2003 Open Source Development Lab
  * 
- * This file is release under the GPLv2
+ * This file is released under the GPLv2
  *
  */
 
diff -ubw -Naur linux-2.6.8.orig/kernel/power/pmdisk.c linux-2.6.8/kernel/power/pmdisk.c
--- linux-2.6.8.orig/kernel/power/pmdisk.c	2004-08-14 07:37:14.000000000 +0200
+++ linux-2.6.8/kernel/power/pmdisk.c	2004-08-14 18:23:40.015574672 +0200
@@ -18,7 +18,7 @@
  *
  */
 
-#undef DEBUG
+#define DEBUG
 
 #include <linux/mm.h>
 #include <linux/bio.h>
@@ -569,7 +569,7 @@
 
 
 /**
- *	enough_free_mem - Make sure we enough free memory to snapshot.
+ *	enough_free_mem - Make sure we have enough free memory to snapshot.
  *
  *	Returns TRUE or FALSE after checking the number of available 
  *	free pages.
@@ -625,8 +625,10 @@
 {
 	int error = 0;
 
-	if ((error = read_swapfiles()))
+	if ((error = read_swapfiles())) {
+		printk("Can't read swapfiles\n");
 		return error;
+	}
 
 	drain_local_pages();
 
@@ -703,6 +705,7 @@
  * Magic happens here
  */
 
+#if 0
 int pmdisk_resume(void)
 {
 	BUG_ON (nr_copy_pages_check != pmdisk_pages);
@@ -712,6 +715,7 @@
 	__flush_tlb_global();
 	return 0;
 }
+#endif
 
 /* pmdisk_arch_suspend() is implemented in arch/?/power/pmdisk.S,
    and basically does:
@@ -1069,9 +1073,11 @@
 	if ((error = arch_prepare_suspend()))
 		return error;
 	local_irq_disable();
+	device_power_down(PM_SUSPEND_DISK);
 	save_processor_state();
 	error = pmdisk_arch_suspend(0);
 	restore_processor_state();
+	device_power_up();
 	local_irq_enable();
 	return error;
 }
@@ -1130,10 +1136,13 @@
 int __init pmdisk_restore(void)
 {
 	int error;
+
 	local_irq_disable();
+	device_power_down(PM_SUSPEND_DISK);
 	save_processor_state();
 	error = pmdisk_arch_suspend(1);
 	restore_processor_state();
+	device_power_up();
 	local_irq_enable();
 	return error;
 }
diff -ubw -Naur linux-2.6.8.orig/kernel/power/pmdisk.c.orig linux-2.6.8/kernel/power/pmdisk.c.orig
--- linux-2.6.8.orig/kernel/power/pmdisk.c.orig	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.8/kernel/power/pmdisk.c.orig	2004-08-14 07:37:14.000000000 +0200
@@ -0,0 +1,1166 @@
+/*
+ * kernel/power/pmdisk.c - Suspend-to-disk implmentation
+ *
+ * This STD implementation is initially derived from swsusp (suspend-to-swap).
+ * The original copyright on that was: 
+ *
+ * Copyright (C) 1998-2001 Gabor Kuti <seasons@fornax.hu>
+ * Copyright (C) 1998,2001,2002 Pavel Machek <pavel@suse.cz>
+ *
+ * The additional parts are: 
+ * 
+ * Copyright (C) 2003 Patrick Mochel
+ * Copyright (C) 2003 Open Source Development Lab
+ * 
+ * This file is released under the GPLv2. 
+ *
+ * For more information, please see the text files in Documentation/power/
+ *
+ */
+
+#undef DEBUG
+
+#include <linux/mm.h>
+#include <linux/bio.h>
+#include <linux/suspend.h>
+#include <linux/version.h>
+#include <linux/reboot.h>
+#include <linux/device.h>
+#include <linux/swapops.h>
+#include <linux/bootmem.h>
+#include <linux/utsname.h>
+
+#include <asm/mmu_context.h>
+
+#include "power.h"
+
+
+extern asmlinkage int pmdisk_arch_suspend(int resume);
+
+#define __ADDRESS(x)  ((unsigned long) phys_to_virt(x))
+#define ADDRESS(x) __ADDRESS((x) << PAGE_SHIFT)
+#define ADDRESS2(x) __ADDRESS(__pa(x))		/* Needed for x86-64 where some pages are in memory twice */
+
+/* References to section boundaries */
+extern char __nosave_begin, __nosave_end;
+
+extern int is_head_of_free_region(struct page *);
+
+/* Variables to be preserved over suspend */
+static int pagedir_order_check;
+static int nr_copy_pages_check;
+
+/* For resume= kernel option */
+static char resume_file[256] = CONFIG_PM_DISK_PARTITION;
+
+static dev_t resume_device;
+/* Local variables that should not be affected by save */
+unsigned int pmdisk_pages __nosavedata = 0;
+
+/* Suspend pagedir is allocated before final copy, therefore it
+   must be freed after resume 
+
+   Warning: this is evil. There are actually two pagedirs at time of
+   resume. One is "pagedir_save", which is empty frame allocated at
+   time of suspend, that must be freed. Second is "pagedir_nosave", 
+   allocated at time of resume, that travels through memory not to
+   collide with anything.
+ */
+suspend_pagedir_t *pm_pagedir_nosave __nosavedata = NULL;
+static suspend_pagedir_t *pagedir_save;
+static int pagedir_order __nosavedata = 0;
+
+
+struct pmdisk_info {
+	struct new_utsname	uts;
+	u32			version_code;
+	unsigned long		num_physpages;
+	int			cpus;
+	unsigned long		image_pages;
+	unsigned long		pagedir_pages;
+	swp_entry_t		pagedir[768];
+} __attribute__((aligned(PAGE_SIZE))) pmdisk_info;
+
+
+
+#define PMDISK_SIG	"pmdisk-swap1"
+
+struct pmdisk_header {
+	char reserved[PAGE_SIZE - 20 - sizeof(swp_entry_t)];
+	swp_entry_t pmdisk_info;
+	char	orig_sig[10];
+	char	sig[10];
+} __attribute__((packed, aligned(PAGE_SIZE))) pmdisk_header;
+
+/*
+ * XXX: We try to keep some more pages free so that I/O operations succeed
+ * without paging. Might this be more?
+ */
+#define PAGES_FOR_IO	512
+
+
+/*
+ * Saving part...
+ */
+
+
+/* We memorize in swapfile_used what swap devices are used for suspension */
+#define SWAPFILE_UNUSED    0
+#define SWAPFILE_SUSPEND   1	/* This is the suspending device */
+#define SWAPFILE_IGNORED   2	/* Those are other swap devices ignored for suspension */
+
+static unsigned short swapfile_used[MAX_SWAPFILES];
+static unsigned short root_swap;
+
+
+static int mark_swapfiles(swp_entry_t prev)
+{
+	int error;
+
+	rw_swap_page_sync(READ, 
+			  swp_entry(root_swap, 0),
+			  virt_to_page((unsigned long)&pmdisk_header));
+	if (!memcmp("SWAP-SPACE",pmdisk_header.sig,10) ||
+	    !memcmp("SWAPSPACE2",pmdisk_header.sig,10)) {
+		memcpy(pmdisk_header.orig_sig,pmdisk_header.sig,10);
+		memcpy(pmdisk_header.sig,PMDISK_SIG,10);
+		pmdisk_header.pmdisk_info = prev;
+		error = rw_swap_page_sync(WRITE, 
+					  swp_entry(root_swap, 0),
+					  virt_to_page((unsigned long)
+						       &pmdisk_header));
+	} else {
+		pr_debug("pmdisk: Partition is not swap space.\n");
+		error = -ENODEV;
+	}
+	return error;
+}
+
+static int read_swapfiles(void) /* This is called before saving image */
+{
+	int i, len;
+	
+	len=strlen(resume_file);
+	root_swap = 0xFFFF;
+	
+	swap_list_lock();
+	for(i=0; i<MAX_SWAPFILES; i++) {
+		if (swap_info[i].flags == 0) {
+			swapfile_used[i]=SWAPFILE_UNUSED;
+		} else {
+			if(!len) {
+				pr_debug("pmdisk: Default resume partition not set.\n");
+				if(root_swap == 0xFFFF) {
+					swapfile_used[i] = SWAPFILE_SUSPEND;
+					root_swap = i;
+				} else
+					swapfile_used[i] = SWAPFILE_IGNORED;				  
+			} else {
+	  			/* we ignore all swap devices that are not the resume_file */
+				if (1) {
+// FIXME				if(resume_device == swap_info[i].swap_device) {
+					swapfile_used[i] = SWAPFILE_SUSPEND;
+					root_swap = i;
+				} else
+				  	swapfile_used[i] = SWAPFILE_IGNORED;
+			}
+		}
+	}
+	swap_list_unlock();
+	return (root_swap != 0xffff) ? 0 : -ENODEV;
+}
+
+
+/* This is called after saving image so modification
+   will be lost after resume... and that's what we want. */
+static void lock_swapdevices(void)
+{
+	int i;
+
+	swap_list_lock();
+	for(i = 0; i< MAX_SWAPFILES; i++)
+		if(swapfile_used[i] == SWAPFILE_IGNORED) {
+			swap_info[i].flags ^= 0xFF; /* we make the device unusable. A new call to
+						       lock_swapdevices can unlock the devices. */
+		}
+	swap_list_unlock();
+}
+
+
+
+/**
+ *	write_swap_page - Write one page to a fresh swap location.
+ *	@addr:	Address we're writing.
+ *	@loc:	Place to store the entry we used.
+ *
+ *	Allocate a new swap entry and 'sync' it. Note we discard -EIO
+ *	errors. That is an artifact left over from swsusp. It did not 
+ *	check the return of rw_swap_page_sync() at all, since most pages
+ *	written back to swap would return -EIO.
+ *	This is a partial improvement, since we will at least return other
+ *	errors, though we need to eventually fix the damn code.
+ */
+
+static int write_swap_page(unsigned long addr, swp_entry_t * loc)
+{
+	swp_entry_t entry;
+	int error = 0;
+
+	entry = get_swap_page();
+	if (swp_offset(entry) && 
+	    swapfile_used[swp_type(entry)] == SWAPFILE_SUSPEND) {
+		error = rw_swap_page_sync(WRITE, entry,
+					  virt_to_page(addr));
+		if (error == -EIO)
+			error = 0;
+		if (!error)
+			*loc = entry;
+	} else
+		error = -ENOSPC;
+	return error;
+}
+
+
+/**
+ *	free_data - Free the swap entries used by the saved image.
+ *
+ *	Walk the list of used swap entries and free each one. 
+ */
+
+static void free_data(void)
+{
+	swp_entry_t entry;
+	int i;
+
+	for (i = 0; i < pmdisk_pages; i++) {
+		entry = (pm_pagedir_nosave + i)->swap_address;
+		if (entry.val)
+			swap_free(entry);
+		else
+			break;
+		(pm_pagedir_nosave + i)->swap_address = (swp_entry_t){0};
+	}
+}
+
+
+/**
+ *	write_data - Write saved image to swap.
+ *
+ *	Walk the list of pages in the image and sync each one to swap.
+ */
+
+static int write_data(void)
+{
+	int error = 0;
+	int i;
+
+	printk( "Writing data to swap (%d pages): ", pmdisk_pages );
+	for (i = 0; i < pmdisk_pages && !error; i++) {
+		if (!(i%100))
+			printk( "." );
+		error = write_swap_page((pm_pagedir_nosave+i)->address,
+					&((pm_pagedir_nosave+i)->swap_address));
+	}
+	printk(" %d Pages done.\n",i);
+	return error;
+}
+
+
+/**
+ *	free_pagedir - Free pages used by the page directory.
+ */
+
+static void free_pagedir_entries(void)
+{
+	int num = pmdisk_info.pagedir_pages;
+	int i;
+
+	for (i = 0; i < num; i++)
+		swap_free(pmdisk_info.pagedir[i]);
+}
+
+
+/**
+ *	write_pagedir - Write the array of pages holding the page directory.
+ *	@last:	Last swap entry we write (needed for header).
+ */
+
+static int write_pagedir(void)
+{
+	unsigned long addr = (unsigned long)pm_pagedir_nosave;
+	int error = 0;
+	int n = SUSPEND_PD_PAGES(pmdisk_pages);
+	int i;
+
+	pmdisk_info.pagedir_pages = n;
+	printk( "Writing pagedir (%d pages)\n", n);
+	for (i = 0; i < n && !error; i++, addr += PAGE_SIZE)
+		error = write_swap_page(addr,&pmdisk_info.pagedir[i]);
+	return error;
+}
+
+
+#ifdef DEBUG
+static void dump_pmdisk_info(void)
+{
+	printk(" pmdisk: Version: %u\n",pmdisk_info.version_code);
+	printk(" pmdisk: Num Pages: %ld\n",pmdisk_info.num_physpages);
+	printk(" pmdisk: UTS Sys: %s\n",pmdisk_info.uts.sysname);
+	printk(" pmdisk: UTS Node: %s\n",pmdisk_info.uts.nodename);
+	printk(" pmdisk: UTS Release: %s\n",pmdisk_info.uts.release);
+	printk(" pmdisk: UTS Version: %s\n",pmdisk_info.uts.version);
+	printk(" pmdisk: UTS Machine: %s\n",pmdisk_info.uts.machine);
+	printk(" pmdisk: UTS Domain: %s\n",pmdisk_info.uts.domainname);
+	printk(" pmdisk: CPUs: %d\n",pmdisk_info.cpus);
+	printk(" pmdisk: Image: %ld Pages\n",pmdisk_info.image_pages);
+	printk(" pmdisk: Pagedir: %ld Pages\n",pmdisk_info.pagedir_pages);
+}
+#else
+static void dump_pmdisk_info(void)
+{
+
+}
+#endif
+
+static void init_header(void)
+{
+	memset(&pmdisk_info,0,sizeof(pmdisk_info));
+	pmdisk_info.version_code = LINUX_VERSION_CODE;
+	pmdisk_info.num_physpages = num_physpages;
+	memcpy(&pmdisk_info.uts,&system_utsname,sizeof(system_utsname));
+
+	pmdisk_info.cpus = num_online_cpus();
+	pmdisk_info.image_pages = pmdisk_pages;
+}
+
+/**
+ *	write_header - Fill and write the suspend header.
+ *	@entry:	Location of the last swap entry used.
+ *
+ *	Allocate a page, fill header, write header. 
+ *
+ *	@entry is the location of the last pagedir entry written on 
+ *	entrance. On exit, it contains the location of the header. 
+ */
+
+static int write_header(swp_entry_t * entry)
+{
+	dump_pmdisk_info();
+	return write_swap_page((unsigned long)&pmdisk_info,entry);
+}
+
+
+
+/**
+ *	write_suspend_image - Write entire image and metadata.
+ *
+ */
+
+static int write_suspend_image(void)
+{
+	int error;
+	swp_entry_t prev = { 0 };
+
+	init_header();
+
+	if ((error = write_data()))
+		goto FreeData;
+
+	if ((error = write_pagedir()))
+		goto FreePagedir;
+
+	if ((error = write_header(&prev)))
+		goto FreePagedir;
+
+	error = mark_swapfiles(prev);
+ Done:
+	return error;
+ FreePagedir:
+	free_pagedir_entries();
+ FreeData:
+	free_data();
+	goto Done;
+}
+
+
+
+/**
+ *	saveable - Determine whether a page should be cloned or not.
+ *	@pfn:	The page
+ *
+ *	We save a page if it's Reserved, and not in the range of pages
+ *	statically defined as 'unsaveable', or if it isn't reserved, and
+ *	isn't part of a free chunk of pages.
+ *	If it is part of a free chunk, we update @pfn to point to the last 
+ *	page of the chunk.
+ */
+
+static int saveable(unsigned long * pfn)
+{
+	struct page * page = pfn_to_page(*pfn);
+
+	if (PageNosave(page))
+		return 0;
+
+	if (!PageReserved(page)) {
+		int chunk_size;
+
+		if ((chunk_size = is_head_of_free_region(page))) {
+			*pfn += chunk_size - 1;
+			return 0;
+		}
+	} else if (PageReserved(page)) {
+		/* Just copy whole code segment. 
+		 * Hopefully it is not that big.
+		 */
+		if ((ADDRESS(*pfn) >= (unsigned long) ADDRESS2(&__nosave_begin)) && 
+		    (ADDRESS(*pfn) <  (unsigned long) ADDRESS2(&__nosave_end))) {
+			pr_debug("[nosave %lx]\n", ADDRESS(*pfn));
+			return 0;
+		}
+		/* Hmm, perhaps copying all reserved pages is not 
+		 * too healthy as they may contain 
+		 * critical bios data? 
+		 */
+	}
+	return 1;
+}
+
+
+
+/**
+ *	count_pages - Determine size of page directory.
+ *	
+ *	Iterate over all the pages in the system and tally the number
+ *	we need to clone.
+ */
+
+static void count_pages(void)
+{
+	unsigned long pfn;
+	int n = 0;
+	
+	for (pfn = 0; pfn < max_pfn; pfn++) {
+		if (saveable(&pfn))
+			n++;
+	}
+	pmdisk_pages = n;
+}
+
+
+/**
+ *	copy_pages - Atomically snapshot memory.
+ *
+ *	Iterate over all the pages in the system and copy each one 
+ *	into its corresponding location in the pagedir.
+ *	We rely on the fact that the number of pages that we're snap-
+ *	shotting hasn't changed since we counted them. 
+ */
+
+static void copy_pages(void)
+{
+	struct pbe * p = pagedir_save;
+	unsigned long pfn;
+	int n = 0;
+
+	for (pfn = 0; pfn < max_pfn; pfn++) {
+		if (saveable(&pfn)) {
+			n++;
+			p->orig_address = ADDRESS(pfn);
+			copy_page((void *) p->address, 
+				  (void *) p->orig_address);
+			p++;
+		}
+	}
+	BUG_ON(n != pmdisk_pages);
+}
+
+
+/**
+ *	free_image_pages - Free each page allocated for snapshot.
+ */
+
+static void free_image_pages(void)
+{
+	struct pbe * p;
+	int i;
+
+	for (i = 0, p = pagedir_save; i < pmdisk_pages; i++, p++) {
+		ClearPageNosave(virt_to_page(p->address));
+		free_page(p->address);
+	}
+}
+
+
+/**
+ *	free_pagedir - Free the page directory.
+ */
+
+static void free_pagedir(void)
+{
+	free_image_pages();
+	free_pages((unsigned long)pagedir_save, pagedir_order);
+}
+
+
+static void calc_order(void)
+{
+	int diff;
+	int order;
+
+	order = get_bitmask_order(SUSPEND_PD_PAGES(pmdisk_pages));
+	pmdisk_pages += 1 << order;
+	do {
+		diff = get_bitmask_order(SUSPEND_PD_PAGES(pmdisk_pages)) - order;
+		if (diff) {
+			order += diff;
+			pmdisk_pages += 1 << diff;
+		}
+	} while(diff);
+	pagedir_order = order;
+}
+
+
+/**
+ *	alloc_pagedir - Allocate the page directory.
+ *
+ *	First, determine exactly how many contiguous pages we need, 
+ *	allocate them, then mark each 'unsavable'.
+ */
+
+static int alloc_pagedir(void)
+{
+	calc_order();
+	pagedir_save = (suspend_pagedir_t *)__get_free_pages(GFP_ATOMIC | __GFP_COLD, 
+							     pagedir_order);
+	if(!pagedir_save)
+		return -ENOMEM;
+	memset(pagedir_save,0,(1 << pagedir_order) * PAGE_SIZE);
+	pm_pagedir_nosave = pagedir_save;
+	return 0;
+}
+
+
+/**
+ *	alloc_image_pages - Allocate pages for the snapshot.
+ *
+ */
+
+static int alloc_image_pages(void)
+{
+	struct pbe * p;
+	int i;
+
+	for (i = 0, p = pagedir_save; i < pmdisk_pages; i++, p++) {
+		p->address = get_zeroed_page(GFP_ATOMIC | __GFP_COLD);
+		if(!p->address)
+			goto Error;
+		SetPageNosave(virt_to_page(p->address));
+	}
+	return 0;
+ Error:
+	do { 
+		if (p->address)
+			free_page(p->address);
+		p->address = 0;
+	} while (p-- > pagedir_save);
+	return -ENOMEM;
+}
+
+
+/**
+ *	enough_free_mem - Make sure we enough free memory to snapshot.
+ *
+ *	Returns TRUE or FALSE after checking the number of available 
+ *	free pages.
+ */
+
+static int enough_free_mem(void)
+{
+	if(nr_free_pages() < (pmdisk_pages + PAGES_FOR_IO)) {
+		pr_debug("pmdisk: Not enough free pages: Have %d\n",
+			 nr_free_pages());
+		return 0;
+	}
+	return 1;
+}
+
+
+/**
+ *	enough_swap - Make sure we have enough swap to save the image.
+ *
+ *	Returns TRUE or FALSE after checking the total amount of swap 
+ *	space avaiable.
+ *
+ *	FIXME: si_swapinfo(&i) returns all swap devices information.
+ *	We should only consider resume_device. 
+ */
+
+static int enough_swap(void)
+{
+	struct sysinfo i;
+
+	si_swapinfo(&i);
+	if (i.freeswap < (pmdisk_pages + PAGES_FOR_IO))  {
+		pr_debug("pmdisk: Not enough swap. Need %ld\n",i.freeswap);
+		return 0;
+	}
+	return 1;
+}
+
+
+/**
+ *	pmdisk_suspend - Atomically snapshot the system.
+ *
+ *	This must be called with interrupts disabled, to prevent the 
+ *	system changing at all from underneath us. 
+ *
+ *	To do this, we count the number of pages in the system that we 
+ *	need to save; make sure	we have enough memory and swap to clone
+ *	the pages and save them in swap, allocate the space to hold them,
+ *	and then snapshot them all.
+ */
+
+int pmdisk_suspend(void)
+{
+	int error = 0;
+
+	if ((error = read_swapfiles()))
+		return error;
+
+	drain_local_pages();
+
+	pm_pagedir_nosave = NULL;
+	pr_debug("pmdisk: Counting pages to copy.\n" );
+	count_pages();
+	
+	pr_debug("pmdisk: (pages needed: %d + %d free: %d)\n",
+		 pmdisk_pages,PAGES_FOR_IO,nr_free_pages());
+
+	if (!enough_free_mem())
+		return -ENOMEM;
+
+	if (!enough_swap())
+		return -ENOSPC;
+
+	if ((error = alloc_pagedir())) {
+		pr_debug("pmdisk: Allocating pagedir failed.\n");
+		return error;
+	}
+	if ((error = alloc_image_pages())) {
+		pr_debug("pmdisk: Allocating image pages failed.\n");
+		free_pagedir();
+		return error;
+	}
+
+	nr_copy_pages_check = pmdisk_pages;
+	pagedir_order_check = pagedir_order;
+
+	/* During allocating of suspend pagedir, new cold pages may appear. 
+	 * Kill them 
+	 */
+	drain_local_pages();
+
+	/* copy */
+	copy_pages();
+
+	/*
+	 * End of critical section. From now on, we can write to memory,
+	 * but we should not touch disk. This specially means we must _not_
+	 * touch swap space! Except we must write out our image of course.
+	 */
+
+	pr_debug("pmdisk: %d pages copied\n", pmdisk_pages );
+	return 0;
+}
+
+
+/**
+ *	suspend_save_image - Prepare and write saved image to swap.
+ *
+ *	IRQs are re-enabled here so we can resume devices and safely write
+ *	to the swap devices. We disable them again before we leave.
+ *
+ *	The second lock_swapdevices() will unlock ignored swap devices since
+ *	writing is finished.
+ *	It is important _NOT_ to umount filesystems at this point. We want
+ *	them synced (in case something goes wrong) but we DO not want to mark
+ *	filesystem clean: it is not. (And it does not matter, if we resume
+ *	correctly, we'll mark system clean, anyway.)
+ */
+
+static int suspend_save_image(void)
+{
+	int error;
+	device_resume();
+	lock_swapdevices();
+	error = write_suspend_image();
+	lock_swapdevices();
+	return error;
+}
+
+/*
+ * Magic happens here
+ */
+
+int pmdisk_resume(void)
+{
+	BUG_ON (nr_copy_pages_check != pmdisk_pages);
+	BUG_ON (pagedir_order_check != pagedir_order);
+	
+	/* Even mappings of "global" things (vmalloc) need to be fixed */
+	__flush_tlb_global();
+	return 0;
+}
+
+/* pmdisk_arch_suspend() is implemented in arch/?/power/pmdisk.S,
+   and basically does:
+
+	if (!resume) {
+		save_processor_state();
+		SAVE_REGISTERS
+		return pmdisk_suspend();
+	}
+	GO_TO_SWAPPER_PAGE_TABLES
+	COPY_PAGES_BACK
+	RESTORE_REGISTERS
+	restore_processor_state();
+	return pmdisk_resume();
+
+ */
+
+
+/* More restore stuff */
+
+#define does_collide(addr) does_collide_order(pm_pagedir_nosave, addr, 0)
+
+/*
+ * Returns true if given address/order collides with any orig_address 
+ */
+static int __init does_collide_order(suspend_pagedir_t *pagedir, 
+				     unsigned long addr, int order)
+{
+	int i;
+	unsigned long addre = addr + (PAGE_SIZE<<order);
+	
+	for(i=0; i < pmdisk_pages; i++)
+		if((pagedir+i)->orig_address >= addr &&
+			(pagedir+i)->orig_address < addre)
+			return 1;
+
+	return 0;
+}
+
+/*
+ * We check here that pagedir & pages it points to won't collide with pages
+ * where we're going to restore from the loaded pages later
+ */
+static int __init check_pagedir(void)
+{
+	int i;
+
+	for(i=0; i < pmdisk_pages; i++) {
+		unsigned long addr;
+
+		do {
+			addr = get_zeroed_page(GFP_ATOMIC);
+			if(!addr)
+				return -ENOMEM;
+		} while (does_collide(addr));
+
+		(pm_pagedir_nosave+i)->address = addr;
+	}
+	return 0;
+}
+
+static int __init relocate_pagedir(void)
+{
+	/*
+	 * We have to avoid recursion (not to overflow kernel stack),
+	 * and that's why code looks pretty cryptic 
+	 */
+	suspend_pagedir_t *old_pagedir = pm_pagedir_nosave;
+	void **eaten_memory = NULL;
+	void **c = eaten_memory, *m, *f;
+	int err;
+
+	pr_debug("pmdisk: Relocating pagedir\n");
+
+	if(!does_collide_order(old_pagedir, (unsigned long)old_pagedir, pagedir_order)) {
+		pr_debug("pmdisk: Relocation not necessary\n");
+		return 0;
+	}
+
+	err = -ENOMEM;
+	while ((m = (void *) __get_free_pages(GFP_ATOMIC, pagedir_order)) != NULL) {
+		if (!does_collide_order(old_pagedir, (unsigned long)m,
+					pagedir_order)) {
+			pm_pagedir_nosave =
+				memcpy(m, old_pagedir,
+				       PAGE_SIZE << pagedir_order);
+			err = 0;
+			break;
+		}
+		eaten_memory = m;
+		printk( "." ); 
+		*eaten_memory = c;
+		c = eaten_memory;
+	}
+
+	c = eaten_memory;
+	while(c) {
+		printk(":");
+		f = c;
+		c = *c;
+		free_pages((unsigned long)f, pagedir_order);
+	}
+	printk("|\n");
+	return err;
+}
+
+
+static struct block_device * resume_bdev;
+
+
+/**
+ *	Using bio to read from swap.
+ *	This code requires a bit more work than just using buffer heads
+ *	but, it is the recommended way for 2.5/2.6.
+ *	The following are to signal the beginning and end of I/O. Bios
+ *	finish asynchronously, while we want them to happen synchronously.
+ *	A simple atomic_t, and a wait loop take care of this problem.
+ */
+
+static atomic_t io_done = ATOMIC_INIT(0);
+
+static void start_io(void)
+{
+	atomic_set(&io_done,1);
+}
+
+static int end_io(struct bio * bio, unsigned int num, int err)
+{
+	atomic_set(&io_done,0);
+	return 0;
+}
+
+static void wait_io(void)
+{
+	while(atomic_read(&io_done))
+		io_schedule();
+}
+
+
+/**
+ *	submit - submit BIO request.
+ *	@rw:	READ or WRITE.
+ *	@off	physical offset of page.
+ *	@page:	page we're reading or writing.
+ *
+ *	Straight from the textbook - allocate and initialize the bio.
+ *	If we're writing, make sure the page is marked as dirty.
+ *	Then submit it and wait.
+ */
+
+static int submit(int rw, pgoff_t page_off, void * page)
+{
+	int error = 0;
+	struct bio * bio;
+
+	bio = bio_alloc(GFP_ATOMIC,1);
+	if (!bio)
+		return -ENOMEM;
+	bio->bi_sector = page_off * (PAGE_SIZE >> 9);
+	bio_get(bio);
+	bio->bi_bdev = resume_bdev;
+	bio->bi_end_io = end_io;
+
+	if (bio_add_page(bio, virt_to_page(page), PAGE_SIZE, 0) < PAGE_SIZE) {
+		printk("pmdisk: ERROR: adding page to bio at %ld\n",page_off);
+		error = -EFAULT;
+		goto Done;
+	}
+
+	if (rw == WRITE)
+		bio_set_pages_dirty(bio);
+	start_io();
+	submit_bio(rw | (1 << BIO_RW_SYNC), bio);
+	wait_io();
+ Done:
+	bio_put(bio);
+	return error;
+}
+
+static int
+read_page(pgoff_t page_off, void * page)
+{
+	return submit(READ,page_off,page);
+}
+
+static int
+write_page(pgoff_t page_off, void * page)
+{
+	return submit(WRITE,page_off,page);
+}
+
+
+extern dev_t __init name_to_dev_t(const char *line);
+
+
+static int __init check_sig(void)
+{
+	int error;
+
+	memset(&pmdisk_header,0,sizeof(pmdisk_header));
+	if ((error = read_page(0,&pmdisk_header)))
+		return error;
+	if (!memcmp(PMDISK_SIG,pmdisk_header.sig,10)) {
+		memcpy(pmdisk_header.sig,pmdisk_header.orig_sig,10);
+
+		/*
+		 * Reset swap signature now.
+		 */
+		error = write_page(0,&pmdisk_header);
+	} else { 
+		pr_debug(KERN_ERR "pmdisk: Invalid partition type.\n");
+		return -EINVAL;
+	}
+	if (!error)
+		pr_debug("pmdisk: Signature found, resuming\n");
+	return error;
+}
+
+
+/*
+ * Sanity check if this image makes sense with this kernel/swap context
+ * I really don't think that it's foolproof but more than nothing..
+ */
+
+static const char * __init sanity_check(void)
+{
+	dump_pmdisk_info();
+	if(pmdisk_info.version_code != LINUX_VERSION_CODE)
+		return "kernel version";
+	if(pmdisk_info.num_physpages != num_physpages)
+		return "memory size";
+	if (strcmp(pmdisk_info.uts.sysname,system_utsname.sysname))
+		return "system type";
+	if (strcmp(pmdisk_info.uts.release,system_utsname.release))
+		return "kernel release";
+	if (strcmp(pmdisk_info.uts.version,system_utsname.version))
+		return "version";
+	if (strcmp(pmdisk_info.uts.machine,system_utsname.machine))
+		return "machine";
+	if(pmdisk_info.cpus != num_online_cpus())
+		return "number of cpus";
+	return NULL;
+}
+
+
+static int __init check_header(void)
+{
+	const char * reason = NULL;
+	int error;
+
+	init_header();
+
+	if ((error = read_page(swp_offset(pmdisk_header.pmdisk_info), 
+			       &pmdisk_info)))
+		return error;
+
+ 	/* Is this same machine? */
+	if ((reason = sanity_check())) {
+		printk(KERN_ERR "pmdisk: Resume mismatch: %s\n",reason);
+		return -EPERM;
+	}
+	pmdisk_pages = pmdisk_info.image_pages;
+	return error;
+}
+
+
+static int __init read_pagedir(void)
+{
+	unsigned long addr;
+	int i, n = pmdisk_info.pagedir_pages;
+	int error = 0;
+
+	pagedir_order = get_bitmask_order(n);
+
+	addr =__get_free_pages(GFP_ATOMIC, pagedir_order);
+	if (!addr)
+		return -ENOMEM;
+	pm_pagedir_nosave = (struct pbe *)addr;
+
+	pr_debug("pmdisk: Reading pagedir (%d Pages)\n",n);
+
+	for (i = 0; i < n && !error; i++, addr += PAGE_SIZE) {
+		unsigned long offset = swp_offset(pmdisk_info.pagedir[i]);
+		if (offset)
+			error = read_page(offset, (void *)addr);
+		else
+			error = -EFAULT;
+	}
+	if (error)
+		free_pages((unsigned long)pm_pagedir_nosave,pagedir_order);
+	return error;
+}
+
+
+/**
+ *	read_image_data - Read image pages from swap.
+ *
+ *	You do not need to check for overlaps, check_pagedir()
+ *	already did that.
+ */
+
+static int __init read_image_data(void)
+{
+	struct pbe * p;
+	int error = 0;
+	int i;
+
+	printk( "Reading image data (%d pages): ", pmdisk_pages );
+	for(i = 0, p = pm_pagedir_nosave; i < pmdisk_pages && !error; i++, p++) {
+		if (!(i%100))
+			printk( "." );
+		error = read_page(swp_offset(p->swap_address),
+				  (void *)p->address);
+	}
+	printk(" %d done.\n",i);
+	return error;
+}
+
+
+static int __init read_suspend_image(void)
+{
+	int error = 0;
+
+	if ((error = check_sig()))
+		return error;
+	if ((error = check_header()))
+		return error;
+	if ((error = read_pagedir()))
+		return error;
+	if ((error = relocate_pagedir()))
+		goto FreePagedir;
+	if ((error = check_pagedir()))
+		goto FreePagedir;
+	if ((error = read_image_data()))
+		goto FreePagedir;
+ Done:
+	return error;
+ FreePagedir:
+	free_pages((unsigned long)pm_pagedir_nosave,pagedir_order);
+	goto Done;
+}
+
+/**
+ *	pmdisk_save - Snapshot memory
+ */
+
+int pmdisk_save(void) 
+{
+	int error;
+
+#if defined (CONFIG_HIGHMEM) || defined (CONFIG_DISCONTIGMEM)
+	pr_debug("pmdisk: not supported with high- or discontig-mem.\n");
+	return -EPERM;
+#endif
+	if ((error = arch_prepare_suspend()))
+		return error;
+	local_irq_disable();
+	save_processor_state();
+	error = pmdisk_arch_suspend(0);
+	restore_processor_state();
+	local_irq_enable();
+	return error;
+}
+
+
+/**
+ *	pmdisk_write - Write saved memory image to swap.
+ *
+ *	pmdisk_arch_suspend(0) returns after system is resumed.
+ *
+ *	pmdisk_arch_suspend() copies all "used" memory to "free" memory,
+ *	then unsuspends all device drivers, and writes memory to disk
+ *	using normal kernel mechanism.
+ */
+
+int pmdisk_write(void)
+{
+	return suspend_save_image();
+}
+
+
+/**
+ *	pmdisk_read - Read saved image from swap.
+ */
+
+int __init pmdisk_read(void)
+{
+	int error;
+
+	if (!strlen(resume_file))
+		return -ENOENT;
+
+	resume_device = name_to_dev_t(resume_file);
+	pr_debug("pmdisk: Resume From Partition: %s\n", resume_file);
+
+	resume_bdev = open_by_devnum(resume_device, FMODE_READ);
+	if (!IS_ERR(resume_bdev)) {
+		set_blocksize(resume_bdev, PAGE_SIZE);
+		error = read_suspend_image();
+		blkdev_put(resume_bdev);
+	} else
+		error = PTR_ERR(resume_bdev);
+
+	if (!error)
+		pr_debug("Reading resume file was successful\n");
+	else
+		pr_debug("pmdisk: Error %d resuming\n", error);
+	return error;
+}
+
+
+/**
+ *	pmdisk_restore - Replace running kernel with saved image.
+ */
+
+int __init pmdisk_restore(void)
+{
+	int error;
+	local_irq_disable();
+	save_processor_state();
+	error = pmdisk_arch_suspend(1);
+	restore_processor_state();
+	local_irq_enable();
+	return error;
+}
+
+
+/**
+ *	pmdisk_free - Free memory allocated to hold snapshot.
+ */
+
+int pmdisk_free(void)
+{
+	pr_debug( "Freeing prev allocated pagedir\n" );
+	free_pagedir();
+	return 0;
+}
+
+static int __init pmdisk_setup(char *str)
+{
+	if (strlen(str)) {
+		if (!strcmp(str,"off"))
+			resume_file[0] = '\0';
+		else
+			strncpy(resume_file, str, 255);
+	} else
+		resume_file[0] = '\0';
+	return 1;
+}
+
+__setup("pmdisk=", pmdisk_setup);
+
diff -ubw -Naur linux-2.6.8.orig/kernel/sys.c linux-2.6.8/kernel/sys.c
--- linux-2.6.8.orig/kernel/sys.c	2004-08-14 07:36:16.000000000 +0200
+++ linux-2.6.8/kernel/sys.c	2004-08-14 18:23:40.020573912 +0200
@@ -84,7 +84,7 @@
  *	and the like. 
  */
 
-static struct notifier_block *reboot_notifier_list;
+struct notifier_block *reboot_notifier_list;
 rwlock_t notifier_lock = RW_LOCK_UNLOCKED;
 
 /**
