Merge git://git.kernel.org/pub/scm/linux/kernel/git/paulus/powerpc-merge
diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
index 6ffae2d..1493c78 100644
--- a/arch/powerpc/Kconfig
+++ b/arch/powerpc/Kconfig
@@ -404,6 +404,14 @@
 	  this currently includes some models of iBook & Titanium
 	  PowerBook.
 
+config CPU_FREQ_PMAC64
+	bool "Support for some Apple G5s"
+	depends on CPU_FREQ && PMAC_SMU && PPC64
+	select CPU_FREQ_TABLE
+	help
+	  This adds support for frequency switching on Apple iMac G5,
+	  and some of the more recent desktop G5 machines as well.
+
 config PPC601_SYNC_FIX
 	bool "Workarounds for PPC601 bugs"
 	depends on 6xx && (PPC_PREP || PPC_PMAC)
@@ -484,6 +492,7 @@
 config FORCE_MAX_ZONEORDER
 	int
 	depends on PPC64
+	default "9" if PPC_64K_PAGES
 	default "13"
 
 config MATH_EMULATION
diff --git a/arch/powerpc/configs/g5_defconfig b/arch/powerpc/configs/g5_defconfig
index 6323065..e76854f 100644
--- a/arch/powerpc/configs/g5_defconfig
+++ b/arch/powerpc/configs/g5_defconfig
@@ -1,18 +1,32 @@
 #
 # Automatically generated make config: don't edit
-# Linux kernel version: 2.6.14-rc4
-# Thu Oct 20 08:30:23 2005
+# Linux kernel version: 2.6.14
+# Mon Nov  7 13:37:59 2005
 #
+CONFIG_PPC64=y
 CONFIG_64BIT=y
+CONFIG_PPC_MERGE=y
 CONFIG_MMU=y
+CONFIG_GENERIC_HARDIRQS=y
 CONFIG_RWSEM_XCHGADD_ALGORITHM=y
 CONFIG_GENERIC_CALIBRATE_DELAY=y
-CONFIG_GENERIC_ISA_DMA=y
+CONFIG_PPC=y
 CONFIG_EARLY_PRINTK=y
 CONFIG_COMPAT=y
+CONFIG_SYSVIPC_COMPAT=y
 CONFIG_SCHED_NO_NO_OMIT_FRAME_POINTER=y
 CONFIG_ARCH_MAY_HAVE_PC_FDC=y
-CONFIG_FORCE_MAX_ZONEORDER=13
+
+#
+# Processor support
+#
+CONFIG_POWER4_ONLY=y
+CONFIG_POWER4=y
+CONFIG_PPC_FPU=y
+CONFIG_ALTIVEC=y
+CONFIG_PPC_STD_MMU=y
+CONFIG_SMP=y
+CONFIG_NR_CPUS=2
 
 #
 # Code maturity level options
@@ -67,30 +81,60 @@
 CONFIG_MODULE_SRCVERSION_ALL=y
 CONFIG_KMOD=y
 CONFIG_STOP_MACHINE=y
-CONFIG_SYSVIPC_COMPAT=y
 
 #
 # Platform support
 #
-# CONFIG_PPC_ISERIES is not set
 CONFIG_PPC_MULTIPLATFORM=y
+# CONFIG_PPC_ISERIES is not set
+# CONFIG_EMBEDDED6xx is not set
+# CONFIG_APUS is not set
 # CONFIG_PPC_PSERIES is not set
-# CONFIG_PPC_BPA is not set
 CONFIG_PPC_PMAC=y
-# CONFIG_PPC_MAPLE is not set
-CONFIG_PPC=y
-CONFIG_PPC64=y
-CONFIG_PPC_OF=y
-CONFIG_MPIC=y
-CONFIG_ALTIVEC=y
-CONFIG_KEXEC=y
-CONFIG_U3_DART=y
 CONFIG_PPC_PMAC64=y
-CONFIG_BOOTX_TEXT=y
-CONFIG_POWER4_ONLY=y
+# CONFIG_PPC_MAPLE is not set
+# CONFIG_PPC_CELL is not set
+CONFIG_PPC_OF=y
+CONFIG_U3_DART=y
+CONFIG_MPIC=y
+# CONFIG_PPC_RTAS is not set
+# CONFIG_MMIO_NVRAM is not set
+# CONFIG_PPC_MPC106 is not set
+CONFIG_GENERIC_TBSYNC=y
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_TABLE=y
+# CONFIG_CPU_FREQ_DEBUG is not set
+CONFIG_CPU_FREQ_STAT=y
+# CONFIG_CPU_FREQ_STAT_DETAILS is not set
+CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y
+# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set
+CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
+CONFIG_CPU_FREQ_GOV_POWERSAVE=y
+CONFIG_CPU_FREQ_GOV_USERSPACE=y
+# CONFIG_CPU_FREQ_GOV_ONDEMAND is not set
+# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set
+CONFIG_CPU_FREQ_PMAC64=y
+# CONFIG_WANT_EARLY_SERIAL is not set
+
+#
+# Kernel options
+#
+# CONFIG_HZ_100 is not set
+CONFIG_HZ_250=y
+# CONFIG_HZ_1000 is not set
+CONFIG_HZ=250
+CONFIG_PREEMPT_NONE=y
+# CONFIG_PREEMPT_VOLUNTARY is not set
+# CONFIG_PREEMPT is not set
+# CONFIG_PREEMPT_BKL is not set
+CONFIG_BINFMT_ELF=y
+# CONFIG_BINFMT_MISC is not set
+CONFIG_FORCE_MAX_ZONEORDER=13
 CONFIG_IOMMU_VMERGE=y
-CONFIG_SMP=y
-CONFIG_NR_CPUS=2
+# CONFIG_HOTPLUG_CPU is not set
+CONFIG_KEXEC=y
+CONFIG_IRQ_ALL_CPUS=y
+# CONFIG_NUMA is not set
 CONFIG_ARCH_SELECT_MEMORY_MODEL=y
 CONFIG_ARCH_FLATMEM_ENABLE=y
 CONFIG_SELECT_MEMORY_MODEL=y
@@ -100,28 +144,21 @@
 CONFIG_FLATMEM=y
 CONFIG_FLAT_NODE_MEM_MAP=y
 # CONFIG_SPARSEMEM_STATIC is not set
-# CONFIG_NUMA is not set
+CONFIG_SPLIT_PTLOCK_CPUS=4
+# CONFIG_PPC_64K_PAGES is not set
 # CONFIG_SCHED_SMT is not set
-CONFIG_PREEMPT_NONE=y
-# CONFIG_PREEMPT_VOLUNTARY is not set
-# CONFIG_PREEMPT is not set
-# CONFIG_PREEMPT_BKL is not set
-# CONFIG_HZ_100 is not set
-CONFIG_HZ_250=y
-# CONFIG_HZ_1000 is not set
-CONFIG_HZ=250
-CONFIG_GENERIC_HARDIRQS=y
-CONFIG_SECCOMP=y
-CONFIG_BINFMT_ELF=y
-# CONFIG_BINFMT_MISC is not set
-# CONFIG_HOTPLUG_CPU is not set
 CONFIG_PROC_DEVICETREE=y
 # CONFIG_CMDLINE_BOOL is not set
+# CONFIG_PM is not set
+CONFIG_SECCOMP=y
 CONFIG_ISA_DMA_API=y
 
 #
-# Bus Options
+# Bus options
 #
+CONFIG_GENERIC_ISA_DMA=y
+# CONFIG_PPC_I8259 is not set
+# CONFIG_PPC_INDIRECT_PCI is not set
 CONFIG_PCI=y
 CONFIG_PCI_DOMAINS=y
 CONFIG_PCI_LEGACY_PROC=y
@@ -136,6 +173,7 @@
 # PCI Hotplug Support
 #
 # CONFIG_HOTPLUG_PCI is not set
+CONFIG_KERNEL_START=0xc000000000000000
 
 #
 # Networking
@@ -276,6 +314,10 @@
 # CONFIG_NET_DIVERT is not set
 # CONFIG_ECONET is not set
 # CONFIG_WAN_ROUTER is not set
+
+#
+# QoS and/or fair queueing
+#
 # CONFIG_NET_SCHED is not set
 CONFIG_NET_CLS_ROUTE=y
 
@@ -348,6 +390,11 @@
 CONFIG_IOSCHED_AS=y
 CONFIG_IOSCHED_DEADLINE=y
 CONFIG_IOSCHED_CFQ=y
+CONFIG_DEFAULT_AS=y
+# CONFIG_DEFAULT_DEADLINE is not set
+# CONFIG_DEFAULT_CFQ is not set
+# CONFIG_DEFAULT_NOOP is not set
+CONFIG_DEFAULT_IOSCHED="anticipatory"
 # CONFIG_ATA_OVER_ETH is not set
 
 #
@@ -449,6 +496,7 @@
 #
 # SCSI low-level drivers
 #
+# CONFIG_ISCSI_TCP is not set
 # CONFIG_BLK_DEV_3W_XXXX_RAID is not set
 # CONFIG_SCSI_3W_9XXX is not set
 # CONFIG_SCSI_ACARD is not set
@@ -465,10 +513,12 @@
 # CONFIG_SCSI_ATA_PIIX is not set
 # CONFIG_SCSI_SATA_MV is not set
 # CONFIG_SCSI_SATA_NV is not set
-# CONFIG_SCSI_SATA_PROMISE is not set
+# CONFIG_SCSI_PDC_ADMA is not set
 # CONFIG_SCSI_SATA_QSTOR is not set
+# CONFIG_SCSI_SATA_PROMISE is not set
 # CONFIG_SCSI_SATA_SX4 is not set
 # CONFIG_SCSI_SATA_SIL is not set
+# CONFIG_SCSI_SATA_SIL24 is not set
 # CONFIG_SCSI_SATA_SIS is not set
 # CONFIG_SCSI_SATA_ULI is not set
 # CONFIG_SCSI_SATA_VIA is not set
@@ -567,6 +617,9 @@
 CONFIG_ADB_PMU=y
 CONFIG_PMAC_SMU=y
 CONFIG_THERM_PM72=y
+CONFIG_WINDFARM=y
+CONFIG_WINDFARM_PM81=y
+CONFIG_WINDFARM_PM91=y
 
 #
 # Network device support
@@ -603,6 +656,7 @@
 # CONFIG_NET_TULIP is not set
 # CONFIG_HP100 is not set
 # CONFIG_NET_PCI is not set
+# CONFIG_FEC_8XX is not set
 
 #
 # Ethernet (1000 Mbit)
@@ -768,6 +822,7 @@
 # TPM devices
 #
 # CONFIG_TCG_TPM is not set
+# CONFIG_TELCLOCK is not set
 
 #
 # I2C support
@@ -820,6 +875,7 @@
 # CONFIG_SENSORS_PCF8591 is not set
 # CONFIG_SENSORS_RTC8564 is not set
 # CONFIG_SENSORS_MAX6875 is not set
+# CONFIG_RTC_X1205_I2C is not set
 # CONFIG_I2C_DEBUG_CORE is not set
 # CONFIG_I2C_DEBUG_ALGO is not set
 # CONFIG_I2C_DEBUG_BUS is not set
@@ -876,10 +932,9 @@
 # CONFIG_FB_ASILIANT is not set
 # CONFIG_FB_IMSTT is not set
 # CONFIG_FB_VGA16 is not set
-# CONFIG_FB_NVIDIA is not set
-CONFIG_FB_RIVA=y
-# CONFIG_FB_RIVA_I2C is not set
-# CONFIG_FB_RIVA_DEBUG is not set
+CONFIG_FB_NVIDIA=y
+CONFIG_FB_NVIDIA_I2C=y
+# CONFIG_FB_RIVA is not set
 # CONFIG_FB_MATROX is not set
 # CONFIG_FB_RADEON_OLD is not set
 CONFIG_FB_RADEON=y
@@ -924,7 +979,96 @@
 #
 # Sound
 #
-# CONFIG_SOUND is not set
+CONFIG_SOUND=m
+
+#
+# Advanced Linux Sound Architecture
+#
+CONFIG_SND=m
+CONFIG_SND_TIMER=m
+CONFIG_SND_PCM=m
+CONFIG_SND_HWDEP=m
+CONFIG_SND_RAWMIDI=m
+CONFIG_SND_SEQUENCER=m
+# CONFIG_SND_SEQ_DUMMY is not set
+CONFIG_SND_OSSEMUL=y
+CONFIG_SND_MIXER_OSS=m
+CONFIG_SND_PCM_OSS=m
+CONFIG_SND_SEQUENCER_OSS=y
+# CONFIG_SND_VERBOSE_PRINTK is not set
+# CONFIG_SND_DEBUG is not set
+CONFIG_SND_GENERIC_DRIVER=y
+
+#
+# Generic devices
+#
+# CONFIG_SND_DUMMY is not set
+# CONFIG_SND_VIRMIDI is not set
+# CONFIG_SND_MTPAV is not set
+# CONFIG_SND_SERIAL_U16550 is not set
+# CONFIG_SND_MPU401 is not set
+
+#
+# PCI devices
+#
+# CONFIG_SND_ALI5451 is not set
+# CONFIG_SND_ATIIXP is not set
+# CONFIG_SND_ATIIXP_MODEM is not set
+# CONFIG_SND_AU8810 is not set
+# CONFIG_SND_AU8820 is not set
+# CONFIG_SND_AU8830 is not set
+# CONFIG_SND_AZT3328 is not set
+# CONFIG_SND_BT87X is not set
+# CONFIG_SND_CS46XX is not set
+# CONFIG_SND_CS4281 is not set
+# CONFIG_SND_EMU10K1 is not set
+# CONFIG_SND_EMU10K1X is not set
+# CONFIG_SND_CA0106 is not set
+# CONFIG_SND_KORG1212 is not set
+# CONFIG_SND_MIXART is not set
+# CONFIG_SND_NM256 is not set
+# CONFIG_SND_RME32 is not set
+# CONFIG_SND_RME96 is not set
+# CONFIG_SND_RME9652 is not set
+# CONFIG_SND_HDSP is not set
+# CONFIG_SND_HDSPM is not set
+# CONFIG_SND_TRIDENT is not set
+# CONFIG_SND_YMFPCI is not set
+# CONFIG_SND_AD1889 is not set
+# CONFIG_SND_ALS4000 is not set
+# CONFIG_SND_CMIPCI is not set
+# CONFIG_SND_ENS1370 is not set
+# CONFIG_SND_ENS1371 is not set
+# CONFIG_SND_ES1938 is not set
+# CONFIG_SND_ES1968 is not set
+# CONFIG_SND_MAESTRO3 is not set
+# CONFIG_SND_FM801 is not set
+# CONFIG_SND_ICE1712 is not set
+# CONFIG_SND_ICE1724 is not set
+# CONFIG_SND_INTEL8X0 is not set
+# CONFIG_SND_INTEL8X0M is not set
+# CONFIG_SND_SONICVIBES is not set
+# CONFIG_SND_VIA82XX is not set
+# CONFIG_SND_VIA82XX_MODEM is not set
+# CONFIG_SND_VX222 is not set
+# CONFIG_SND_HDA_INTEL is not set
+
+#
+# ALSA PowerMac devices
+#
+CONFIG_SND_POWERMAC=m
+CONFIG_SND_POWERMAC_AUTO_DRC=y
+
+#
+# USB devices
+#
+CONFIG_SND_USB_AUDIO=m
+# CONFIG_SND_USB_USX2Y is not set
+
+#
+# Open Sound System
+#
+# CONFIG_SOUND_PRIME is not set
 
 #
 # USB support
@@ -958,12 +1102,16 @@
 #
 # USB Device Class drivers
 #
-# CONFIG_USB_BLUETOOTH_TTY is not set
+# CONFIG_OBSOLETE_OSS_USB_DRIVER is not set
 CONFIG_USB_ACM=m
 CONFIG_USB_PRINTER=y
 
 #
-# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' may also be needed; see USB_STORAGE Help for more information
+# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support'
+#
+
+#
+# may also be needed; see USB_STORAGE Help for more information
 #
 CONFIG_USB_STORAGE=y
 # CONFIG_USB_STORAGE_DEBUG is not set
@@ -1074,6 +1222,7 @@
 CONFIG_USB_SERIAL_KLSI=m
 CONFIG_USB_SERIAL_KOBIL_SCT=m
 CONFIG_USB_SERIAL_MCT_U232=m
+# CONFIG_USB_SERIAL_NOKIA_DKU2 is not set
 CONFIG_USB_SERIAL_PL2303=m
 # CONFIG_USB_SERIAL_HP4X is not set
 CONFIG_USB_SERIAL_SAFE=m
@@ -1311,6 +1460,20 @@
 CONFIG_NLS_UTF8=y
 
 #
+# Library routines
+#
+CONFIG_CRC_CCITT=m
+# CONFIG_CRC16 is not set
+CONFIG_CRC32=y
+CONFIG_LIBCRC32C=m
+CONFIG_ZLIB_INFLATE=y
+CONFIG_ZLIB_DEFLATE=m
+CONFIG_TEXTSEARCH=y
+CONFIG_TEXTSEARCH_KMP=m
+CONFIG_TEXTSEARCH_BM=m
+CONFIG_TEXTSEARCH_FSM=m
+
+#
 # Profiling support
 #
 CONFIG_PROFILING=y
@@ -1331,12 +1494,14 @@
 # CONFIG_DEBUG_KOBJECT is not set
 # CONFIG_DEBUG_INFO is not set
 CONFIG_DEBUG_FS=y
+# CONFIG_DEBUG_VM is not set
+# CONFIG_RCU_TORTURE_TEST is not set
 # CONFIG_DEBUG_STACKOVERFLOW is not set
 # CONFIG_KPROBES is not set
 # CONFIG_DEBUG_STACK_USAGE is not set
 # CONFIG_DEBUGGER is not set
-# CONFIG_PPCDBG is not set
 CONFIG_IRQSTACKS=y
+CONFIG_BOOTX_TEXT=y
 
 #
 # Security options
@@ -1376,17 +1541,3 @@
 #
 # Hardware crypto devices
 #
-
-#
-# Library routines
-#
-CONFIG_CRC_CCITT=m
-# CONFIG_CRC16 is not set
-CONFIG_CRC32=y
-CONFIG_LIBCRC32C=m
-CONFIG_ZLIB_INFLATE=y
-CONFIG_ZLIB_DEFLATE=m
-CONFIG_TEXTSEARCH=y
-CONFIG_TEXTSEARCH_KMP=m
-CONFIG_TEXTSEARCH_BM=m
-CONFIG_TEXTSEARCH_FSM=m
diff --git a/arch/powerpc/kernel/misc_64.S b/arch/powerpc/kernel/misc_64.S
index b3e95ff..ae1433d 100644
--- a/arch/powerpc/kernel/misc_64.S
+++ b/arch/powerpc/kernel/misc_64.S
@@ -604,6 +604,76 @@
 #endif /* defined(CONFIG_PPC_PMAC) || defined(CONFIG_PPC_MAPLE) */
 
 /*
+ * SCOM access functions for 970 (FX only for now)
+ *
+ * unsigned long scom970_read(unsigned int address);
+ * void scom970_write(unsigned int address, unsigned long value);
+ *
+ * The address passed in is the 24 bits register address. This code
+ * is 970 specific and will not check the status bits, so you should
+ * know what you are doing.
+ */
+_GLOBAL(scom970_read)
+	/* interrupts off */
+	mfmsr	r4
+	ori	r0,r4,MSR_EE
+	xori	r0,r0,MSR_EE
+	mtmsrd	r0,1
+
+	/* rotate 24 bits SCOM address 8 bits left and mask out it's low 8 bits
+	 * (including parity). On current CPUs they must be 0'd,
+	 * and finally or in RW bit
+	 */
+	rlwinm	r3,r3,8,0,15
+	ori	r3,r3,0x8000
+
+	/* do the actual scom read */
+	sync
+	mtspr	SPRN_SCOMC,r3
+	isync
+	mfspr	r3,SPRN_SCOMD
+	isync
+	mfspr	r0,SPRN_SCOMC
+	isync
+
+	/* XXX:	fixup result on some buggy 970's (ouch ! we lost a bit, bah
+	 * that's the best we can do). Not implemented yet as we don't use
+	 * the scom on any of the bogus CPUs yet, but may have to be done
+	 * ultimately
+	 */
+
+	/* restore interrupts */
+	mtmsrd	r4,1
+	blr
+
+
+_GLOBAL(scom970_write)
+	/* interrupts off */
+	mfmsr	r5
+	ori	r0,r5,MSR_EE
+	xori	r0,r0,MSR_EE
+	mtmsrd	r0,1
+
+	/* rotate 24 bits SCOM address 8 bits left and mask out it's low 8 bits
+	 * (including parity). On current CPUs they must be 0'd.
+	 */
+
+	rlwinm	r3,r3,8,0,15
+
+	sync
+	mtspr	SPRN_SCOMD,r4      /* write data */
+	isync
+	mtspr	SPRN_SCOMC,r3      /* write command */
+	isync
+	mfspr	3,SPRN_SCOMC
+	isync
+
+	/* restore interrupts */
+	mtmsrd	r5,1
+	blr
+
+
+/*
  * Create a kernel thread
  *   kernel_thread(fn, arg, flags)
  */
diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c
index 7f64f04..de69fb37c 100644
--- a/arch/powerpc/kernel/process.c
+++ b/arch/powerpc/kernel/process.c
@@ -46,10 +46,10 @@
 #include <asm/processor.h>
 #include <asm/mmu.h>
 #include <asm/prom.h>
+#include <asm/machdep.h>
 #ifdef CONFIG_PPC64
 #include <asm/firmware.h>
 #include <asm/time.h>
-#include <asm/machdep.h>
 #endif
 
 extern unsigned long _get_SP(void);
@@ -203,10 +203,8 @@
 
 int set_dabr(unsigned long dabr)
 {
-#ifdef CONFIG_PPC64
 	if (ppc_md.set_dabr)
 		return ppc_md.set_dabr(dabr);
-#endif
 
 	mtspr(SPRN_DABR, dabr);
 	return 0;
diff --git a/arch/powerpc/kernel/prom.c b/arch/powerpc/kernel/prom.c
index 3675ef4..f645adb 100644
--- a/arch/powerpc/kernel/prom.c
+++ b/arch/powerpc/kernel/prom.c
@@ -1974,14 +1974,29 @@
 /*
  * Add a property to a node
  */
-void prom_add_property(struct device_node* np, struct property* prop)
+int prom_add_property(struct device_node* np, struct property* prop)
 {
-	struct property **next = &np->properties;
+	struct property **next;
 
 	prop->next = NULL;	
-	while (*next)
+	write_lock(&devtree_lock);
+	next = &np->properties;
+	while (*next) {
+		if (strcmp(prop->name, (*next)->name) == 0) {
+			/* duplicate ! don't insert it */
+			write_unlock(&devtree_lock);
+			return -1;
+		}
 		next = &(*next)->next;
+	}
 	*next = prop;
+	write_unlock(&devtree_lock);
+
+	/* try to add to proc as well if it was initialized */
+	if (np->pde)
+		proc_device_tree_add_prop(np->pde, prop);
+
+	return 0;
 }
 
 /* I quickly hacked that one, check against spec ! */
diff --git a/arch/powerpc/kernel/prom_init.c b/arch/powerpc/kernel/prom_init.c
index c758b66..6dc33d1 100644
--- a/arch/powerpc/kernel/prom_init.c
+++ b/arch/powerpc/kernel/prom_init.c
@@ -403,19 +403,19 @@
 	}
 }
 
-static int __init prom_getprop(phandle node, const char *pname,
+static int inline prom_getprop(phandle node, const char *pname,
 			       void *value, size_t valuelen)
 {
 	return call_prom("getprop", 4, 1, node, ADDR(pname),
 			 (u32)(unsigned long) value, (u32) valuelen);
 }
 
-static int __init prom_getproplen(phandle node, const char *pname)
+static int inline prom_getproplen(phandle node, const char *pname)
 {
 	return call_prom("getproplen", 2, 1, node, ADDR(pname));
 }
 
-static int __init prom_setprop(phandle node, const char *pname,
+static int inline prom_setprop(phandle node, const char *pname,
 			       void *value, size_t valuelen)
 {
 	return call_prom("setprop", 4, 1, node, ADDR(pname),
@@ -1408,8 +1408,9 @@
 	struct prom_t *_prom = &RELOC(prom);
 	char compat[256];
 	int len, i = 0;
+#ifdef CONFIG_PPC64
 	phandle rtas;
-
+#endif
 	len = prom_getprop(_prom->root, "compatible",
 			   compat, sizeof(compat)-1);
 	if (len > 0) {
@@ -1872,7 +1873,7 @@
 	if (prom_getprop(u3, "device-rev", &u3_rev, sizeof(u3_rev))
 	    == PROM_ERROR)
 		return;
-	if (u3_rev != 0x35 && u3_rev != 0x37)
+	if (u3_rev < 0x35 || u3_rev > 0x39)
 		return;
 	/* does it need fixup ? */
 	if (prom_getproplen(i2c, "interrupts") > 0)
diff --git a/arch/powerpc/kernel/rtas.c b/arch/powerpc/kernel/rtas.c
index b7fc2d8..9d4e07f 100644
--- a/arch/powerpc/kernel/rtas.c
+++ b/arch/powerpc/kernel/rtas.c
@@ -17,6 +17,7 @@
 #include <linux/spinlock.h>
 #include <linux/module.h>
 #include <linux/init.h>
+#include <linux/delay.h>
 
 #include <asm/prom.h>
 #include <asm/rtas.h>
@@ -83,7 +84,7 @@
 		while (width-- > 0)
 			call_rtas_display_status(' ');
 		width = 16;
-		udelay(500000);
+		mdelay(500);
 		pending_newline = 1;
 	} else {
 		if (pending_newline) {
@@ -608,7 +609,6 @@
 	return 0;
 }
 
-#ifdef CONFIG_SMP
 /* This version can't take the spinlock, because it never returns */
 
 struct rtas_args rtas_stop_self_args = {
@@ -633,7 +633,6 @@
 
 	panic("Alas, I survived.\n");
 }
-#endif
 
 /*
  * Call early during boot, before mem init or bootmem, to retreive the RTAS
diff --git a/arch/powerpc/kernel/setup-common.c b/arch/powerpc/kernel/setup-common.c
index d43fa8c..e22856e 100644
--- a/arch/powerpc/kernel/setup-common.c
+++ b/arch/powerpc/kernel/setup-common.c
@@ -405,6 +405,46 @@
 console_initcall(set_preferred_console);
 #endif /* CONFIG_PPC_MULTIPLATFORM */
 
+void __init check_for_initrd(void)
+{
+#ifdef CONFIG_BLK_DEV_INITRD
+	unsigned long *prop;
+
+	DBG(" -> check_for_initrd()\n");
+
+	if (of_chosen) {
+		prop = (unsigned long *)get_property(of_chosen,
+				"linux,initrd-start", NULL);
+		if (prop != NULL) {
+			initrd_start = (unsigned long)__va(*prop);
+			prop = (unsigned long *)get_property(of_chosen,
+					"linux,initrd-end", NULL);
+			if (prop != NULL) {
+				initrd_end = (unsigned long)__va(*prop);
+				initrd_below_start_ok = 1;
+			} else
+				initrd_start = 0;
+		}
+	}
+
+	/* If we were passed an initrd, set the ROOT_DEV properly if the values
+	 * look sensible. If not, clear initrd reference.
+	 */
+	if (initrd_start >= KERNELBASE && initrd_end >= KERNELBASE &&
+	    initrd_end > initrd_start)
+		ROOT_DEV = Root_RAM0;
+	else {
+		printk("Bogus initrd %08lx %08lx\n", initrd_start, initrd_end);
+		initrd_start = initrd_end = 0;
+	}
+
+	if (initrd_start)
+		printk("Found initrd at 0x%lx:0x%lx\n", initrd_start, initrd_end);
+
+	DBG(" <- check_for_initrd()\n");
+#endif /* CONFIG_BLK_DEV_INITRD */
+}
+
 #ifdef CONFIG_SMP
 
 /**
diff --git a/arch/powerpc/kernel/setup_32.c b/arch/powerpc/kernel/setup_32.c
index b45eedb..3af2631 100644
--- a/arch/powerpc/kernel/setup_32.c
+++ b/arch/powerpc/kernel/setup_32.c
@@ -286,6 +286,7 @@
 	loops_per_jiffy = 500000000 / HZ;
 
 	unflatten_device_tree();
+	check_for_initrd();
 	finish_device_tree();
 
 	smp_setup_cpu_maps();
diff --git a/arch/powerpc/kernel/setup_64.c b/arch/powerpc/kernel/setup_64.c
index b099405..0471e84 100644
--- a/arch/powerpc/kernel/setup_64.c
+++ b/arch/powerpc/kernel/setup_64.c
@@ -41,7 +41,6 @@
 #include <asm/elf.h>
 #include <asm/machdep.h>
 #include <asm/paca.h>
-#include <asm/ppcdebug.h>
 #include <asm/time.h>
 #include <asm/cputable.h>
 #include <asm/sections.h>
@@ -60,6 +59,7 @@
 #include <asm/firmware.h>
 #include <asm/systemcfg.h>
 #include <asm/xmon.h>
+#include <asm/udbg.h>
 
 #ifdef DEBUG
 #define DBG(fmt...) udbg_printf(fmt)
@@ -244,12 +244,6 @@
 	DBG(" -> early_setup()\n");
 
 	/*
-	 * Fill the default DBG level (do we want to keep
-	 * that old mecanism around forever ?)
-	 */
-	ppcdbg_initialize();
-
-	/*
 	 * Do early initializations using the flattened device
 	 * tree, like retreiving the physical memory map or
 	 * calculating/retreiving the hash table size
@@ -401,43 +395,6 @@
 	DBG(" <- initialize_cache_info()\n");
 }
 
-static void __init check_for_initrd(void)
-{
-#ifdef CONFIG_BLK_DEV_INITRD
-	u64 *prop;
-
-	DBG(" -> check_for_initrd()\n");
-
-	if (of_chosen) {
-		prop = (u64 *)get_property(of_chosen,
-				"linux,initrd-start", NULL);
-		if (prop != NULL) {
-			initrd_start = (unsigned long)__va(*prop);
-			prop = (u64 *)get_property(of_chosen,
-					"linux,initrd-end", NULL);
-			if (prop != NULL) {
-				initrd_end = (unsigned long)__va(*prop);
-				initrd_below_start_ok = 1;
-			} else
-				initrd_start = 0;
-		}
-	}
-
-	/* If we were passed an initrd, set the ROOT_DEV properly if the values
-	 * look sensible. If not, clear initrd reference.
-	 */
-	if (initrd_start >= KERNELBASE && initrd_end >= KERNELBASE &&
-	    initrd_end > initrd_start)
-		ROOT_DEV = Root_RAM0;
-	else
-		initrd_start = initrd_end = 0;
-
-	if (initrd_start)
-		printk("Found initrd at 0x%lx:0x%lx\n", initrd_start, initrd_end);
-
-	DBG(" <- check_for_initrd()\n");
-#endif /* CONFIG_BLK_DEV_INITRD */
-}
 
 /*
  * Do some initial setup of the system.  The parameters are those which 
@@ -521,7 +478,6 @@
 
 	printk("-----------------------------------------------------\n");
 	printk("ppc64_pft_size                = 0x%lx\n", ppc64_pft_size);
-	printk("ppc64_debug_switch            = 0x%lx\n", ppc64_debug_switch);
 	printk("ppc64_interrupt_controller    = 0x%ld\n", ppc64_interrupt_controller);
 	printk("systemcfg                     = 0x%p\n", systemcfg);
 	printk("systemcfg->platform           = 0x%x\n", systemcfg->platform);
diff --git a/arch/powerpc/kernel/signal_32.c b/arch/powerpc/kernel/signal_32.c
index 876c57c..081d931 100644
--- a/arch/powerpc/kernel/signal_32.c
+++ b/arch/powerpc/kernel/signal_32.c
@@ -44,7 +44,6 @@
 #include <asm/cacheflush.h>
 #ifdef CONFIG_PPC64
 #include "ppc32.h"
-#include <asm/ppcdebug.h>
 #include <asm/unistd.h>
 #include <asm/vdso.h>
 #else
diff --git a/arch/powerpc/kernel/signal_64.c b/arch/powerpc/kernel/signal_64.c
index ec9d098..58194e1 100644
--- a/arch/powerpc/kernel/signal_64.c
+++ b/arch/powerpc/kernel/signal_64.c
@@ -33,7 +33,6 @@
 #include <asm/ucontext.h>
 #include <asm/uaccess.h>
 #include <asm/pgtable.h>
-#include <asm/ppcdebug.h>
 #include <asm/unistd.h>
 #include <asm/cacheflush.h>
 #include <asm/vdso.h>
diff --git a/arch/powerpc/kernel/smp.c b/arch/powerpc/kernel/smp.c
index 1794a69..5c330c3 100644
--- a/arch/powerpc/kernel/smp.c
+++ b/arch/powerpc/kernel/smp.c
@@ -40,7 +40,6 @@
 #include <asm/prom.h>
 #include <asm/smp.h>
 #include <asm/time.h>
-#include <asm/xmon.h>
 #include <asm/machdep.h>
 #include <asm/cputable.h>
 #include <asm/system.h>
diff --git a/arch/powerpc/kernel/time.c b/arch/powerpc/kernel/time.c
index b1c89bc..a6282b6 100644
--- a/arch/powerpc/kernel/time.c
+++ b/arch/powerpc/kernel/time.c
@@ -61,6 +61,7 @@
 #include <asm/prom.h>
 #include <asm/irq.h>
 #include <asm/div64.h>
+#include <asm/smp.h>
 #ifdef CONFIG_PPC64
 #include <asm/systemcfg.h>
 #include <asm/firmware.h>
@@ -119,10 +120,6 @@
 unsigned long ppc_proc_freq;
 unsigned long ppc_tb_freq;
 
-#ifdef CONFIG_PPC32	/* XXX for now */
-#define boot_cpuid	0
-#endif
-
 u64 tb_last_jiffy __cacheline_aligned_in_smp;
 unsigned long tb_last_stamp;
 
diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c
index 07e5ee4..32f2158 100644
--- a/arch/powerpc/kernel/traps.c
+++ b/arch/powerpc/kernel/traps.c
@@ -39,7 +39,6 @@
 #include <asm/io.h>
 #include <asm/machdep.h>
 #include <asm/rtas.h>
-#include <asm/xmon.h>
 #include <asm/pmc.h>
 #ifdef CONFIG_PPC32
 #include <asm/reg.h>
@@ -748,22 +747,12 @@
 		return 0;
 	if (bug->line & BUG_WARNING_TRAP) {
 		/* this is a WARN_ON rather than BUG/BUG_ON */
-#ifdef CONFIG_XMON
-		xmon_printf(KERN_ERR "Badness in %s at %s:%ld\n",
-		       bug->function, bug->file,
-		       bug->line & ~BUG_WARNING_TRAP);
-#endif /* CONFIG_XMON */		
 		printk(KERN_ERR "Badness in %s at %s:%ld\n",
 		       bug->function, bug->file,
 		       bug->line & ~BUG_WARNING_TRAP);
 		dump_stack();
 		return 1;
 	}
-#ifdef CONFIG_XMON
-	xmon_printf(KERN_CRIT "kernel BUG in %s at %s:%ld!\n",
-	       bug->function, bug->file, bug->line);
-	xmon(regs);
-#endif /* CONFIG_XMON */
 	printk(KERN_CRIT "kernel BUG in %s at %s:%ld!\n",
 	       bug->function, bug->file, bug->line);
 
diff --git a/arch/powerpc/lib/locks.c b/arch/powerpc/lib/locks.c
index 2a912f4..35bd03c 100644
--- a/arch/powerpc/lib/locks.c
+++ b/arch/powerpc/lib/locks.c
@@ -23,6 +23,7 @@
 #if defined(CONFIG_PPC_SPLPAR) || defined(CONFIG_PPC_ISERIES)
 #include <asm/hvcall.h>
 #include <asm/iseries/hv_call.h>
+#include <asm/smp.h>
 
 void __spin_yield(raw_spinlock_t *lock)
 {
diff --git a/arch/powerpc/mm/fault.c b/arch/powerpc/mm/fault.c
index 841d8b6..93d4fbfd 100644
--- a/arch/powerpc/mm/fault.c
+++ b/arch/powerpc/mm/fault.c
@@ -389,5 +389,22 @@
 	}
 
 	/* kernel has accessed a bad area */
+
+	printk(KERN_ALERT "Unable to handle kernel paging request for ");
+	switch (regs->trap) {
+		case 0x300:
+		case 0x380:
+			printk("data at address 0x%08lx\n", regs->dar);
+			break;
+		case 0x400:
+		case 0x480:
+			printk("instruction fetch\n");
+			break;
+		default:
+			printk("unknown fault\n");
+	}
+	printk(KERN_ALERT "Faulting instruction address: 0x%08lx\n",
+		regs->nip);
+
 	die("Kernel access of bad area", regs, sig);
 }
diff --git a/arch/powerpc/mm/hash_utils_64.c b/arch/powerpc/mm/hash_utils_64.c
index f15dfb9..22e4748 100644
--- a/arch/powerpc/mm/hash_utils_64.c
+++ b/arch/powerpc/mm/hash_utils_64.c
@@ -33,7 +33,6 @@
 #include <linux/init.h>
 #include <linux/signal.h>
 
-#include <asm/ppcdebug.h>
 #include <asm/processor.h>
 #include <asm/pgtable.h>
 #include <asm/mmu.h>
@@ -409,12 +408,6 @@
 	htab_size_bytes = htab_get_table_size();
 	pteg_count = htab_size_bytes >> 7;
 
-	/* For debug, make the HTAB 1/8 as big as it normally would be. */
-	ifppcdebug(PPCDBG_HTABSIZE) {
-		pteg_count >>= 3;
-		htab_size_bytes = pteg_count << 7;
-	}
-
 	htab_hash_mask = pteg_count - 1;
 
 	if (systemcfg->platform & PLATFORM_LPAR) {
@@ -514,6 +507,9 @@
 {
 	struct page *page;
 
+	if (!pfn_valid(pte_pfn(pte)))
+		return pp;
+
 	page = pte_page(pte);
 
 	/* page is dirty */
diff --git a/arch/powerpc/mm/init_64.c b/arch/powerpc/mm/init_64.c
index dfe7fa3..ce974c8 100644
--- a/arch/powerpc/mm/init_64.c
+++ b/arch/powerpc/mm/init_64.c
@@ -57,7 +57,6 @@
 #include <asm/processor.h>
 #include <asm/mmzone.h>
 #include <asm/cputable.h>
-#include <asm/ppcdebug.h>
 #include <asm/sections.h>
 #include <asm/system.h>
 #include <asm/iommu.h>
diff --git a/arch/powerpc/mm/mem.c b/arch/powerpc/mm/mem.c
index 7faa46b..6f55efd 100644
--- a/arch/powerpc/mm/mem.c
+++ b/arch/powerpc/mm/mem.c
@@ -358,7 +358,7 @@
 	}
 
 	codesize = (unsigned long)&_sdata - (unsigned long)&_stext;
-	datasize = (unsigned long)&__init_begin - (unsigned long)&_sdata;
+	datasize = (unsigned long)&_edata - (unsigned long)&_sdata;
 	initsize = (unsigned long)&__init_end - (unsigned long)&__init_begin;
 	bsssize = (unsigned long)&__bss_stop - (unsigned long)&__bss_start;
 
diff --git a/arch/powerpc/mm/numa.c b/arch/powerpc/mm/numa.c
index 4035cad..da09ba0 100644
--- a/arch/powerpc/mm/numa.c
+++ b/arch/powerpc/mm/numa.c
@@ -21,6 +21,7 @@
 #include <asm/machdep.h>
 #include <asm/abs_addr.h>
 #include <asm/system.h>
+#include <asm/smp.h>
 
 static int numa_enabled = 1;
 
diff --git a/arch/powerpc/mm/pgtable_64.c b/arch/powerpc/mm/pgtable_64.c
index 51b7869..9008424 100644
--- a/arch/powerpc/mm/pgtable_64.c
+++ b/arch/powerpc/mm/pgtable_64.c
@@ -59,7 +59,6 @@
 #include <asm/processor.h>
 #include <asm/mmzone.h>
 #include <asm/cputable.h>
-#include <asm/ppcdebug.h>
 #include <asm/sections.h>
 #include <asm/system.h>
 #include <asm/iommu.h>
diff --git a/arch/powerpc/oprofile/op_model_power4.c b/arch/powerpc/oprofile/op_model_power4.c
index 8864493..c4ee547 100644
--- a/arch/powerpc/oprofile/op_model_power4.c
+++ b/arch/powerpc/oprofile/op_model_power4.c
@@ -17,6 +17,7 @@
 #include <asm/systemcfg.h>
 #include <asm/rtas.h>
 #include <asm/oprofile_impl.h>
+#include <asm/reg.h>
 
 #define dbg(args...)
 
@@ -81,6 +82,26 @@
 
 extern void ppc64_enable_pmcs(void);
 
+/*
+ * Older CPUs require the MMCRA sample bit to be always set, but newer 
+ * CPUs only want it set for some groups. Eventually we will remove all
+ * knowledge of this bit in the kernel, oprofile userspace should be
+ * setting it when required.
+ *
+ * In order to keep current installations working we force the bit for
+ * those older CPUs. Once everyone has updated their oprofile userspace we
+ * can remove this hack.
+ */
+static inline int mmcra_must_set_sample(void)
+{
+	if (__is_processor(PV_POWER4) || __is_processor(PV_POWER4p) ||
+	    __is_processor(PV_970) || __is_processor(PV_970FX) ||
+	    __is_processor(PV_970MP))
+		return 1;
+
+	return 0;
+}
+
 static void power4_cpu_setup(void *unused)
 {
 	unsigned int mmcr0 = mmcr0_val;
@@ -98,7 +119,8 @@
 
 	mtspr(SPRN_MMCR1, mmcr1_val);
 
-	mmcra |= MMCRA_SAMPLE_ENABLE;
+	if (mmcra_must_set_sample())
+		mmcra |= MMCRA_SAMPLE_ENABLE;
 	mtspr(SPRN_MMCRA, mmcra);
 
 	dbg("setup on cpu %d, mmcr0 %lx\n", smp_processor_id(),
diff --git a/arch/powerpc/platforms/iseries/irq.c b/arch/powerpc/platforms/iseries/irq.c
index c113591..a06603d 100644
--- a/arch/powerpc/platforms/iseries/irq.c
+++ b/arch/powerpc/platforms/iseries/irq.c
@@ -35,7 +35,6 @@
 #include <linux/irq.h>
 #include <linux/spinlock.h>
 
-#include <asm/ppcdebug.h>
 #include <asm/iseries/hv_types.h>
 #include <asm/iseries/hv_lp_event.h>
 #include <asm/iseries/hv_call_xm.h>
@@ -227,8 +226,6 @@
 	/* Unmask secondary INTA */
 	mask = 0x80000000;
 	HvCallPci_unmaskInterrupts(bus, subBus, deviceId, mask);
-	PPCDBG(PPCDBG_BUSWALK, "iSeries_enable_IRQ 0x%02X.%02X.%02X 0x%04X\n",
-			bus, subBus, deviceId, irq);
 }
 
 /* This is called by iSeries_activate_IRQs */
@@ -310,8 +307,6 @@
 	/* Mask secondary INTA   */
 	mask = 0x80000000;
 	HvCallPci_maskInterrupts(bus, subBus, deviceId, mask);
-	PPCDBG(PPCDBG_BUSWALK, "iSeries_disable_IRQ 0x%02X.%02X.%02X 0x%04X\n",
-			bus, subBus, deviceId, irq);
 }
 
 /*
diff --git a/arch/powerpc/platforms/iseries/pci.c b/arch/powerpc/platforms/iseries/pci.c
index 7d7d588..4b75131 100644
--- a/arch/powerpc/platforms/iseries/pci.c
+++ b/arch/powerpc/platforms/iseries/pci.c
@@ -32,7 +32,6 @@
 #include <asm/prom.h>
 #include <asm/machdep.h>
 #include <asm/pci-bridge.h>
-#include <asm/ppcdebug.h>
 #include <asm/iommu.h>
 #include <asm/abs_addr.h>
 
@@ -207,10 +206,6 @@
 	struct device_node *node;
 	struct pci_dn *pdn;
 
-	PPCDBG(PPCDBG_BUSWALK,
-			"-build_device_node 0x%02X.%02X.%02X Function: %02X\n",
-			Bus, SubBus, AgentId, Function);
-
 	node = kmalloc(sizeof(struct device_node), GFP_KERNEL);
 	if (node == NULL)
 		return NULL;
@@ -243,8 +238,6 @@
 	struct pci_controller *phb;
 	HvBusNumber bus;
 
-	PPCDBG(PPCDBG_BUSWALK, "find_and_init_phbs Entry\n");
-
 	/* Check all possible buses. */
 	for (bus = 0; bus < 256; bus++) {
 		int ret = HvCallXm_testBus(bus);
@@ -261,9 +254,6 @@
 			phb->last_busno = bus;
 			phb->ops = &iSeries_pci_ops;
 
-			PPCDBG(PPCDBG_BUSWALK, "PCI:Create iSeries pci_controller(%p), Bus: %04X\n",
-					phb, bus);
-
 			/* Find and connect the devices. */
 			scan_PHB_slots(phb);
 		}
@@ -285,11 +275,9 @@
  */
 void iSeries_pcibios_init(void)
 {
-	PPCDBG(PPCDBG_BUSWALK, "iSeries_pcibios_init Entry.\n");
 	iomm_table_initialize();
 	find_and_init_phbs();
 	io_page_mask = -1;
-	PPCDBG(PPCDBG_BUSWALK, "iSeries_pcibios_init Exit.\n");
 }
 
 /*
@@ -301,8 +289,6 @@
 	struct device_node *node;
 	int DeviceCount = 0;
 
-	PPCDBG(PPCDBG_BUSWALK, "iSeries_pcibios_fixup Entry.\n");
-
 	/* Fix up at the device node and pci_dev relationship */
 	mf_display_src(0xC9000100);
 
@@ -316,9 +302,6 @@
 			++DeviceCount;
 			pdev->sysdata = (void *)node;
 			PCI_DN(node)->pcidev = pdev;
-			PPCDBG(PPCDBG_BUSWALK,
-					"pdev 0x%p <==> DevNode 0x%p\n",
-					pdev, node);
 			allocate_device_bars(pdev);
 			iSeries_Device_Information(pdev, DeviceCount);
 			iommu_devnode_init_iSeries(node);
@@ -333,13 +316,10 @@
 
 void pcibios_fixup_bus(struct pci_bus *PciBus)
 {
-	PPCDBG(PPCDBG_BUSWALK, "iSeries_pcibios_fixup_bus(0x%04X) Entry.\n",
-			PciBus->number);
 }
 
 void pcibios_fixup_resources(struct pci_dev *pdev)
 {
-	PPCDBG(PPCDBG_BUSWALK, "fixup_resources pdev %p\n", pdev);
 }
 
 /*
@@ -401,9 +381,6 @@
 			printk("found device at bus %d idsel %d func %d (AgentId %x)\n",
 			       bus, IdSel, Function, AgentId);
 			/*  Connect EADs: 0x18.00.12 = 0x00 */
-			PPCDBG(PPCDBG_BUSWALK,
-					"PCI:Connect EADs: 0x%02X.%02X.%02X\n",
-					bus, SubBus, AgentId);
 			HvRc = HvCallPci_getBusUnitInfo(bus, SubBus, AgentId,
 					iseries_hv_addr(BridgeInfo),
 					sizeof(struct HvCallPci_BridgeInfo));
@@ -414,14 +391,6 @@
 					BridgeInfo->maxAgents,
 					BridgeInfo->maxSubBusNumber,
 					BridgeInfo->logicalSlotNumber);
-				PPCDBG(PPCDBG_BUSWALK,
-					"PCI: BridgeInfo, Type:0x%02X, SubBus:0x%02X, MaxAgents:0x%02X, MaxSubBus: 0x%02X, LSlot: 0x%02X\n",
-					BridgeInfo->busUnitInfo.deviceType,
-					BridgeInfo->subBusNumber,
-					BridgeInfo->maxAgents,
-					BridgeInfo->maxSubBusNumber,
-					BridgeInfo->logicalSlotNumber);
-
 				if (BridgeInfo->busUnitInfo.deviceType ==
 						HvCallPci_BridgeDevice)  {
 					/* Scan_Bridge_Slot...: 0x18.00.12 */
@@ -454,9 +423,6 @@
 
 	/* iSeries_allocate_IRQ.: 0x18.00.12(0xA3) */
 	Irq = iSeries_allocate_IRQ(Bus, 0, EADsIdSel);
-	PPCDBG(PPCDBG_BUSWALK,
-		"PCI:- allocate and assign IRQ 0x%02X.%02X.%02X = 0x%02X\n",
-		Bus, 0, EADsIdSel, Irq);
 
 	/*
 	 * Connect all functions of any device found.
@@ -482,9 +448,6 @@
 			printk("read vendor ID: %x\n", VendorId);
 
 			/* FoundDevice: 0x18.28.10 = 0x12AE */
-			PPCDBG(PPCDBG_BUSWALK,
-			       "PCI:- FoundDevice: 0x%02X.%02X.%02X = 0x%04X, irq %d\n",
-			       Bus, SubBus, AgentId, VendorId, Irq);
 			HvRc = HvCallPci_configStore8(Bus, SubBus, AgentId,
 						      PCI_INTERRUPT_LINE, Irq);
 			if (HvRc != 0)
diff --git a/arch/powerpc/platforms/iseries/setup.c b/arch/powerpc/platforms/iseries/setup.c
index c5207064..d3e4bf7 100644
--- a/arch/powerpc/platforms/iseries/setup.c
+++ b/arch/powerpc/platforms/iseries/setup.c
@@ -71,8 +71,6 @@
 #endif
 
 /* Function Prototypes */
-extern void ppcdbg_initialize(void);
-
 static void build_iSeries_Memory_Map(void);
 static void iseries_shared_idle(void);
 static void iseries_dedicated_idle(void);
@@ -309,8 +307,6 @@
 
 	ppc64_firmware_features = FW_FEATURE_ISERIES;
 
-	ppcdbg_initialize();
-
 	ppc64_interrupt_controller = IC_ISERIES;
 
 #if defined(CONFIG_BLK_DEV_INITRD)
diff --git a/arch/powerpc/platforms/iseries/smp.c b/arch/powerpc/platforms/iseries/smp.c
index 3336bad..fcb094e 100644
--- a/arch/powerpc/platforms/iseries/smp.c
+++ b/arch/powerpc/platforms/iseries/smp.c
@@ -40,7 +40,6 @@
 #include <asm/paca.h>
 #include <asm/iseries/hv_call.h>
 #include <asm/time.h>
-#include <asm/ppcdebug.h>
 #include <asm/machdep.h>
 #include <asm/cputable.h>
 #include <asm/system.h>
diff --git a/arch/powerpc/platforms/powermac/Makefile b/arch/powerpc/platforms/powermac/Makefile
index 4369676..c9df44f 100644
--- a/arch/powerpc/platforms/powermac/Makefile
+++ b/arch/powerpc/platforms/powermac/Makefile
@@ -1,7 +1,8 @@
 obj-y				+= pic.o setup.o time.o feature.o pci.o \
 				   sleep.o low_i2c.o cache.o
 obj-$(CONFIG_PMAC_BACKLIGHT)	+= backlight.o
-obj-$(CONFIG_CPU_FREQ_PMAC)	+= cpufreq.o
+obj-$(CONFIG_CPU_FREQ_PMAC)	+= cpufreq_32.o
+obj-$(CONFIG_CPU_FREQ_PMAC64)	+= cpufreq_64.o
 obj-$(CONFIG_NVRAM)		+= nvram.o
 # ppc64 pmac doesn't define CONFIG_NVRAM but needs nvram stuff
 obj-$(CONFIG_PPC64)		+= nvram.o
diff --git a/arch/powerpc/platforms/powermac/cpufreq.c b/arch/powerpc/platforms/powermac/cpufreq_32.c
similarity index 98%
rename from arch/powerpc/platforms/powermac/cpufreq.c
rename to arch/powerpc/platforms/powermac/cpufreq_32.c
index c47f8b6..56fd4e0 100644
--- a/arch/powerpc/platforms/powermac/cpufreq.c
+++ b/arch/powerpc/platforms/powermac/cpufreq_32.c
@@ -397,18 +397,16 @@
 					unsigned int relation)
 {
 	unsigned int    newstate = 0;
+	int		rc;
 
 	if (cpufreq_frequency_table_target(policy, pmac_cpu_freqs,
 			target_freq, relation, &newstate))
 		return -EINVAL;
 
-	return do_set_cpu_speed(newstate, 1);
-}
+	rc = do_set_cpu_speed(newstate, 1);
 
-unsigned int pmac_get_one_cpufreq(int i)
-{
-	/* Supports only one CPU for now */
-	return (i == 0) ? cur_freq : 0;
+	ppc_proc_freq = cur_freq * 1000ul;
+	return rc;
 }
 
 static int pmac_cpufreq_cpu_init(struct cpufreq_policy *policy)
@@ -474,6 +472,8 @@
 	do_set_cpu_speed(sleep_freq == low_freq ?
 			 CPUFREQ_LOW : CPUFREQ_HIGH, 0);
 
+	ppc_proc_freq = cur_freq * 1000ul;
+
 	no_schedule = 0;
 	return 0;
 }
@@ -547,7 +547,7 @@
 		 */
 		if (low_freq < 98000000)
 			low_freq = 101000000;
-			
+
 		/* Convert those to CPU core clocks */
 		low_freq = (low_freq * (*ratio)) / 2000;
 		hi_freq = (hi_freq * (*ratio)) / 2000;
@@ -714,6 +714,7 @@
 
 	pmac_cpu_freqs[CPUFREQ_LOW].frequency = low_freq;
 	pmac_cpu_freqs[CPUFREQ_HIGH].frequency = hi_freq;
+	ppc_proc_freq = cur_freq * 1000ul;
 
 	printk(KERN_INFO "Registering PowerMac CPU frequency driver\n");
 	printk(KERN_INFO "Low: %d Mhz, High: %d Mhz, Boot: %d Mhz\n",
diff --git a/arch/powerpc/platforms/powermac/cpufreq_64.c b/arch/powerpc/platforms/powermac/cpufreq_64.c
new file mode 100644
index 0000000..3915034
--- /dev/null
+++ b/arch/powerpc/platforms/powermac/cpufreq_64.c
@@ -0,0 +1,323 @@
+/*
+ *  Copyright (C) 2002 - 2005 Benjamin Herrenschmidt <benh@kernel.crashing.org>
+ *  and                       Markus Demleitner <msdemlei@cl.uni-heidelberg.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This driver adds basic cpufreq support for SMU & 970FX based G5 Macs,
+ * that is iMac G5 and latest single CPU desktop.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/cpufreq.h>
+#include <linux/init.h>
+#include <linux/completion.h>
+#include <asm/prom.h>
+#include <asm/machdep.h>
+#include <asm/irq.h>
+#include <asm/sections.h>
+#include <asm/cputable.h>
+#include <asm/time.h>
+#include <asm/smu.h>
+
+#undef DEBUG
+
+#ifdef DEBUG
+#define DBG(fmt...) printk(fmt)
+#else
+#define DBG(fmt...)
+#endif
+
+/* see 970FX user manual */
+
+#define SCOM_PCR 0x0aa001			/* PCR scom addr */
+
+#define PCR_HILO_SELECT		0x80000000U	/* 1 = PCR, 0 = PCRH */
+#define PCR_SPEED_FULL		0x00000000U	/* 1:1 speed value */
+#define PCR_SPEED_HALF		0x00020000U	/* 1:2 speed value */
+#define PCR_SPEED_QUARTER	0x00040000U	/* 1:4 speed value */
+#define PCR_SPEED_MASK		0x000e0000U	/* speed mask */
+#define PCR_SPEED_SHIFT		17
+#define PCR_FREQ_REQ_VALID	0x00010000U	/* freq request valid */
+#define PCR_VOLT_REQ_VALID	0x00008000U	/* volt request valid */
+#define PCR_TARGET_TIME_MASK	0x00006000U	/* target time */
+#define PCR_STATLAT_MASK	0x00001f00U	/* STATLAT value */
+#define PCR_SNOOPLAT_MASK	0x000000f0U	/* SNOOPLAT value */
+#define PCR_SNOOPACC_MASK	0x0000000fU	/* SNOOPACC value */
+
+#define SCOM_PSR 0x408001			/* PSR scom addr */
+/* warning: PSR is a 64 bits register */
+#define PSR_CMD_RECEIVED	0x2000000000000000U   /* command received */
+#define PSR_CMD_COMPLETED	0x1000000000000000U   /* command completed */
+#define PSR_CUR_SPEED_MASK	0x0300000000000000U   /* current speed */
+#define PSR_CUR_SPEED_SHIFT	(56)
+
+/*
+ * The G5 only supports two frequencies (Quarter speed is not supported)
+ */
+#define CPUFREQ_HIGH                  0
+#define CPUFREQ_LOW                   1
+
+static struct cpufreq_frequency_table g5_cpu_freqs[] = {
+	{CPUFREQ_HIGH, 		0},
+	{CPUFREQ_LOW,		0},
+	{0,			CPUFREQ_TABLE_END},
+};
+
+static struct freq_attr* g5_cpu_freqs_attr[] = {
+	&cpufreq_freq_attr_scaling_available_freqs,
+	NULL,
+};
+
+/* Power mode data is an array of the 32 bits PCR values to use for
+ * the various frequencies, retreived from the device-tree
+ */
+static u32 *g5_pmode_data;
+static int g5_pmode_max;
+static int g5_pmode_cur;
+
+static DECLARE_MUTEX(g5_switch_mutex);
+
+
+static struct smu_sdbp_fvt *g5_fvt_table;	/* table of op. points */
+static int g5_fvt_count;			/* number of op. points */
+static int g5_fvt_cur;				/* current op. point */
+
+/* ----------------- real hardware interface */
+
+static void g5_switch_volt(int speed_mode)
+{
+	struct smu_simple_cmd	cmd;
+
+	DECLARE_COMPLETION(comp);
+	smu_queue_simple(&cmd, SMU_CMD_POWER_COMMAND, 8, smu_done_complete,
+			 &comp, 'V', 'S', 'L', 'E', 'W',
+			 0xff, g5_fvt_cur+1, speed_mode);
+	wait_for_completion(&comp);
+}
+
+static int g5_switch_freq(int speed_mode)
+{
+	struct cpufreq_freqs freqs;
+	int to;
+
+	if (g5_pmode_cur == speed_mode)
+		return 0;
+
+	down(&g5_switch_mutex);
+
+	freqs.old = g5_cpu_freqs[g5_pmode_cur].frequency;
+	freqs.new = g5_cpu_freqs[speed_mode].frequency;
+	freqs.cpu = 0;
+
+	cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+
+	/* If frequency is going up, first ramp up the voltage */
+	if (speed_mode < g5_pmode_cur)
+		g5_switch_volt(speed_mode);
+
+	/* Clear PCR high */
+	scom970_write(SCOM_PCR, 0);
+	/* Clear PCR low */
+       	scom970_write(SCOM_PCR, PCR_HILO_SELECT | 0);
+	/* Set PCR low */
+	scom970_write(SCOM_PCR, PCR_HILO_SELECT |
+		      g5_pmode_data[speed_mode]);
+
+	/* Wait for completion */
+	for (to = 0; to < 10; to++) {
+		unsigned long psr = scom970_read(SCOM_PSR);
+
+		if ((psr & PSR_CMD_RECEIVED) == 0 &&
+		    (((psr >> PSR_CUR_SPEED_SHIFT) ^
+		      (g5_pmode_data[speed_mode] >> PCR_SPEED_SHIFT)) & 0x3)
+		    == 0)
+			break;
+		if (psr & PSR_CMD_COMPLETED)
+			break;
+		udelay(100);
+	}
+
+	/* If frequency is going down, last ramp the voltage */
+	if (speed_mode > g5_pmode_cur)
+		g5_switch_volt(speed_mode);
+
+	g5_pmode_cur = speed_mode;
+	ppc_proc_freq = g5_cpu_freqs[speed_mode].frequency * 1000ul;
+
+	cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+
+	up(&g5_switch_mutex);
+
+	return 0;
+}
+
+static int g5_query_freq(void)
+{
+	unsigned long psr = scom970_read(SCOM_PSR);
+	int i;
+
+	for (i = 0; i <= g5_pmode_max; i++)
+		if ((((psr >> PSR_CUR_SPEED_SHIFT) ^
+		      (g5_pmode_data[i] >> PCR_SPEED_SHIFT)) & 0x3) == 0)
+			break;
+	return i;
+}
+
+/* ----------------- cpufreq bookkeeping */
+
+static int g5_cpufreq_verify(struct cpufreq_policy *policy)
+{
+	return cpufreq_frequency_table_verify(policy, g5_cpu_freqs);
+}
+
+static int g5_cpufreq_target(struct cpufreq_policy *policy,
+	unsigned int target_freq, unsigned int relation)
+{
+	unsigned int    newstate = 0;
+
+	if (cpufreq_frequency_table_target(policy, g5_cpu_freqs,
+			target_freq, relation, &newstate))
+		return -EINVAL;
+
+	return g5_switch_freq(newstate);
+}
+
+static unsigned int g5_cpufreq_get_speed(unsigned int cpu)
+{
+	return g5_cpu_freqs[g5_pmode_cur].frequency;
+}
+
+static int g5_cpufreq_cpu_init(struct cpufreq_policy *policy)
+{
+	if (policy->cpu != 0)
+		return -ENODEV;
+
+	policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
+	policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
+	policy->cur = g5_cpu_freqs[g5_query_freq()].frequency;
+	cpufreq_frequency_table_get_attr(g5_cpu_freqs, policy->cpu);
+
+	return cpufreq_frequency_table_cpuinfo(policy,
+		g5_cpu_freqs);
+}
+
+
+static struct cpufreq_driver g5_cpufreq_driver = {
+	.name		= "powermac",
+	.owner		= THIS_MODULE,
+	.flags		= CPUFREQ_CONST_LOOPS,
+	.init		= g5_cpufreq_cpu_init,
+	.verify		= g5_cpufreq_verify,
+	.target		= g5_cpufreq_target,
+	.get		= g5_cpufreq_get_speed,
+	.attr 		= g5_cpu_freqs_attr,
+};
+
+
+static int __init g5_cpufreq_init(void)
+{
+	struct device_node *cpunode;
+	unsigned int psize, ssize;
+	struct smu_sdbp_header *shdr;
+	unsigned long max_freq;
+	u32 *valp;
+	int rc = -ENODEV;
+
+	/* Look for CPU and SMU nodes */
+	cpunode = of_find_node_by_type(NULL, "cpu");
+	if (!cpunode) {
+		DBG("No CPU node !\n");
+		return -ENODEV;
+	}
+
+	/* Check 970FX for now */
+	valp = (u32 *)get_property(cpunode, "cpu-version", NULL);
+	if (!valp) {
+		DBG("No cpu-version property !\n");
+		goto bail_noprops;
+	}
+	if (((*valp) >> 16) != 0x3c) {
+		DBG("Wrong CPU version: %08x\n", *valp);
+		goto bail_noprops;
+	}
+
+	/* Look for the powertune data in the device-tree */
+	g5_pmode_data = (u32 *)get_property(cpunode, "power-mode-data",&psize);
+	if (!g5_pmode_data) {
+		DBG("No power-mode-data !\n");
+		goto bail_noprops;
+	}
+	g5_pmode_max = psize / sizeof(u32) - 1;
+
+	/* Look for the FVT table */
+	shdr = smu_get_sdb_partition(SMU_SDB_FVT_ID, NULL);
+	if (!shdr)
+		goto bail_noprops;
+	g5_fvt_table = (struct smu_sdbp_fvt *)&shdr[1];
+	ssize = (shdr->len * sizeof(u32)) - sizeof(struct smu_sdbp_header);
+	g5_fvt_count = ssize / sizeof(struct smu_sdbp_fvt);
+	g5_fvt_cur = 0;
+
+	/* Sanity checking */
+	if (g5_fvt_count < 1 || g5_pmode_max < 1)
+		goto bail_noprops;
+
+	/*
+	 * From what I see, clock-frequency is always the maximal frequency.
+	 * The current driver can not slew sysclk yet, so we really only deal
+	 * with powertune steps for now. We also only implement full freq and
+	 * half freq in this version. So far, I haven't yet seen a machine
+	 * supporting anything else.
+	 */
+	valp = (u32 *)get_property(cpunode, "clock-frequency", NULL);
+	if (!valp)
+		return -ENODEV;
+	max_freq = (*valp)/1000;
+	g5_cpu_freqs[0].frequency = max_freq;
+	g5_cpu_freqs[1].frequency = max_freq/2;
+
+	/* Check current frequency */
+	g5_pmode_cur = g5_query_freq();
+	if (g5_pmode_cur > 1)
+		/* We don't support anything but 1:1 and 1:2, fixup ... */
+		g5_pmode_cur = 1;
+
+	/* Force apply current frequency to make sure everything is in
+	 * sync (voltage is right for example). Firmware may leave us with
+	 * a strange setting ...
+	 */
+	g5_switch_freq(g5_pmode_cur);
+
+	printk(KERN_INFO "Registering G5 CPU frequency driver\n");
+	printk(KERN_INFO "Low: %d Mhz, High: %d Mhz, Cur: %d MHz\n",
+		g5_cpu_freqs[1].frequency/1000,
+		g5_cpu_freqs[0].frequency/1000,
+		g5_cpu_freqs[g5_pmode_cur].frequency/1000);
+
+	rc = cpufreq_register_driver(&g5_cpufreq_driver);
+
+	/* We keep the CPU node on hold... hopefully, Apple G5 don't have
+	 * hotplug CPU with a dynamic device-tree ...
+	 */
+	return rc;
+
+ bail_noprops:
+	of_node_put(cpunode);
+
+	return rc;
+}
+
+module_init(g5_cpufreq_init);
+
+
+MODULE_LICENSE("GPL");
diff --git a/arch/powerpc/platforms/powermac/setup.c b/arch/powerpc/platforms/powermac/setup.c
index 80b58c1..7acb054 100644
--- a/arch/powerpc/platforms/powermac/setup.c
+++ b/arch/powerpc/platforms/powermac/setup.c
@@ -193,18 +193,6 @@
 		   pmac_newworld ? "NewWorld" : "OldWorld");
 }
 
-static void 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;
-	}
-#endif /* CONFIG_CPU_FREQ_PMAC */
-}
-
 #ifndef CONFIG_ADB_CUDA
 int find_via_cuda(void)
 {
@@ -767,7 +755,6 @@
 	.setup_arch		= pmac_setup_arch,
 	.init_early		= pmac_init_early,
 	.show_cpuinfo		= pmac_show_cpuinfo,
-	.show_percpuinfo	= pmac_show_percpuinfo,
 	.init_IRQ		= pmac_pic_init,
 	.get_irq		= mpic_get_irq,	/* changed later */
 	.pcibios_fixup		= pmac_pcibios_fixup,
diff --git a/arch/powerpc/platforms/pseries/iommu.c b/arch/powerpc/platforms/pseries/iommu.c
index 513e2723..fcc50bf 100644
--- a/arch/powerpc/platforms/pseries/iommu.c
+++ b/arch/powerpc/platforms/pseries/iommu.c
@@ -37,7 +37,6 @@
 #include <asm/io.h>
 #include <asm/prom.h>
 #include <asm/rtas.h>
-#include <asm/ppcdebug.h>
 #include <asm/iommu.h>
 #include <asm/pci-bridge.h>
 #include <asm/machdep.h>
@@ -47,6 +46,7 @@
 #include <asm/firmware.h>
 #include <asm/tce.h>
 #include <asm/ppc-pci.h>
+#include <asm/udbg.h>
 
 #include "plpar_wrappers.h"
 
diff --git a/arch/powerpc/platforms/pseries/lpar.c b/arch/powerpc/platforms/pseries/lpar.c
index ab0c6dd..a50e5f3 100644
--- a/arch/powerpc/platforms/pseries/lpar.c
+++ b/arch/powerpc/platforms/pseries/lpar.c
@@ -31,13 +31,14 @@
 #include <asm/machdep.h>
 #include <asm/abs_addr.h>
 #include <asm/mmu_context.h>
-#include <asm/ppcdebug.h>
 #include <asm/iommu.h>
 #include <asm/tlbflush.h>
 #include <asm/tlb.h>
 #include <asm/prom.h>
 #include <asm/abs_addr.h>
 #include <asm/cputable.h>
+#include <asm/udbg.h>
+#include <asm/smp.h>
 
 #include "plpar_wrappers.h"
 
diff --git a/arch/powerpc/platforms/pseries/plpar_wrappers.h b/arch/powerpc/platforms/pseries/plpar_wrappers.h
index 382f8c5..3bd1b3e 100644
--- a/arch/powerpc/platforms/pseries/plpar_wrappers.h
+++ b/arch/powerpc/platforms/pseries/plpar_wrappers.h
@@ -107,14 +107,4 @@
 			lbuf[1]);
 }
 
-static inline long plpar_set_xdabr(unsigned long address, unsigned long flags)
-{
-	return plpar_hcall_norets(H_SET_XDABR, address, flags);
-}
-
-static inline long plpar_set_dabr(unsigned long val)
-{
-	return plpar_hcall_norets(H_SET_DABR, val);
-}
-
 #endif /* _PSERIES_PLPAR_WRAPPERS_H */
diff --git a/arch/powerpc/platforms/pseries/ras.c b/arch/powerpc/platforms/pseries/ras.c
index 6562ff4..fbd214d 100644
--- a/arch/powerpc/platforms/pseries/ras.c
+++ b/arch/powerpc/platforms/pseries/ras.c
@@ -48,7 +48,7 @@
 #include <asm/ptrace.h>
 #include <asm/machdep.h>
 #include <asm/rtas.h>
-#include <asm/ppcdebug.h>
+#include <asm/udbg.h>
 
 static unsigned char ras_log_buf[RTAS_ERROR_LOG_MAX];
 static DEFINE_SPINLOCK(ras_log_buf_lock);
diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c
index 65bee93..e78c393 100644
--- a/arch/powerpc/platforms/pseries/setup.c
+++ b/arch/powerpc/platforms/pseries/setup.c
@@ -65,6 +65,7 @@
 #include <asm/ppc-pci.h>
 #include <asm/i8259.h>
 #include <asm/udbg.h>
+#include <asm/smp.h>
 
 #include "plpar_wrappers.h"
 
@@ -353,14 +354,15 @@
 
 static int pseries_set_dabr(unsigned long dabr)
 {
-	if (firmware_has_feature(FW_FEATURE_XDABR)) {
-		/* We want to catch accesses from kernel and userspace */
-		return plpar_set_xdabr(dabr, H_DABRX_KERNEL | H_DABRX_USER);
-	}
-
-	return plpar_set_dabr(dabr);
+	return plpar_hcall_norets(H_SET_DABR, dabr);
 }
 
+static int pseries_set_xdabr(unsigned long dabr)
+{
+	/* We want to catch accesses from kernel and userspace */
+	return plpar_hcall_norets(H_SET_XDABR, dabr,
+			H_DABRX_KERNEL | H_DABRX_USER);
+}
 
 /*
  * Early initialization.  Relocation is on but do not reference unbolted pages
@@ -396,8 +398,10 @@
 		DBG("Hello World !\n");
 	}
 
-	if (firmware_has_feature(FW_FEATURE_XDABR | FW_FEATURE_DABR))
+	if (firmware_has_feature(FW_FEATURE_DABR))
 		ppc_md.set_dabr = pseries_set_dabr;
+	else if (firmware_has_feature(FW_FEATURE_XDABR))
+		ppc_md.set_dabr = pseries_set_xdabr;
 
 	iommu_init_early_pSeries();
 
diff --git a/arch/powerpc/sysdev/i8259.c b/arch/powerpc/sysdev/i8259.c
index 90bce6e..b7ac32f 100644
--- a/arch/powerpc/sysdev/i8259.c
+++ b/arch/powerpc/sysdev/i8259.c
@@ -207,6 +207,9 @@
 
 	spin_unlock_irqrestore(&i8259_lock, flags);
 
+	for (i = 0; i < NUM_ISA_INTERRUPTS; ++i)
+		irq_desc[offset + i].handler = &i8259_pic;
+
 	/* reserve our resources */
 	setup_irq(offset + 2, &i8259_irqaction);
 	request_resource(&ioport_resource, &pic1_iores);
@@ -216,6 +219,4 @@
 	if (intack_addr != 0)
 		pci_intack = ioremap(intack_addr, 1);
 
-	for (i = 0; i < NUM_ISA_INTERRUPTS; ++i)
-		irq_desc[offset + i].handler = &i8259_pic;
 }
diff --git a/arch/powerpc/sysdev/u3_iommu.c b/arch/powerpc/sysdev/u3_iommu.c
index 6077221..543d659 100644
--- a/arch/powerpc/sysdev/u3_iommu.c
+++ b/arch/powerpc/sysdev/u3_iommu.c
@@ -37,7 +37,6 @@
 #include <linux/vmalloc.h>
 #include <asm/io.h>
 #include <asm/prom.h>
-#include <asm/ppcdebug.h>
 #include <asm/iommu.h>
 #include <asm/pci-bridge.h>
 #include <asm/machdep.h>
diff --git a/arch/ppc/kernel/misc.S b/arch/ppc/kernel/misc.S
index 3056ede..ae6af29 100644
--- a/arch/ppc/kernel/misc.S
+++ b/arch/ppc/kernel/misc.S
@@ -25,6 +25,11 @@
 #include <asm/thread_info.h>
 #include <asm/asm-offsets.h>
 
+#ifdef CONFIG_8xx
+#define ISYNC_8xx isync
+#else
+#define ISYNC_8xx
+#endif
 	.text
 
 	.align	5
@@ -800,8 +805,18 @@
 	subi	r4,r4,1
 	blelr-
 00:	lbz	r5,0(r3)
-	eieio
-	stbu	r5,1(r4)
+01:	eieio
+02:	stbu	r5,1(r4)
+	ISYNC_8xx
+	.section .fixup,"ax"
+03:	blr
+	.text
+	.section __ex_table, "a"
+		.align 2
+		.long 00b, 03b
+		.long 01b, 03b
+		.long 02b, 03b
+	.text
 	bdnz	00b
 	blr
 
@@ -811,8 +826,18 @@
 	subi	r4,r4,1
 	blelr-
 00:	lbzu	r5,1(r4)
-	stb	r5,0(r3)
-	eieio
+01:	stb	r5,0(r3)
+02:	eieio
+	ISYNC_8xx
+	.section .fixup,"ax"
+03:	blr
+	.text
+	.section __ex_table, "a"
+		.align 2
+		.long 00b, 03b
+		.long 01b, 03b
+		.long 02b, 03b
+	.text
 	bdnz	00b
 	blr
 
@@ -822,8 +847,18 @@
 	subi	r4,r4,2
 	blelr-
 00:	lhbrx	r5,0,r3
-	eieio
-	sthu	r5,2(r4)
+01:	eieio
+02:	sthu	r5,2(r4)
+	ISYNC_8xx
+	.section .fixup,"ax"
+03:	blr
+	.text
+	.section __ex_table, "a"
+		.align 2
+		.long 00b, 03b
+		.long 01b, 03b
+		.long 02b, 03b
+	.text
 	bdnz	00b
 	blr
 
@@ -833,8 +868,18 @@
 	subi	r4,r4,2
 	blelr-
 00:	lhzu	r5,2(r4)
-	eieio
-	sthbrx	r5,0,r3
+01:	eieio
+02:	sthbrx	r5,0,r3
+	ISYNC_8xx
+	.section .fixup,"ax"
+03:	blr
+	.text
+	.section __ex_table, "a"
+		.align 2
+		.long 00b, 03b
+		.long 01b, 03b
+		.long 02b, 03b
+	.text
 	bdnz	00b
 	blr
 
@@ -844,8 +889,18 @@
 	subi	r4,r4,4
 	blelr-
 00:	lwbrx	r5,0,r3
-	eieio
-	stwu	r5,4(r4)
+01:	eieio
+02:	stwu	r5,4(r4)
+	ISYNC_8xx
+	.section .fixup,"ax"
+03:	blr
+	.text
+	.section __ex_table, "a"
+		.align 2
+		.long 00b, 03b
+		.long 01b, 03b
+		.long 02b, 03b
+	.text
 	bdnz	00b
 	blr
 
@@ -855,8 +910,18 @@
 	subi	r4,r4,4
 	blelr-
 00:	lwzu	r5,4(r4)
-	stwbrx	r5,0,r3
-	eieio
+01:	stwbrx	r5,0,r3
+02:	eieio
+	ISYNC_8xx
+	.section .fixup,"ax"
+03:	blr
+	.text
+	.section __ex_table, "a"
+		.align 2
+		.long 00b, 03b
+		.long 01b, 03b
+		.long 02b, 03b
+	.text
 	bdnz	00b
 	blr
 
@@ -867,8 +932,18 @@
 	subi	r4,r4,2
 	blelr-
 00:	lhz	r5,0(r3)
-	eieio
-	sthu	r5,2(r4)
+01:	eieio
+02:	sthu	r5,2(r4)
+	ISYNC_8xx
+	.section .fixup,"ax"
+03:	blr
+	.text
+	.section __ex_table, "a"
+		.align 2
+		.long 00b, 03b
+		.long 01b, 03b
+		.long 02b, 03b
+	.text
 	bdnz	00b
 	blr
 
@@ -879,8 +954,18 @@
 	subi	r4,r4,2
 	blelr-
 00:	lhzu	r5,2(r4)
-	sth	r5,0(r3)
-	eieio
+01:	sth	r5,0(r3)
+02:	eieio
+	ISYNC_8xx
+	.section .fixup,"ax"
+03:	blr
+	.text
+	.section __ex_table, "a"
+		.align 2
+		.long 00b, 03b
+		.long 01b, 03b
+		.long 02b, 03b
+	.text
 	bdnz	00b
 	blr
 
@@ -891,8 +976,18 @@
 	subi	r4,r4,4
 	blelr-
 00:	lwz	r5,0(r3)
-	eieio
-	stwu	r5,4(r4)
+01:	eieio
+02:	stwu	r5,4(r4)
+	ISYNC_8xx
+	.section .fixup,"ax"
+03:	blr
+	.text
+	.section __ex_table, "a"
+		.align 2
+		.long 00b, 03b
+		.long 01b, 03b
+		.long 02b, 03b
+	.text
 	bdnz	00b
 	blr
 
@@ -903,8 +998,18 @@
 	subi	r4,r4,4
 	blelr-
 00:	lwzu	r5,4(r4)
-	stw	r5,0(r3)
-	eieio
+01:	stw	r5,0(r3)
+02:	eieio
+	ISYNC_8xx
+	.section .fixup,"ax"
+03:	blr
+	.text
+	.section __ex_table, "a"
+		.align 2
+		.long 00b, 03b
+		.long 01b, 03b
+		.long 02b, 03b
+	.text
 	bdnz	00b
 	blr
 
diff --git a/arch/ppc/kernel/traps.c b/arch/ppc/kernel/traps.c
index 16adde6..9dbc4d2 100644
--- a/arch/ppc/kernel/traps.c
+++ b/arch/ppc/kernel/traps.c
@@ -49,7 +49,7 @@
 extern int xmon_iabr_match(struct pt_regs *regs);
 extern int xmon_dabr_match(struct pt_regs *regs);
 
-void (*debugger)(struct pt_regs *regs) = xmon;
+int (*debugger)(struct pt_regs *regs) = xmon;
 int (*debugger_bpt)(struct pt_regs *regs) = xmon_bpt;
 int (*debugger_sstep)(struct pt_regs *regs) = xmon_sstep;
 int (*debugger_iabr_match)(struct pt_regs *regs) = xmon_iabr_match;
@@ -57,7 +57,7 @@
 void (*debugger_fault_handler)(struct pt_regs *regs);
 #else
 #ifdef CONFIG_KGDB
-void (*debugger)(struct pt_regs *regs);
+int (*debugger)(struct pt_regs *regs);
 int (*debugger_bpt)(struct pt_regs *regs);
 int (*debugger_sstep)(struct pt_regs *regs);
 int (*debugger_iabr_match)(struct pt_regs *regs);
@@ -159,7 +159,7 @@
  */
 static inline int check_io_access(struct pt_regs *regs)
 {
-#ifdef CONFIG_PPC_PMAC
+#if defined CONFIG_PPC_PMAC || defined CONFIG_8xx
 	unsigned long msr = regs->msr;
 	const struct exception_table_entry *entry;
 	unsigned int *nip = (unsigned int *)regs->nip;
@@ -178,7 +178,11 @@
 			nip -= 2;
 		else if (*nip == 0x4c00012c)	/* isync */
 			--nip;
-		if (*nip == 0x7c0004ac || (*nip >> 26) == 3) {
+		/* eieio from I/O string functions */
+		else if ((*nip) == 0x7c0006ac || *(nip+1) == 0x7c0006ac)
+			nip += 2;
+		if (*nip == 0x7c0004ac || (*nip >> 26) == 3 ||
+			(*(nip+1) >> 26) == 3) {
 			/* sync or twi */
 			unsigned int rb;
 
diff --git a/arch/ppc/syslib/m8xx_wdt.c b/arch/ppc/syslib/m8xx_wdt.c
index c5ac5ce..a21632d 100644
--- a/arch/ppc/syslib/m8xx_wdt.c
+++ b/arch/ppc/syslib/m8xx_wdt.c
@@ -14,6 +14,7 @@
 #include <linux/irq.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
+#include <asm/io.h>
 #include <asm/8xx_immap.h>
 #include <syslib/m8xx_wdt.h>
 
@@ -29,8 +30,8 @@
 {
 	volatile immap_t *imap = (volatile immap_t *)IMAP_ADDR;
 
-	out_be16(imap->im_siu_conf.sc_swsr, 0x556c);	/* write magic1 */
-	out_be16(imap->im_siu_conf.sc_swsr, 0xaa39);	/* write magic2 */
+	out_be16(&imap->im_siu_conf.sc_swsr, 0x556c);	/* write magic1 */
+	out_be16(&imap->im_siu_conf.sc_swsr, 0xaa39);	/* write magic2 */
 }
 
 static irqreturn_t m8xx_wdt_interrupt(int irq, void *dev, struct pt_regs *regs)
@@ -39,7 +40,7 @@
 
 	m8xx_wdt_reset();
 
-	out_be16(imap->im_sit.sit_piscr, in_be16(imap->im_sit.sit_piscr | PISCR_PS));	/* clear irq */
+	out_be16(&imap->im_sit.sit_piscr, in_be16(&imap->im_sit.sit_piscr) | PISCR_PS);	/* clear irq */
 
 	return IRQ_HANDLED;
 }
@@ -51,7 +52,7 @@
 	u32 sypcr;
 	u32 pitrtclk;
 
-	sypcr = in_be32(imap->im_siu_conf.sc_sypcr);
+	sypcr = in_be32(&imap->im_siu_conf.sc_sypcr);
 
 	if (!(sypcr & 0x04)) {
 		printk(KERN_NOTICE "m8xx_wdt: wdt disabled (SYPCR: 0x%08X)\n",
@@ -87,9 +88,9 @@
 	else
 		pitc = pitrtclk * wdt_timeout / binfo->bi_intfreq / 2;
 
-	out_be32(imap->im_sit.sit_pitc, pitc << 16);
+	out_be32(&imap->im_sit.sit_pitc, pitc << 16);
 
-	out_be16(imap->im_sit.sit_piscr, (mk_int_int_mask(PIT_INTERRUPT) << 8) | PISCR_PIE | PISCR_PTE);
+	out_be16(&imap->im_sit.sit_piscr, (mk_int_int_mask(PIT_INTERRUPT) << 8) | PISCR_PIE | PISCR_PTE);
 
 	if (setup_irq(PIT_INTERRUPT, &m8xx_wdt_irqaction))
 		panic("m8xx_wdt: error setting up the watchdog irq!");
diff --git a/arch/ppc/syslib/prom.c b/arch/ppc/syslib/prom.c
index 1b9aa0d..03b1fc9 100644
--- a/arch/ppc/syslib/prom.c
+++ b/arch/ppc/syslib/prom.c
@@ -1165,7 +1165,7 @@
 /*
  * Add a property to a node
  */
-void
+int
 prom_add_property(struct device_node* np, struct property* prop)
 {
 	struct property **next = &np->properties;
@@ -1174,6 +1174,8 @@
 	while (*next)
 		next = &(*next)->next;
 	*next = prop;
+
+	return 0;
 }
 
 /* I quickly hacked that one, check against spec ! */
diff --git a/arch/ppc/xmon/xmon.c b/arch/ppc/xmon/xmon.c
index 66bfaa3..2b483b4 100644
--- a/arch/ppc/xmon/xmon.c
+++ b/arch/ppc/xmon/xmon.c
@@ -220,8 +220,7 @@
 	p[1] = lo;
 }
 
-void
-xmon(struct pt_regs *excp)
+int xmon(struct pt_regs *excp)
 {
 	struct pt_regs regs;
 	int msr, cmd;
@@ -290,6 +289,8 @@
 #endif /* CONFIG_SMP */
 	set_msr(msr);		/* restore interrupt enable */
 	get_tb(start_tb[smp_processor_id()]);
+
+	return cmd != 'X';
 }
 
 irqreturn_t
diff --git a/arch/ppc64/Kconfig b/arch/ppc64/Kconfig
index 2130cc3..2955234 100644
--- a/arch/ppc64/Kconfig
+++ b/arch/ppc64/Kconfig
@@ -56,6 +56,7 @@
 # max order + 1
 config FORCE_MAX_ZONEORDER
 	int
+	default "9" if PPC_64K_PAGES
 	default "13"
 
 source "init/Kconfig"
@@ -173,6 +174,16 @@
 	  support.  As of this writing the exact hardware interface is
 	  strongly in flux, so no good recommendation can be made.
 
+source "drivers/cpufreq/Kconfig"
+
+config CPU_FREQ_PMAC64
+	bool "Support for some Apple G5s"
+	depends on CPU_FREQ && PMAC_SMU && PPC64
+	select CPU_FREQ_TABLE
+	help
+	  This adds support for frequency switching on Apple iMac G5,
+	  and some of the more recent desktop G5 machines as well.
+
 config IBMVIO
 	depends on PPC_PSERIES || PPC_ISERIES
 	bool
diff --git a/arch/ppc64/Kconfig.debug b/arch/ppc64/Kconfig.debug
index f16a503..b258c93 100644
--- a/arch/ppc64/Kconfig.debug
+++ b/arch/ppc64/Kconfig.debug
@@ -55,10 +55,6 @@
 	  xmon is normally disabled unless booted with 'xmon=on'.
 	  Use 'xmon=off' to disable xmon init during runtime.
 
-config PPCDBG
-	bool "Include PPCDBG realtime debugging"
-	depends on DEBUG_KERNEL
-
 config IRQSTACKS
 	bool "Use separate kernel stacks when processing interrupts"
 	help
diff --git a/arch/ppc64/kernel/idle.c b/arch/ppc64/kernel/idle.c
index 8abd2ad..8fec274 100644
--- a/arch/ppc64/kernel/idle.c
+++ b/arch/ppc64/kernel/idle.c
@@ -28,6 +28,7 @@
 #include <asm/time.h>
 #include <asm/systemcfg.h>
 #include <asm/machdep.h>
+#include <asm/smp.h>
 
 extern void power4_idle(void);
 
diff --git a/arch/ppc64/kernel/machine_kexec.c b/arch/ppc64/kernel/machine_kexec.c
index ff8679f..07ea035 100644
--- a/arch/ppc64/kernel/machine_kexec.c
+++ b/arch/ppc64/kernel/machine_kexec.c
@@ -24,6 +24,7 @@
 #include <asm/mmu.h>
 #include <asm/sections.h>	/* _end */
 #include <asm/prom.h>
+#include <asm/smp.h>
 
 #define HASH_GROUP_SIZE 0x80	/* size of each hash group, asm/mmu.h */
 
diff --git a/arch/ppc64/kernel/misc.S b/arch/ppc64/kernel/misc.S
index 077507f..914632e 100644
--- a/arch/ppc64/kernel/misc.S
+++ b/arch/ppc64/kernel/misc.S
@@ -560,7 +560,7 @@
 	isync
 	blr
 
-	/*
+/*
  * Do an IO access in real mode
  */
 _GLOBAL(real_writeb)
@@ -593,6 +593,76 @@
 #endif /* defined(CONFIG_PPC_PMAC) || defined(CONFIG_PPC_MAPLE) */
 
 /*
+ * SCOM access functions for 970 (FX only for now)
+ *
+ * unsigned long scom970_read(unsigned int address);
+ * void scom970_write(unsigned int address, unsigned long value);
+ *
+ * The address passed in is the 24 bits register address. This code
+ * is 970 specific and will not check the status bits, so you should
+ * know what you are doing.
+ */
+_GLOBAL(scom970_read)
+	/* interrupts off */
+	mfmsr	r4
+	ori	r0,r4,MSR_EE
+	xori	r0,r0,MSR_EE
+	mtmsrd	r0,1
+
+	/* rotate 24 bits SCOM address 8 bits left and mask out it's low 8 bits
+	 * (including parity). On current CPUs they must be 0'd,
+	 * and finally or in RW bit
+	 */
+	rlwinm	r3,r3,8,0,15
+	ori	r3,r3,0x8000
+
+	/* do the actual scom read */
+	sync
+	mtspr	SPRN_SCOMC,r3
+	isync
+	mfspr	r3,SPRN_SCOMD
+	isync
+	mfspr	r0,SPRN_SCOMC
+	isync
+
+	/* XXX:	fixup result on some buggy 970's (ouch ! we lost a bit, bah
+	 * that's the best we can do). Not implemented yet as we don't use
+	 * the scom on any of the bogus CPUs yet, but may have to be done
+	 * ultimately
+	 */
+
+	/* restore interrupts */
+	mtmsrd	r4,1
+	blr
+
+
+_GLOBAL(scom970_write)
+	/* interrupts off */
+	mfmsr	r5
+	ori	r0,r5,MSR_EE
+	xori	r0,r0,MSR_EE
+	mtmsrd	r0,1
+
+	/* rotate 24 bits SCOM address 8 bits left and mask out it's low 8 bits
+	 * (including parity). On current CPUs they must be 0'd.
+	 */
+
+	rlwinm	r3,r3,8,0,15
+
+	sync
+	mtspr	SPRN_SCOMD,r4      /* write data */
+	isync
+	mtspr	SPRN_SCOMC,r3      /* write command */
+	isync
+	mfspr	3,SPRN_SCOMC
+	isync
+
+	/* restore interrupts */
+	mtmsrd	r5,1
+	blr
+
+
+/*
  * Create a kernel thread
  *   kernel_thread(fn, arg, flags)
  */
diff --git a/arch/ppc64/kernel/pci.c b/arch/ppc64/kernel/pci.c
index 3d2106b..30247ff 100644
--- a/arch/ppc64/kernel/pci.c
+++ b/arch/ppc64/kernel/pci.c
@@ -295,8 +295,8 @@
 	}
 }
 
-static struct pci_dev *of_create_pci_dev(struct device_node *node,
-					 struct pci_bus *bus, int devfn)
+struct pci_dev *of_create_pci_dev(struct device_node *node,
+				 struct pci_bus *bus, int devfn)
 {
 	struct pci_dev *dev;
 	const char *type;
@@ -354,10 +354,9 @@
 
 	return dev;
 }
+EXPORT_SYMBOL(of_create_pci_dev);
 
-static void of_scan_pci_bridge(struct device_node *node, struct pci_dev *dev);
-
-static void __devinit of_scan_bus(struct device_node *node,
+void __devinit of_scan_bus(struct device_node *node,
 				  struct pci_bus *bus)
 {
 	struct device_node *child = NULL;
@@ -381,9 +380,10 @@
 
 	do_bus_setup(bus);
 }
+EXPORT_SYMBOL(of_scan_bus);
 
-static void __devinit of_scan_pci_bridge(struct device_node *node,
-					 struct pci_dev *dev)
+void __devinit of_scan_pci_bridge(struct device_node *node,
+			 	struct pci_dev *dev)
 {
 	struct pci_bus *bus;
 	u32 *busrange, *ranges;
@@ -464,9 +464,10 @@
 	else if (mode == PCI_PROBE_NORMAL)
 		pci_scan_child_bus(bus);
 }
+EXPORT_SYMBOL(of_scan_pci_bridge);
 #endif /* CONFIG_PPC_MULTIPLATFORM */
 
-static void __devinit scan_phb(struct pci_controller *hose)
+void __devinit scan_phb(struct pci_controller *hose)
 {
 	struct pci_bus *bus;
 	struct device_node *node = hose->arch_data;
diff --git a/arch/ppc64/kernel/prom.c b/arch/ppc64/kernel/prom.c
index dece31e..3402fbe 100644
--- a/arch/ppc64/kernel/prom.c
+++ b/arch/ppc64/kernel/prom.c
@@ -31,6 +31,7 @@
 #include <linux/initrd.h>
 #include <linux/bitops.h>
 #include <linux/module.h>
+#include <linux/module.h>
 
 #include <asm/prom.h>
 #include <asm/rtas.h>
@@ -46,7 +47,6 @@
 #include <asm/pgtable.h>
 #include <asm/pci.h>
 #include <asm/iommu.h>
-#include <asm/ppcdebug.h>
 #include <asm/btext.h>
 #include <asm/sections.h>
 #include <asm/machdep.h>
@@ -1866,17 +1866,32 @@
 EXPORT_SYMBOL(get_property);
 
 /*
- * Add a property to a node
+ * Add a property to a node.
  */
-void
+int
 prom_add_property(struct device_node* np, struct property* prop)
 {
-	struct property **next = &np->properties;
+	struct property **next;
 
 	prop->next = NULL;	
-	while (*next)
+	write_lock(&devtree_lock);
+	next = &np->properties;
+	while (*next) {
+		if (strcmp(prop->name, (*next)->name) == 0) {
+			/* duplicate ! don't insert it */
+			write_unlock(&devtree_lock);
+			return -1;
+		}
 		next = &(*next)->next;
+	}
 	*next = prop;
+	write_unlock(&devtree_lock);
+
+	/* try to add to proc as well if it was initialized */
+	if (np->pde)
+		proc_device_tree_add_prop(np->pde, prop);
+
+	return 0;
 }
 
 #if 0
diff --git a/arch/ppc64/kernel/prom_init.c b/arch/ppc64/kernel/prom_init.c
index a4bbca6..e4c880da 100644
--- a/arch/ppc64/kernel/prom_init.c
+++ b/arch/ppc64/kernel/prom_init.c
@@ -44,7 +44,6 @@
 #include <asm/pgtable.h>
 #include <asm/pci.h>
 #include <asm/iommu.h>
-#include <asm/ppcdebug.h>
 #include <asm/btext.h>
 #include <asm/sections.h>
 #include <asm/machdep.h>
@@ -1825,7 +1824,7 @@
 	if (prom_getprop(u3, "device-rev", &u3_rev, sizeof(u3_rev))
 	    == PROM_ERROR)
 		return;
-	if (u3_rev != 0x35 && u3_rev != 0x37)
+	if (u3_rev < 0x35 || u3_rev > 0x39)
 		return;
 	/* does it need fixup ? */
 	if (prom_getproplen(i2c, "interrupts") > 0)
diff --git a/arch/ppc64/kernel/rtas_pci.c b/arch/ppc64/kernel/rtas_pci.c
index 3ad15c9..3c3f191 100644
--- a/arch/ppc64/kernel/rtas_pci.c
+++ b/arch/ppc64/kernel/rtas_pci.c
@@ -440,7 +440,6 @@
 	struct device_node *root = of_find_node_by_path("/");
 	unsigned int root_size_cells = 0;
 	struct pci_controller *phb;
-	struct pci_bus *bus;
 	int primary;
 
 	root_size_cells = prom_n_size_cells(root);
@@ -456,10 +455,7 @@
 	of_node_put(root);
 
 	pci_devs_phb_init_dynamic(phb);
-	phb->last_busno = 0xff;
-	bus = pci_scan_bus(phb->first_busno, phb->ops, phb->arch_data);
-	phb->bus = bus;
-	phb->last_busno = bus->subordinate;
+	scan_phb(phb);
 
 	return phb;
 }
diff --git a/arch/ppc64/kernel/udbg.c b/arch/ppc64/kernel/udbg.c
index d49c361..0d878e7 100644
--- a/arch/ppc64/kernel/udbg.c
+++ b/arch/ppc64/kernel/udbg.c
@@ -10,12 +10,10 @@
  */
 
 #include <stdarg.h>
-#define WANT_PPCDBG_TAB /* Only defined here */
 #include <linux/config.h>
 #include <linux/types.h>
 #include <linux/sched.h>
 #include <linux/console.h>
-#include <asm/ppcdebug.h>
 #include <asm/processor.h>
 
 void (*udbg_putc)(unsigned char c);
@@ -89,59 +87,6 @@
 	va_end(args);
 }
 
-/* PPCDBG stuff */
-
-u64 ppc64_debug_switch;
-
-/* Special print used by PPCDBG() macro */
-void udbg_ppcdbg(unsigned long debug_flags, const char *fmt, ...)
-{
-	unsigned long active_debugs = debug_flags & ppc64_debug_switch;
-
-	if (active_debugs) {
-		va_list ap;
-		unsigned char buf[UDBG_BUFSIZE];
-		unsigned long i, len = 0;
-
-		for (i=0; i < PPCDBG_NUM_FLAGS; i++) {
-			if (((1U << i) & active_debugs) && 
-			    trace_names[i]) {
-				len += strlen(trace_names[i]); 
-				udbg_puts(trace_names[i]);
-				break;
-			}
-		}
-
-		snprintf(buf, UDBG_BUFSIZE, " [%s]: ", current->comm);
-		len += strlen(buf); 
-		udbg_puts(buf);
-
-		while (len < 18) {
-			udbg_puts(" ");
-			len++;
-		}
-
-		va_start(ap, fmt);
-		vsnprintf(buf, UDBG_BUFSIZE, fmt, ap);
-		udbg_puts(buf);
-		va_end(ap);
-	}
-}
-
-unsigned long udbg_ifdebug(unsigned long flags)
-{
-	return (flags & ppc64_debug_switch);
-}
-
-/*
- * Initialize the PPCDBG state.  Called before relocation has been enabled.
- */
-void __init ppcdbg_initialize(void)
-{
-	ppc64_debug_switch = PPC_DEBUG_DEFAULT; /* | PPCDBG_BUSWALK | */
-	/* PPCDBG_PHBINIT | PPCDBG_MM | PPCDBG_MMINIT | PPCDBG_TCEINIT | PPCDBG_TCE */;
-}
-
 /*
  * Early boot console based on udbg
  */
diff --git a/drivers/block/swim3.c b/drivers/block/swim3.c
index e425ad3..af7cb2b 100644
--- a/drivers/block/swim3.c
+++ b/drivers/block/swim3.c
@@ -28,6 +28,7 @@
 #include <linux/devfs_fs_kernel.h>
 #include <linux/interrupt.h>
 #include <linux/module.h>
+#include <linux/spinlock.h>
 #include <asm/io.h>
 #include <asm/dbdma.h>
 #include <asm/prom.h>
@@ -176,6 +177,7 @@
 
 struct floppy_state {
 	enum swim_state	state;
+	spinlock_t lock;
 	struct swim3 __iomem *swim3;	/* hardware registers */
 	struct dbdma_regs __iomem *dma;	/* DMA controller registers */
 	int	swim3_intr;	/* interrupt number for SWIM3 */
@@ -304,7 +306,6 @@
 #endif /* CONFIG_PMAC_MEDIABAY */
 		start_request(&floppy_states[i]);
 	}
-	sti();
 }
 
 static void start_request(struct floppy_state *fs)
@@ -370,7 +371,7 @@
 {
 	unsigned long flags;
 
-	save_flags(flags); cli();
+	spin_lock_irqsave(&fs->lock, flags);
 	if (fs->timeout_pending)
 		del_timer(&fs->timeout);
 	fs->timeout.expires = jiffies + nticks;
@@ -378,7 +379,7 @@
 	fs->timeout.data = (unsigned long) fs;
 	add_timer(&fs->timeout);
 	fs->timeout_pending = 1;
-	restore_flags(flags);
+	spin_unlock_irqrestore(&fs->lock, flags);
 }
 
 static inline void scan_track(struct floppy_state *fs)
@@ -790,14 +791,13 @@
 {
 	unsigned long flags;
 
-	save_flags(flags);
-	cli();
+	spin_lock_irqsave(&fs->lock, flags);
 	if (fs->state != idle) {
 		++fs->wanted;
 		while (fs->state != available) {
 			if (interruptible && signal_pending(current)) {
 				--fs->wanted;
-				restore_flags(flags);
+				spin_unlock_irqrestore(&fs->lock, flags);
 				return -EINTR;
 			}
 			interruptible_sleep_on(&fs->wait);
@@ -805,7 +805,7 @@
 		--fs->wanted;
 	}
 	fs->state = state;
-	restore_flags(flags);
+	spin_unlock_irqrestore(&fs->lock, flags);
 	return 0;
 }
 
@@ -813,11 +813,10 @@
 {
 	unsigned long flags;
 
-	save_flags(flags);
-	cli();
+	spin_lock_irqsave(&fs->lock, flags);
 	fs->state = idle;
 	start_request(fs);
-	restore_flags(flags);
+	spin_unlock_irqrestore(&fs->lock, flags);
 }
 
 static int fd_eject(struct floppy_state *fs)
@@ -1109,6 +1108,7 @@
 		pmac_call_feature(PMAC_FTR_SWIM3_ENABLE, swim, 0, 1);
 	
 	memset(fs, 0, sizeof(*fs));
+	spin_lock_init(&fs->lock);
 	fs->state = idle;
 	fs->swim3 = (struct swim3 __iomem *)
 		ioremap(swim->addrs[0].address, 0x200);
diff --git a/drivers/ide/ppc/pmac.c b/drivers/ide/ppc/pmac.c
index d8c3d8e..b3e65a6 100644
--- a/drivers/ide/ppc/pmac.c
+++ b/drivers/ide/ppc/pmac.c
@@ -497,16 +497,19 @@
 	if (pmu_get_model() != PMU_KEYLARGO_BASED)
 		return 0;
 	
-	dt = find_devices("device-tree");
+	dt = of_find_node_by_path("/");
 	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)
+	    strncmp(model, "iBook", strlen("iBook")) != 0) {
+		of_node_put(dt);
 	    	return 0;
-	
+	}
+	of_node_put(dt);
+
 	pmu_blink_on.complete = 1;
 	pmu_blink_off.complete = 1;
 	spin_lock_init(&pmu_blink_lock);
diff --git a/drivers/macintosh/Kconfig b/drivers/macintosh/Kconfig
index bc3e096..a0ea44c 100644
--- a/drivers/macintosh/Kconfig
+++ b/drivers/macintosh/Kconfig
@@ -169,6 +169,25 @@
 	  This driver provides thermostat and fan control for the desktop
 	  G5 machines. 
 
+config WINDFARM
+	tristate "New PowerMac thermal control infrastructure"
+
+config WINDFARM_PM81
+	tristate "Support for thermal management on iMac G5"
+	depends on WINDFARM && I2C && CPU_FREQ_PMAC64 && PMAC_SMU
+	select I2C_PMAC_SMU
+	help
+	  This driver provides thermal control for the iMacG5
+
+config WINDFARM_PM91
+	tristate "Support for thermal management on PowerMac9,1"
+	depends on WINDFARM && I2C && CPU_FREQ_PMAC64 && PMAC_SMU
+	select I2C_PMAC_SMU
+	help
+	  This driver provides thermal control for the PowerMac9,1
+          which is the recent (SMU based) single CPU desktop G5
+
+
 config ANSLCD
 	tristate "Support for ANS LCD display"
 	depends on ADB_CUDA && PPC_PMAC
diff --git a/drivers/macintosh/Makefile b/drivers/macintosh/Makefile
index 236291b..f4657aa 100644
--- a/drivers/macintosh/Makefile
+++ b/drivers/macintosh/Makefile
@@ -26,3 +26,12 @@
 obj-$(CONFIG_THERM_PM72)	+= therm_pm72.o
 obj-$(CONFIG_THERM_WINDTUNNEL)	+= therm_windtunnel.o
 obj-$(CONFIG_THERM_ADT746X)	+= therm_adt746x.o
+obj-$(CONFIG_WINDFARM)	        += windfarm_core.o
+obj-$(CONFIG_WINDFARM_PM81)     += windfarm_smu_controls.o \
+				   windfarm_smu_sensors.o \
+				   windfarm_lm75_sensor.o windfarm_pid.o \
+				   windfarm_cpufreq_clamp.o windfarm_pm81.o
+obj-$(CONFIG_WINDFARM_PM91)     += windfarm_smu_controls.o \
+				   windfarm_smu_sensors.o \
+				   windfarm_lm75_sensor.o windfarm_pid.o \
+				   windfarm_cpufreq_clamp.o windfarm_pm91.o
diff --git a/drivers/macintosh/smu.c b/drivers/macintosh/smu.c
index 34f3c7e..e837827 100644
--- a/drivers/macintosh/smu.c
+++ b/drivers/macintosh/smu.c
@@ -47,13 +47,13 @@
 #include <asm/uaccess.h>
 #include <asm/of_device.h>
 
-#define VERSION "0.6"
+#define VERSION "0.7"
 #define AUTHOR  "(c) 2005 Benjamin Herrenschmidt, IBM Corp."
 
 #undef DEBUG_SMU
 
 #ifdef DEBUG_SMU
-#define DPRINTK(fmt, args...) do { printk(KERN_DEBUG fmt , ##args); } while (0)
+#define DPRINTK(fmt, args...) do { udbg_printf(KERN_DEBUG fmt , ##args); } while (0)
 #else
 #define DPRINTK(fmt, args...) do { } while (0)
 #endif
@@ -92,7 +92,7 @@
  * for now, just hard code that
  */
 static struct smu_device	*smu;
-
+static DECLARE_MUTEX(smu_part_access);
 
 /*
  * SMU driver low level stuff
@@ -113,9 +113,11 @@
 
 	DPRINTK("SMU: starting cmd %x, %d bytes data\n", cmd->cmd,
 		cmd->data_len);
-	DPRINTK("SMU: data buffer: %02x %02x %02x %02x ...\n",
+	DPRINTK("SMU: data buffer: %02x %02x %02x %02x %02x %02x %02x %02x\n",
 		((u8 *)cmd->data_buf)[0], ((u8 *)cmd->data_buf)[1],
-		((u8 *)cmd->data_buf)[2], ((u8 *)cmd->data_buf)[3]);
+		((u8 *)cmd->data_buf)[2], ((u8 *)cmd->data_buf)[3],
+		((u8 *)cmd->data_buf)[4], ((u8 *)cmd->data_buf)[5],
+		((u8 *)cmd->data_buf)[6], ((u8 *)cmd->data_buf)[7]);
 
 	/* Fill the SMU command buffer */
 	smu->cmd_buf->cmd = cmd->cmd;
@@ -440,7 +442,7 @@
 EXPORT_SYMBOL(smu_present);
 
 
-int smu_init (void)
+int __init smu_init (void)
 {
 	struct device_node *np;
 	u32 *data;
@@ -588,6 +590,8 @@
 			sprintf(name, "smu-i2c-%02x", *reg);
 			of_platform_device_create(np, name, &smu->of_dev->dev);
 		}
+		if (device_is_compatible(np, "smu-sensors"))
+			of_platform_device_create(np, "smu-sensors", &smu->of_dev->dev);
 	}
 
 }
@@ -845,6 +849,156 @@
 	return 0;
 }
 
+/*
+ * Handling of "partitions"
+ */
+
+static int smu_read_datablock(u8 *dest, unsigned int addr, unsigned int len)
+{
+	DECLARE_COMPLETION(comp);
+	unsigned int chunk;
+	struct smu_cmd cmd;
+	int rc;
+	u8 params[8];
+
+	/* We currently use a chunk size of 0xe. We could check the
+	 * SMU firmware version and use bigger sizes though
+	 */
+	chunk = 0xe;
+
+	while (len) {
+		unsigned int clen = min(len, chunk);
+
+		cmd.cmd = SMU_CMD_MISC_ee_COMMAND;
+		cmd.data_len = 7;
+		cmd.data_buf = params;
+		cmd.reply_len = chunk;
+		cmd.reply_buf = dest;
+		cmd.done = smu_done_complete;
+		cmd.misc = &comp;
+		params[0] = SMU_CMD_MISC_ee_GET_DATABLOCK_REC;
+		params[1] = 0x4;
+		*((u32 *)&params[2]) = addr;
+		params[6] = clen;
+
+		rc = smu_queue_cmd(&cmd);
+		if (rc)
+			return rc;
+		wait_for_completion(&comp);
+		if (cmd.status != 0)
+			return rc;
+		if (cmd.reply_len != clen) {
+			printk(KERN_DEBUG "SMU: short read in "
+			       "smu_read_datablock, got: %d, want: %d\n",
+			       cmd.reply_len, clen);
+			return -EIO;
+		}
+		len -= clen;
+		addr += clen;
+		dest += clen;
+	}
+	return 0;
+}
+
+static struct smu_sdbp_header *smu_create_sdb_partition(int id)
+{
+	DECLARE_COMPLETION(comp);
+	struct smu_simple_cmd cmd;
+	unsigned int addr, len, tlen;
+	struct smu_sdbp_header *hdr;
+	struct property *prop;
+
+	/* First query the partition info */
+	smu_queue_simple(&cmd, SMU_CMD_PARTITION_COMMAND, 2,
+			 smu_done_complete, &comp,
+			 SMU_CMD_PARTITION_LATEST, id);
+	wait_for_completion(&comp);
+
+	/* Partition doesn't exist (or other error) */
+	if (cmd.cmd.status != 0 || cmd.cmd.reply_len != 6)
+		return NULL;
+
+	/* Fetch address and length from reply */
+	addr = *((u16 *)cmd.buffer);
+	len = cmd.buffer[3] << 2;
+	/* Calucluate total length to allocate, including the 17 bytes
+	 * for "sdb-partition-XX" that we append at the end of the buffer
+	 */
+	tlen = sizeof(struct property) + len + 18;
+
+	prop = kcalloc(tlen, 1, GFP_KERNEL);
+	if (prop == NULL)
+		return NULL;
+	hdr = (struct smu_sdbp_header *)(prop + 1);
+	prop->name = ((char *)prop) + tlen - 18;
+	sprintf(prop->name, "sdb-partition-%02x", id);
+	prop->length = len;
+	prop->value = (unsigned char *)hdr;
+	prop->next = NULL;
+
+	/* Read the datablock */
+	if (smu_read_datablock((u8 *)hdr, addr, len)) {
+		printk(KERN_DEBUG "SMU: datablock read failed while reading "
+		       "partition %02x !\n", id);
+		goto failure;
+	}
+
+	/* Got it, check a few things and create the property */
+	if (hdr->id != id) {
+		printk(KERN_DEBUG "SMU: Reading partition %02x and got "
+		       "%02x !\n", id, hdr->id);
+		goto failure;
+	}
+	if (prom_add_property(smu->of_node, prop)) {
+		printk(KERN_DEBUG "SMU: Failed creating sdb-partition-%02x "
+		       "property !\n", id);
+		goto failure;
+	}
+
+	return hdr;
+ failure:
+	kfree(prop);
+	return NULL;
+}
+
+/* Note: Only allowed to return error code in pointers (using ERR_PTR)
+ * when interruptible is 1
+ */
+struct smu_sdbp_header *__smu_get_sdb_partition(int id, unsigned int *size,
+						int interruptible)
+{
+	char pname[32];
+	struct smu_sdbp_header *part;
+
+	if (!smu)
+		return NULL;
+
+	sprintf(pname, "sdb-partition-%02x", id);
+
+	if (interruptible) {
+		int rc;
+		rc = down_interruptible(&smu_part_access);
+		if (rc)
+			return ERR_PTR(rc);
+	} else
+		down(&smu_part_access);
+
+	part = (struct smu_sdbp_header *)get_property(smu->of_node,
+						      pname, size);
+	if (part == NULL) {
+		part = smu_create_sdb_partition(id);
+		if (part != NULL && size)
+			*size = part->len << 2;
+	}
+	up(&smu_part_access);
+	return part;
+}
+
+struct smu_sdbp_header *smu_get_sdb_partition(int id, unsigned int *size)
+{
+	return __smu_get_sdb_partition(id, size, 0);
+}
+EXPORT_SYMBOL(smu_get_sdb_partition);
 
 
 /*
@@ -918,6 +1072,14 @@
 	else if (hdr.cmdtype == SMU_CMDTYPE_WANTS_EVENTS) {
 		pp->mode = smu_file_events;
 		return 0;
+	} else if (hdr.cmdtype == SMU_CMDTYPE_GET_PARTITION) {
+		struct smu_sdbp_header *part;
+		part = __smu_get_sdb_partition(hdr.cmd, NULL, 1);
+		if (part == NULL)
+			return -EINVAL;
+		else if (IS_ERR(part))
+			return PTR_ERR(part);
+		return 0;
 	} else if (hdr.cmdtype != SMU_CMDTYPE_SMU)
 		return -EINVAL;
 	else if (pp->mode != smu_file_commands)
diff --git a/drivers/macintosh/via-pmu.c b/drivers/macintosh/via-pmu.c
index 9bc6cc6..5640435 100644
--- a/drivers/macintosh/via-pmu.c
+++ b/drivers/macintosh/via-pmu.c
@@ -2053,6 +2053,7 @@
 	__list_add(&n->list, list->prev, list);
 	return 0;
 }
+EXPORT_SYMBOL(pmu_register_sleep_notifier);
 
 int
 pmu_unregister_sleep_notifier(struct pmu_sleep_notifier* n)
@@ -2063,6 +2064,7 @@
 	n->list.next = NULL;
 	return 0;
 }
+EXPORT_SYMBOL(pmu_unregister_sleep_notifier);
 #endif /* CONFIG_PM */
 
 #if defined(CONFIG_PM) && defined(CONFIG_PPC32)
@@ -2667,10 +2669,10 @@
 	asleep = 1;
 
 	/* Put the CPU into sleep mode */
-	asm volatile("mfspr %0,1008" : "=r" (hid0) :);
+	hid0 = mfspr(SPRN_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);
+	mtspr(SPRN_HID0, hid0);
+	mtmsr(mfmsr() | MSR_POW | MSR_EE);
 	udelay(10);
 
 	/* OK, we're awake again, start restoring things */
@@ -3139,8 +3141,6 @@
 EXPORT_SYMBOL(pmu_i2c_simple_read);
 EXPORT_SYMBOL(pmu_i2c_simple_write);
 #if defined(CONFIG_PM) && defined(CONFIG_PPC32)
-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);
diff --git a/drivers/macintosh/windfarm.h b/drivers/macintosh/windfarm.h
new file mode 100644
index 0000000..3f0cb03
--- /dev/null
+++ b/drivers/macintosh/windfarm.h
@@ -0,0 +1,131 @@
+/*
+ * Windfarm PowerMac thermal control.
+ *
+ * (c) Copyright 2005 Benjamin Herrenschmidt, IBM Corp.
+ *                    <benh@kernel.crashing.org>
+ *
+ * Released under the term of the GNU GPL v2.
+ */
+
+#ifndef __WINDFARM_H__
+#define __WINDFARM_H__
+
+#include <linux/kref.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/notifier.h>
+
+/* Display a 16.16 fixed point value */
+#define FIX32TOPRINT(f)	((f) >> 16),((((f) & 0xffff) * 1000) >> 16)
+
+/*
+ * Control objects
+ */
+
+struct wf_control;
+
+struct wf_control_ops {
+	int			(*set_value)(struct wf_control *ct, s32 val);
+	int			(*get_value)(struct wf_control *ct, s32 *val);
+	s32			(*get_min)(struct wf_control *ct);
+	s32			(*get_max)(struct wf_control *ct);
+	void			(*release)(struct wf_control *ct);
+	struct module		*owner;
+};
+
+struct wf_control {
+	struct list_head	link;
+	struct wf_control_ops	*ops;
+	char			*name;
+	int			type;
+	struct kref		ref;
+};
+
+#define WF_CONTROL_TYPE_GENERIC		0
+#define WF_CONTROL_RPM_FAN		1
+#define WF_CONTROL_PWM_FAN		2
+
+
+/* Note about lifetime rules: wf_register_control() will initialize
+ * the kref and wf_unregister_control will decrement it, thus the
+ * object creating/disposing a given control shouldn't assume it
+ * still exists after wf_unregister_control has been called.
+ * wf_find_control will inc the refcount for you
+ */
+extern int wf_register_control(struct wf_control *ct);
+extern void wf_unregister_control(struct wf_control *ct);
+extern struct wf_control * wf_find_control(const char *name);
+extern int wf_get_control(struct wf_control *ct);
+extern void wf_put_control(struct wf_control *ct);
+
+static inline int wf_control_set_max(struct wf_control *ct)
+{
+	s32 vmax = ct->ops->get_max(ct);
+	return ct->ops->set_value(ct, vmax);
+}
+
+static inline int wf_control_set_min(struct wf_control *ct)
+{
+	s32 vmin = ct->ops->get_min(ct);
+	return ct->ops->set_value(ct, vmin);
+}
+
+/*
+ * Sensor objects
+ */
+
+struct wf_sensor;
+
+struct wf_sensor_ops {
+	int			(*get_value)(struct wf_sensor *sr, s32 *val);
+	void			(*release)(struct wf_sensor *sr);
+	struct module		*owner;
+};
+
+struct wf_sensor {
+	struct list_head	link;
+	struct wf_sensor_ops	*ops;
+	char			*name;
+	struct kref		ref;
+};
+
+/* Same lifetime rules as controls */
+extern int wf_register_sensor(struct wf_sensor *sr);
+extern void wf_unregister_sensor(struct wf_sensor *sr);
+extern struct wf_sensor * wf_find_sensor(const char *name);
+extern int wf_get_sensor(struct wf_sensor *sr);
+extern void wf_put_sensor(struct wf_sensor *sr);
+
+/* For use by clients. Note that we are a bit racy here since
+ * notifier_block doesn't have a module owner field. I may fix
+ * it one day ...
+ *
+ * LOCKING NOTE !
+ *
+ * All "events" except WF_EVENT_TICK are called with an internal mutex
+ * held which will deadlock if you call basically any core routine.
+ * So don't ! Just take note of the event and do your actual operations
+ * from the ticker.
+ *
+ */
+extern int wf_register_client(struct notifier_block *nb);
+extern int wf_unregister_client(struct notifier_block *nb);
+
+/* Overtemp conditions. Those are refcounted */
+extern void wf_set_overtemp(void);
+extern void wf_clear_overtemp(void);
+extern int wf_is_overtemp(void);
+
+#define WF_EVENT_NEW_CONTROL	0 /* param is wf_control * */
+#define WF_EVENT_NEW_SENSOR	1 /* param is wf_sensor * */
+#define WF_EVENT_OVERTEMP	2 /* no param */
+#define WF_EVENT_NORMALTEMP	3 /* overtemp condition cleared */
+#define WF_EVENT_TICK		4 /* 1 second tick */
+
+/* Note: If that driver gets more broad use, we could replace the
+ * simplistic overtemp bits with "environmental conditions". That
+ * could then be used to also notify of things like fan failure,
+ * case open, battery conditions, ...
+ */
+
+#endif /* __WINDFARM_H__ */
diff --git a/drivers/macintosh/windfarm_core.c b/drivers/macintosh/windfarm_core.c
new file mode 100644
index 0000000..6c2a471
--- /dev/null
+++ b/drivers/macintosh/windfarm_core.c
@@ -0,0 +1,426 @@
+/*
+ * Windfarm PowerMac thermal control. Core
+ *
+ * (c) Copyright 2005 Benjamin Herrenschmidt, IBM Corp.
+ *                    <benh@kernel.crashing.org>
+ *
+ * Released under the term of the GNU GPL v2.
+ *
+ * This core code tracks the list of sensors & controls, register
+ * clients, and holds the kernel thread used for control.
+ *
+ * TODO:
+ *
+ * Add some information about sensor/control type and data format to
+ * sensors/controls, and have the sysfs attribute stuff be moved
+ * generically here instead of hard coded in the platform specific
+ * driver as it us currently
+ *
+ * This however requires solving some annoying lifetime issues with
+ * sysfs which doesn't seem to have lifetime rules for struct attribute,
+ * I may have to create full features kobjects for every sensor/control
+ * instead which is a bit of an overkill imho
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/smp_lock.h>
+#include <linux/kthread.h>
+#include <linux/jiffies.h>
+#include <linux/reboot.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+
+#include "windfarm.h"
+
+#define VERSION "0.2"
+
+#undef DEBUG
+
+#ifdef DEBUG
+#define DBG(args...)	printk(args)
+#else
+#define DBG(args...)	do { } while(0)
+#endif
+
+static LIST_HEAD(wf_controls);
+static LIST_HEAD(wf_sensors);
+static DECLARE_MUTEX(wf_lock);
+static struct notifier_block *wf_client_list;
+static int wf_client_count;
+static unsigned int wf_overtemp;
+static unsigned int wf_overtemp_counter;
+struct task_struct *wf_thread;
+
+/*
+ * Utilities & tick thread
+ */
+
+static inline void wf_notify(int event, void *param)
+{
+	notifier_call_chain(&wf_client_list, event, param);
+}
+
+int wf_critical_overtemp(void)
+{
+	static char * critical_overtemp_path = "/sbin/critical_overtemp";
+	char *argv[] = { critical_overtemp_path, NULL };
+	static char *envp[] = { "HOME=/",
+				"TERM=linux",
+				"PATH=/sbin:/usr/sbin:/bin:/usr/bin",
+				NULL };
+
+	return call_usermodehelper(critical_overtemp_path, argv, envp, 0);
+}
+EXPORT_SYMBOL_GPL(wf_critical_overtemp);
+
+static int wf_thread_func(void *data)
+{
+	unsigned long next, delay;
+
+	next = jiffies;
+
+	DBG("wf: thread started\n");
+
+	while(!kthread_should_stop()) {
+		try_to_freeze();
+
+		if (time_after_eq(jiffies, next)) {
+			wf_notify(WF_EVENT_TICK, NULL);
+			if (wf_overtemp) {
+				wf_overtemp_counter++;
+				/* 10 seconds overtemp, notify userland */
+				if (wf_overtemp_counter > 10)
+					wf_critical_overtemp();
+				/* 30 seconds, shutdown */
+				if (wf_overtemp_counter > 30) {
+					printk(KERN_ERR "windfarm: Overtemp "
+					       "for more than 30"
+					       " seconds, shutting down\n");
+					machine_power_off();
+				}
+			}
+			next += HZ;
+		}
+
+		delay = next - jiffies;
+		if (delay <= HZ)
+			schedule_timeout_interruptible(delay);
+
+		/* there should be no signal, but oh well */
+		if (signal_pending(current)) {
+			printk(KERN_WARNING "windfarm: thread got sigl !\n");
+			break;
+		}
+	}
+
+	DBG("wf: thread stopped\n");
+
+	return 0;
+}
+
+static void wf_start_thread(void)
+{
+	wf_thread = kthread_run(wf_thread_func, NULL, "kwindfarm");
+	if (IS_ERR(wf_thread)) {
+		printk(KERN_ERR "windfarm: failed to create thread,err %ld\n",
+		       PTR_ERR(wf_thread));
+		wf_thread = NULL;
+	}
+}
+
+
+static void wf_stop_thread(void)
+{
+	if (wf_thread)
+		kthread_stop(wf_thread);
+	wf_thread = NULL;
+}
+
+/*
+ * Controls
+ */
+
+static void wf_control_release(struct kref *kref)
+{
+	struct wf_control *ct = container_of(kref, struct wf_control, ref);
+
+	DBG("wf: Deleting control %s\n", ct->name);
+
+	if (ct->ops && ct->ops->release)
+		ct->ops->release(ct);
+	else
+		kfree(ct);
+}
+
+int wf_register_control(struct wf_control *new_ct)
+{
+	struct wf_control *ct;
+
+	down(&wf_lock);
+	list_for_each_entry(ct, &wf_controls, link) {
+		if (!strcmp(ct->name, new_ct->name)) {
+			printk(KERN_WARNING "windfarm: trying to register"
+			       " duplicate control %s\n", ct->name);
+			up(&wf_lock);
+			return -EEXIST;
+		}
+	}
+	kref_init(&new_ct->ref);
+	list_add(&new_ct->link, &wf_controls);
+
+	DBG("wf: Registered control %s\n", new_ct->name);
+
+	wf_notify(WF_EVENT_NEW_CONTROL, new_ct);
+	up(&wf_lock);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(wf_register_control);
+
+void wf_unregister_control(struct wf_control *ct)
+{
+	down(&wf_lock);
+	list_del(&ct->link);
+	up(&wf_lock);
+
+	DBG("wf: Unregistered control %s\n", ct->name);
+
+	kref_put(&ct->ref, wf_control_release);
+}
+EXPORT_SYMBOL_GPL(wf_unregister_control);
+
+struct wf_control * wf_find_control(const char *name)
+{
+	struct wf_control *ct;
+
+	down(&wf_lock);
+	list_for_each_entry(ct, &wf_controls, link) {
+		if (!strcmp(ct->name, name)) {
+			if (wf_get_control(ct))
+				ct = NULL;
+			up(&wf_lock);
+			return ct;
+		}
+	}
+	up(&wf_lock);
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(wf_find_control);
+
+int wf_get_control(struct wf_control *ct)
+{
+	if (!try_module_get(ct->ops->owner))
+		return -ENODEV;
+	kref_get(&ct->ref);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(wf_get_control);
+
+void wf_put_control(struct wf_control *ct)
+{
+	struct module *mod = ct->ops->owner;
+	kref_put(&ct->ref, wf_control_release);
+	module_put(mod);
+}
+EXPORT_SYMBOL_GPL(wf_put_control);
+
+
+/*
+ * Sensors
+ */
+
+
+static void wf_sensor_release(struct kref *kref)
+{
+	struct wf_sensor *sr = container_of(kref, struct wf_sensor, ref);
+
+	DBG("wf: Deleting sensor %s\n", sr->name);
+
+	if (sr->ops && sr->ops->release)
+		sr->ops->release(sr);
+	else
+		kfree(sr);
+}
+
+int wf_register_sensor(struct wf_sensor *new_sr)
+{
+	struct wf_sensor *sr;
+
+	down(&wf_lock);
+	list_for_each_entry(sr, &wf_sensors, link) {
+		if (!strcmp(sr->name, new_sr->name)) {
+			printk(KERN_WARNING "windfarm: trying to register"
+			       " duplicate sensor %s\n", sr->name);
+			up(&wf_lock);
+			return -EEXIST;
+		}
+	}
+	kref_init(&new_sr->ref);
+	list_add(&new_sr->link, &wf_sensors);
+
+	DBG("wf: Registered sensor %s\n", new_sr->name);
+
+	wf_notify(WF_EVENT_NEW_SENSOR, new_sr);
+	up(&wf_lock);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(wf_register_sensor);
+
+void wf_unregister_sensor(struct wf_sensor *sr)
+{
+	down(&wf_lock);
+	list_del(&sr->link);
+	up(&wf_lock);
+
+	DBG("wf: Unregistered sensor %s\n", sr->name);
+
+	wf_put_sensor(sr);
+}
+EXPORT_SYMBOL_GPL(wf_unregister_sensor);
+
+struct wf_sensor * wf_find_sensor(const char *name)
+{
+	struct wf_sensor *sr;
+
+	down(&wf_lock);
+	list_for_each_entry(sr, &wf_sensors, link) {
+		if (!strcmp(sr->name, name)) {
+			if (wf_get_sensor(sr))
+				sr = NULL;
+			up(&wf_lock);
+			return sr;
+		}
+	}
+	up(&wf_lock);
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(wf_find_sensor);
+
+int wf_get_sensor(struct wf_sensor *sr)
+{
+	if (!try_module_get(sr->ops->owner))
+		return -ENODEV;
+	kref_get(&sr->ref);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(wf_get_sensor);
+
+void wf_put_sensor(struct wf_sensor *sr)
+{
+	struct module *mod = sr->ops->owner;
+	kref_put(&sr->ref, wf_sensor_release);
+	module_put(mod);
+}
+EXPORT_SYMBOL_GPL(wf_put_sensor);
+
+
+/*
+ * Client & notification
+ */
+
+int wf_register_client(struct notifier_block *nb)
+{
+	int rc;
+	struct wf_control *ct;
+	struct wf_sensor *sr;
+
+	down(&wf_lock);
+	rc = notifier_chain_register(&wf_client_list, nb);
+	if (rc != 0)
+		goto bail;
+	wf_client_count++;
+	list_for_each_entry(ct, &wf_controls, link)
+		wf_notify(WF_EVENT_NEW_CONTROL, ct);
+	list_for_each_entry(sr, &wf_sensors, link)
+		wf_notify(WF_EVENT_NEW_SENSOR, sr);
+	if (wf_client_count == 1)
+		wf_start_thread();
+ bail:
+	up(&wf_lock);
+	return rc;
+}
+EXPORT_SYMBOL_GPL(wf_register_client);
+
+int wf_unregister_client(struct notifier_block *nb)
+{
+	down(&wf_lock);
+	notifier_chain_unregister(&wf_client_list, nb);
+	wf_client_count++;
+	if (wf_client_count == 0)
+		wf_stop_thread();
+	up(&wf_lock);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(wf_unregister_client);
+
+void wf_set_overtemp(void)
+{
+	down(&wf_lock);
+	wf_overtemp++;
+	if (wf_overtemp == 1) {
+		printk(KERN_WARNING "windfarm: Overtemp condition detected !\n");
+		wf_overtemp_counter = 0;
+		wf_notify(WF_EVENT_OVERTEMP, NULL);
+	}
+	up(&wf_lock);
+}
+EXPORT_SYMBOL_GPL(wf_set_overtemp);
+
+void wf_clear_overtemp(void)
+{
+	down(&wf_lock);
+	WARN_ON(wf_overtemp == 0);
+	if (wf_overtemp == 0) {
+		up(&wf_lock);
+		return;
+	}
+	wf_overtemp--;
+	if (wf_overtemp == 0) {
+		printk(KERN_WARNING "windfarm: Overtemp condition cleared !\n");
+		wf_notify(WF_EVENT_NORMALTEMP, NULL);
+	}
+	up(&wf_lock);
+}
+EXPORT_SYMBOL_GPL(wf_clear_overtemp);
+
+int wf_is_overtemp(void)
+{
+	return (wf_overtemp != 0);
+}
+EXPORT_SYMBOL_GPL(wf_is_overtemp);
+
+static struct platform_device wf_platform_device = {
+	.name	= "windfarm",
+};
+
+static int __init windfarm_core_init(void)
+{
+	DBG("wf: core loaded\n");
+
+	platform_device_register(&wf_platform_device);
+	return 0;
+}
+
+static void __exit windfarm_core_exit(void)
+{
+	BUG_ON(wf_client_count != 0);
+
+	DBG("wf: core unloaded\n");
+
+	platform_device_unregister(&wf_platform_device);
+}
+
+
+module_init(windfarm_core_init);
+module_exit(windfarm_core_exit);
+
+MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
+MODULE_DESCRIPTION("Core component of PowerMac thermal control");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/macintosh/windfarm_cpufreq_clamp.c b/drivers/macintosh/windfarm_cpufreq_clamp.c
new file mode 100644
index 0000000..607dbac
--- /dev/null
+++ b/drivers/macintosh/windfarm_cpufreq_clamp.c
@@ -0,0 +1,105 @@
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/wait.h>
+#include <linux/cpufreq.h>
+
+#include "windfarm.h"
+
+#define VERSION "0.3"
+
+static int clamped;
+static struct wf_control *clamp_control;
+
+static int clamp_notifier_call(struct notifier_block *self,
+			       unsigned long event, void *data)
+{
+	struct cpufreq_policy *p = data;
+	unsigned long max_freq;
+
+	if (event != CPUFREQ_ADJUST)
+		return 0;
+
+	max_freq = clamped ? (p->cpuinfo.min_freq) : (p->cpuinfo.max_freq);
+	cpufreq_verify_within_limits(p, 0, max_freq);
+
+	return 0;
+}
+
+static struct notifier_block clamp_notifier = {
+	.notifier_call = clamp_notifier_call,
+};
+
+static int clamp_set(struct wf_control *ct, s32 value)
+{
+	if (value)
+		printk(KERN_INFO "windfarm: Clamping CPU frequency to "
+		       "minimum !\n");
+	else
+		printk(KERN_INFO "windfarm: CPU frequency unclamped !\n");
+	clamped = value;
+	cpufreq_update_policy(0);
+	return 0;
+}
+
+static int clamp_get(struct wf_control *ct, s32 *value)
+{
+	*value = clamped;
+	return 0;
+}
+
+static s32 clamp_min(struct wf_control *ct)
+{
+	return 0;
+}
+
+static s32 clamp_max(struct wf_control *ct)
+{
+	return 1;
+}
+
+static struct wf_control_ops clamp_ops = {
+	.set_value	= clamp_set,
+	.get_value	= clamp_get,
+	.get_min	= clamp_min,
+	.get_max	= clamp_max,
+	.owner		= THIS_MODULE,
+};
+
+static int __init wf_cpufreq_clamp_init(void)
+{
+	struct wf_control *clamp;
+
+	clamp = kmalloc(sizeof(struct wf_control), GFP_KERNEL);
+	if (clamp == NULL)
+		return -ENOMEM;
+	cpufreq_register_notifier(&clamp_notifier, CPUFREQ_POLICY_NOTIFIER);
+	clamp->ops = &clamp_ops;
+	clamp->name = "cpufreq-clamp";
+	if (wf_register_control(clamp))
+		goto fail;
+	clamp_control = clamp;
+	return 0;
+ fail:
+	kfree(clamp);
+	return -ENODEV;
+}
+
+static void __exit wf_cpufreq_clamp_exit(void)
+{
+	if (clamp_control)
+		wf_unregister_control(clamp_control);
+}
+
+
+module_init(wf_cpufreq_clamp_init);
+module_exit(wf_cpufreq_clamp_exit);
+
+MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
+MODULE_DESCRIPTION("CPU frequency clamp for PowerMacs thermal control");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/macintosh/windfarm_lm75_sensor.c b/drivers/macintosh/windfarm_lm75_sensor.c
new file mode 100644
index 0000000..a0a41ad
--- /dev/null
+++ b/drivers/macintosh/windfarm_lm75_sensor.c
@@ -0,0 +1,263 @@
+/*
+ * Windfarm PowerMac thermal control. LM75 sensor
+ *
+ * (c) Copyright 2005 Benjamin Herrenschmidt, IBM Corp.
+ *                    <benh@kernel.crashing.org>
+ *
+ * Released under the term of the GNU GPL v2.
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/wait.h>
+#include <linux/i2c.h>
+#include <linux/i2c-dev.h>
+#include <asm/prom.h>
+#include <asm/machdep.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/sections.h>
+
+#include "windfarm.h"
+
+#define VERSION "0.1"
+
+#undef DEBUG
+
+#ifdef DEBUG
+#define DBG(args...)	printk(args)
+#else
+#define DBG(args...)	do { } while(0)
+#endif
+
+struct wf_lm75_sensor {
+	int			ds1775 : 1;
+	int			inited : 1;
+	struct 	i2c_client	i2c;
+	struct 	wf_sensor	sens;
+};
+#define wf_to_lm75(c) container_of(c, struct wf_lm75_sensor, sens)
+#define i2c_to_lm75(c) container_of(c, struct wf_lm75_sensor, i2c)
+
+static int wf_lm75_attach(struct i2c_adapter *adapter);
+static int wf_lm75_detach(struct i2c_client *client);
+
+static struct i2c_driver wf_lm75_driver = {
+	.owner		= THIS_MODULE,
+	.name		= "wf_lm75",
+	.flags		= I2C_DF_NOTIFY,
+	.attach_adapter	= wf_lm75_attach,
+	.detach_client	= wf_lm75_detach,
+};
+
+static int wf_lm75_get(struct wf_sensor *sr, s32 *value)
+{
+	struct wf_lm75_sensor *lm = wf_to_lm75(sr);
+	s32 data;
+
+	if (lm->i2c.adapter == NULL)
+		return -ENODEV;
+
+	/* Init chip if necessary */
+	if (!lm->inited) {
+		u8 cfg_new, cfg = (u8)i2c_smbus_read_byte_data(&lm->i2c, 1);
+
+		DBG("wf_lm75: Initializing %s, cfg was: %02x\n",
+		    sr->name, cfg);
+
+		/* clear shutdown bit, keep other settings as left by
+		 * the firmware for now
+		 */
+		cfg_new = cfg & ~0x01;
+		i2c_smbus_write_byte_data(&lm->i2c, 1, cfg_new);
+		lm->inited = 1;
+
+		/* If we just powered it up, let's wait 200 ms */
+		msleep(200);
+	}
+
+	/* Read temperature register */
+	data = (s32)le16_to_cpu(i2c_smbus_read_word_data(&lm->i2c, 0));
+	data <<= 8;
+	*value = data;
+
+	return 0;
+}
+
+static void wf_lm75_release(struct wf_sensor *sr)
+{
+	struct wf_lm75_sensor *lm = wf_to_lm75(sr);
+
+	/* check if client is registered and detach from i2c */
+	if (lm->i2c.adapter) {
+		i2c_detach_client(&lm->i2c);
+		lm->i2c.adapter = NULL;
+	}
+
+	kfree(lm);
+}
+
+static struct wf_sensor_ops wf_lm75_ops = {
+	.get_value	= wf_lm75_get,
+	.release	= wf_lm75_release,
+	.owner		= THIS_MODULE,
+};
+
+static struct wf_lm75_sensor *wf_lm75_create(struct i2c_adapter *adapter,
+					     u8 addr, int ds1775,
+					     const char *loc)
+{
+	struct wf_lm75_sensor *lm;
+
+	DBG("wf_lm75: creating  %s device at address 0x%02x\n",
+	    ds1775 ? "ds1775" : "lm75", addr);
+
+	lm = kmalloc(sizeof(struct wf_lm75_sensor), GFP_KERNEL);
+	if (lm == NULL)
+		return NULL;
+	memset(lm, 0, sizeof(struct wf_lm75_sensor));
+
+	/* Usual rant about sensor names not beeing very consistent in
+	 * the device-tree, oh well ...
+	 * Add more entries below as you deal with more setups
+	 */
+	if (!strcmp(loc, "Hard drive") || !strcmp(loc, "DRIVE BAY"))
+		lm->sens.name = "hd-temp";
+	else
+		goto fail;
+
+	lm->inited = 0;
+	lm->sens.ops = &wf_lm75_ops;
+	lm->ds1775 = ds1775;
+	lm->i2c.addr = (addr >> 1) & 0x7f;
+	lm->i2c.adapter = adapter;
+	lm->i2c.driver = &wf_lm75_driver;
+	strncpy(lm->i2c.name, lm->sens.name, I2C_NAME_SIZE-1);
+
+	if (i2c_attach_client(&lm->i2c)) {
+		printk(KERN_ERR "windfarm: failed to attach %s %s to i2c\n",
+		       ds1775 ? "ds1775" : "lm75", lm->i2c.name);
+		goto fail;
+	}
+
+	if (wf_register_sensor(&lm->sens)) {
+		i2c_detach_client(&lm->i2c);
+		goto fail;
+	}
+
+	return lm;
+ fail:
+	kfree(lm);
+	return NULL;
+}
+
+static int wf_lm75_attach(struct i2c_adapter *adapter)
+{
+	u8 bus_id;
+	struct device_node *smu, *bus, *dev;
+
+	/* We currently only deal with LM75's hanging off the SMU
+	 * i2c busses. If we extend that driver to other/older
+	 * machines, we should split this function into SMU-i2c,
+	 * keywest-i2c, PMU-i2c, ...
+	 */
+
+	DBG("wf_lm75: adapter %s detected\n", adapter->name);
+
+	if (strncmp(adapter->name, "smu-i2c-", 8) != 0)
+		return 0;
+	smu = of_find_node_by_type(NULL, "smu");
+	if (smu == NULL)
+		return 0;
+
+	/* Look for the bus in the device-tree */
+	bus_id = (u8)simple_strtoul(adapter->name + 8, NULL, 16);
+
+	DBG("wf_lm75: bus ID is %x\n", bus_id);
+
+	/* Look for sensors subdir */
+	for (bus = NULL;
+	     (bus = of_get_next_child(smu, bus)) != NULL;) {
+		u32 *reg;
+
+		if (strcmp(bus->name, "i2c"))
+			continue;
+		reg = (u32 *)get_property(bus, "reg", NULL);
+		if (reg == NULL)
+			continue;
+		if (bus_id == *reg)
+			break;
+	}
+	of_node_put(smu);
+	if (bus == NULL) {
+		printk(KERN_WARNING "windfarm: SMU i2c bus 0x%x not found"
+		       " in device-tree !\n", bus_id);
+		return 0;
+	}
+
+	DBG("wf_lm75: bus found, looking for device...\n");
+
+	/* Now look for lm75(s) in there */
+	for (dev = NULL;
+	     (dev = of_get_next_child(bus, dev)) != NULL;) {
+		const char *loc =
+			get_property(dev, "hwsensor-location", NULL);
+		u32 *reg = (u32 *)get_property(dev, "reg", NULL);
+		DBG(" dev: %s... (loc: %p, reg: %p)\n", dev->name, loc, reg);
+		if (loc == NULL || reg == NULL)
+			continue;
+		/* real lm75 */
+		if (device_is_compatible(dev, "lm75"))
+			wf_lm75_create(adapter, *reg, 0, loc);
+		/* ds1775 (compatible, better resolution */
+		else if (device_is_compatible(dev, "ds1775"))
+			wf_lm75_create(adapter, *reg, 1, loc);
+	}
+
+	of_node_put(bus);
+
+	return 0;
+}
+
+static int wf_lm75_detach(struct i2c_client *client)
+{
+	struct wf_lm75_sensor *lm = i2c_to_lm75(client);
+
+	DBG("wf_lm75: i2c detatch called for %s\n", lm->sens.name);
+
+	/* Mark client detached */
+	lm->i2c.adapter = NULL;
+
+	/* release sensor */
+	wf_unregister_sensor(&lm->sens);
+
+	return 0;
+}
+
+static int __init wf_lm75_sensor_init(void)
+{
+	int rc;
+
+	rc = i2c_add_driver(&wf_lm75_driver);
+	if (rc < 0)
+		return rc;
+	return 0;
+}
+
+static void __exit wf_lm75_sensor_exit(void)
+{
+	i2c_del_driver(&wf_lm75_driver);
+}
+
+
+module_init(wf_lm75_sensor_init);
+module_exit(wf_lm75_sensor_exit);
+
+MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
+MODULE_DESCRIPTION("LM75 sensor objects for PowerMacs thermal control");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/macintosh/windfarm_pid.c b/drivers/macintosh/windfarm_pid.c
new file mode 100644
index 0000000..2e803b3
--- /dev/null
+++ b/drivers/macintosh/windfarm_pid.c
@@ -0,0 +1,145 @@
+/*
+ * Windfarm PowerMac thermal control. Generic PID helpers
+ *
+ * (c) Copyright 2005 Benjamin Herrenschmidt, IBM Corp.
+ *                    <benh@kernel.crashing.org>
+ *
+ * Released under the term of the GNU GPL v2.
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/module.h>
+
+#include "windfarm_pid.h"
+
+#undef DEBUG
+
+#ifdef DEBUG
+#define DBG(args...)	printk(args)
+#else
+#define DBG(args...)	do { } while(0)
+#endif
+
+void wf_pid_init(struct wf_pid_state *st, struct wf_pid_param *param)
+{
+	memset(st, 0, sizeof(struct wf_pid_state));
+	st->param = *param;
+	st->first = 1;
+}
+EXPORT_SYMBOL_GPL(wf_pid_init);
+
+s32 wf_pid_run(struct wf_pid_state *st, s32 new_sample)
+{
+	s64	error, integ, deriv;
+	s32	target;
+	int	i, hlen = st->param.history_len;
+
+	/* Calculate error term */
+	error = new_sample - st->param.itarget;
+
+	/* Get samples into our history buffer */
+	if (st->first) {
+		for (i = 0; i < hlen; i++) {
+			st->samples[i] = new_sample;
+			st->errors[i] = error;
+		}
+		st->first = 0;
+		st->index = 0;
+	} else {
+		st->index = (st->index + 1) % hlen;
+		st->samples[st->index] = new_sample;
+		st->errors[st->index] = error;
+	}
+
+	/* Calculate integral term */
+	for (i = 0, integ = 0; i < hlen; i++)
+		integ += st->errors[(st->index + hlen - i) % hlen];
+	integ *= st->param.interval;
+
+	/* Calculate derivative term */
+	deriv = st->errors[st->index] -
+		st->errors[(st->index + hlen - 1) % hlen];
+	deriv /= st->param.interval;
+
+	/* Calculate target */
+	target = (s32)((integ * (s64)st->param.gr + deriv * (s64)st->param.gd +
+		  error * (s64)st->param.gp) >> 36);
+	if (st->param.additive)
+		target += st->target;
+	target = max(target, st->param.min);
+	target = min(target, st->param.max);
+	st->target = target;
+
+	return st->target;
+}
+EXPORT_SYMBOL_GPL(wf_pid_run);
+
+void wf_cpu_pid_init(struct wf_cpu_pid_state *st,
+		     struct wf_cpu_pid_param *param)
+{
+	memset(st, 0, sizeof(struct wf_cpu_pid_state));
+	st->param = *param;
+	st->first = 1;
+}
+EXPORT_SYMBOL_GPL(wf_cpu_pid_init);
+
+s32 wf_cpu_pid_run(struct wf_cpu_pid_state *st, s32 new_power, s32 new_temp)
+{
+	s64	error, integ, deriv, prop;
+	s32	target, sval, adj;
+	int	i, hlen = st->param.history_len;
+
+	/* Calculate error term */
+	error = st->param.pmaxadj - new_power;
+
+	/* Get samples into our history buffer */
+	if (st->first) {
+		for (i = 0; i < hlen; i++) {
+			st->powers[i] = new_power;
+			st->errors[i] = error;
+		}
+		st->temps[0] = st->temps[1] = new_temp;
+		st->first = 0;
+		st->index = st->tindex = 0;
+	} else {
+		st->index = (st->index + 1) % hlen;
+		st->powers[st->index] = new_power;
+		st->errors[st->index] = error;
+		st->tindex = (st->tindex + 1) % 2;
+		st->temps[st->tindex] = new_temp;
+	}
+
+	/* Calculate integral term */
+	for (i = 0, integ = 0; i < hlen; i++)
+		integ += st->errors[(st->index + hlen - i) % hlen];
+	integ *= st->param.interval;
+	integ *= st->param.gr;
+	sval = st->param.tmax - ((integ >> 20) & 0xffffffff);
+	adj = min(st->param.ttarget, sval);
+
+	DBG("integ: %lx, sval: %lx, adj: %lx\n", integ, sval, adj);
+
+	/* Calculate derivative term */
+	deriv = st->temps[st->tindex] -
+		st->temps[(st->tindex + 2 - 1) % 2];
+	deriv /= st->param.interval;
+	deriv *= st->param.gd;
+
+	/* Calculate proportional term */
+	prop = (new_temp - adj);
+	prop *= st->param.gp;
+
+	DBG("deriv: %lx, prop: %lx\n", deriv, prop);
+
+	/* Calculate target */
+	target = st->target + (s32)((deriv + prop) >> 36);
+	target = max(target, st->param.min);
+	target = min(target, st->param.max);
+	st->target = target;
+
+	return st->target;
+}
+EXPORT_SYMBOL_GPL(wf_cpu_pid_run);
diff --git a/drivers/macintosh/windfarm_pid.h b/drivers/macintosh/windfarm_pid.h
new file mode 100644
index 0000000..a364c2a
--- /dev/null
+++ b/drivers/macintosh/windfarm_pid.h
@@ -0,0 +1,84 @@
+/*
+ * Windfarm PowerMac thermal control. Generic PID helpers
+ *
+ * (c) Copyright 2005 Benjamin Herrenschmidt, IBM Corp.
+ *                    <benh@kernel.crashing.org>
+ *
+ * Released under the term of the GNU GPL v2.
+ *
+ * This is a pair of generic PID helpers that can be used by
+ * control loops. One is the basic PID implementation, the
+ * other one is more specifically tailored to the loops used
+ * for CPU control with 2 input sample types (temp and power)
+ */
+
+/*
+ * *** Simple PID ***
+ */
+
+#define WF_PID_MAX_HISTORY	32
+
+/* This parameter array is passed to the PID algorithm. Currently,
+ * we don't support changing parameters on the fly as it's not needed
+ * but could be implemented (with necessary adjustment of the history
+ * buffer
+ */
+struct wf_pid_param {
+	int	interval;	/* Interval between samples in seconds */
+	int	history_len;	/* Size of history buffer */
+	int	additive;	/* 1: target relative to previous value */
+	s32	gd, gp, gr;	/* PID gains */
+	s32	itarget;	/* PID input target */
+	s32	min,max;	/* min and max target values */
+};
+
+struct wf_pid_state {
+	int	first;				/* first run of the loop */
+	int	index; 				/* index of current sample */
+	s32	target;				/* current target value */
+	s32	samples[WF_PID_MAX_HISTORY];	/* samples history buffer */
+	s32	errors[WF_PID_MAX_HISTORY];	/* error history buffer */
+
+	struct wf_pid_param param;
+};
+
+extern void wf_pid_init(struct wf_pid_state *st, struct wf_pid_param *param);
+extern s32 wf_pid_run(struct wf_pid_state *st, s32 sample);
+
+
+/*
+ * *** CPU PID ***
+ */
+
+#define WF_CPU_PID_MAX_HISTORY	32
+
+/* This parameter array is passed to the CPU PID algorithm. Currently,
+ * we don't support changing parameters on the fly as it's not needed
+ * but could be implemented (with necessary adjustment of the history
+ * buffer
+ */
+struct wf_cpu_pid_param {
+	int	interval;	/* Interval between samples in seconds */
+	int	history_len;	/* Size of history buffer */
+	s32	gd, gp, gr;	/* PID gains */
+	s32	pmaxadj;	/* PID max power adjust */
+	s32	ttarget;	/* PID input target */
+	s32	tmax;		/* PID input max */
+	s32	min,max;	/* min and max target values */
+};
+
+struct wf_cpu_pid_state {
+	int	first;				/* first run of the loop */
+	int	index; 				/* index of current power */
+	int	tindex; 			/* index of current temp */
+	s32	target;				/* current target value */
+	s32	powers[WF_PID_MAX_HISTORY];	/* power history buffer */
+	s32	errors[WF_PID_MAX_HISTORY];	/* error history buffer */
+	s32	temps[2];			/* temp. history buffer */
+
+	struct wf_cpu_pid_param param;
+};
+
+extern void wf_cpu_pid_init(struct wf_cpu_pid_state *st,
+			    struct wf_cpu_pid_param *param);
+extern s32 wf_cpu_pid_run(struct wf_cpu_pid_state *st, s32 power, s32 temp);
diff --git a/drivers/macintosh/windfarm_pm81.c b/drivers/macintosh/windfarm_pm81.c
new file mode 100644
index 0000000..322c74b2
--- /dev/null
+++ b/drivers/macintosh/windfarm_pm81.c
@@ -0,0 +1,879 @@
+/*
+ * Windfarm PowerMac thermal control. iMac G5
+ *
+ * (c) Copyright 2005 Benjamin Herrenschmidt, IBM Corp.
+ *                    <benh@kernel.crashing.org>
+ *
+ * Released under the term of the GNU GPL v2.
+ *
+ * The algorithm used is the PID control algorithm, used the same
+ * way the published Darwin code does, using the same values that
+ * are present in the Darwin 8.2 snapshot property lists (note however
+ * that none of the code has been re-used, it's a complete re-implementation
+ *
+ * The various control loops found in Darwin config file are:
+ *
+ * PowerMac8,1 and PowerMac8,2
+ * ===========================
+ *
+ * System Fans control loop. Different based on models. In addition to the
+ * usual PID algorithm, the control loop gets 2 additional pairs of linear
+ * scaling factors (scale/offsets) expressed as 4.12 fixed point values
+ * signed offset, unsigned scale)
+ *
+ * The targets are modified such as:
+ *  - the linked control (second control) gets the target value as-is
+ *    (typically the drive fan)
+ *  - the main control (first control) gets the target value scaled with
+ *    the first pair of factors, and is then modified as below
+ *  - the value of the target of the CPU Fan control loop is retreived,
+ *    scaled with the second pair of factors, and the max of that and
+ *    the scaled target is applied to the main control.
+ *
+ * # model_id: 2
+ *   controls       : system-fan, drive-bay-fan
+ *   sensors        : hd-temp
+ *   PID params     : G_d = 0x15400000
+ *                    G_p = 0x00200000
+ *                    G_r = 0x000002fd
+ *                    History = 2 entries
+ *                    Input target = 0x3a0000
+ *                    Interval = 5s
+ *   linear-factors : offset = 0xff38 scale  = 0x0ccd
+ *                    offset = 0x0208 scale  = 0x07ae
+ *
+ * # model_id: 3
+ *   controls       : system-fan, drive-bay-fan
+ *   sensors        : hd-temp
+ *   PID params     : G_d = 0x08e00000
+ *                    G_p = 0x00566666
+ *                    G_r = 0x0000072b
+ *                    History = 2 entries
+ *                    Input target = 0x350000
+ *                    Interval = 5s
+ *   linear-factors : offset = 0xff38 scale  = 0x0ccd
+ *                    offset = 0x0000 scale  = 0x0000
+ *
+ * # model_id: 5
+ *   controls       : system-fan
+ *   sensors        : hd-temp
+ *   PID params     : G_d = 0x15400000
+ *                    G_p = 0x00233333
+ *                    G_r = 0x000002fd
+ *                    History = 2 entries
+ *                    Input target = 0x3a0000
+ *                    Interval = 5s
+ *   linear-factors : offset = 0x0000 scale  = 0x1000
+ *                    offset = 0x0091 scale  = 0x0bae
+ *
+ * CPU Fan control loop. The loop is identical for all models. it
+ * has an additional pair of scaling factor. This is used to scale the
+ * systems fan control loop target result (the one before it gets scaled
+ * by the System Fans control loop itself). Then, the max value of the
+ * calculated target value and system fan value is sent to the fans
+ *
+ *   controls       : cpu-fan
+ *   sensors        : cpu-temp cpu-power
+ *   PID params     : From SMU sdb partition
+ *   linear-factors : offset = 0xfb50 scale  = 0x1000
+ *
+ * CPU Slew control loop. Not implemented. The cpufreq driver in linux is
+ * completely separate for now, though we could find a way to link it, either
+ * as a client reacting to overtemp notifications, or directling monitoring
+ * the CPU temperature
+ *
+ * WARNING ! The CPU control loop requires the CPU tmax for the current
+ * operating point. However, we currently are completely separated from
+ * the cpufreq driver and thus do not know what the current operating
+ * point is. Fortunately, we also do not have any hardware supporting anything
+ * but operating point 0 at the moment, thus we just peek that value directly
+ * from the SDB partition. If we ever end up with actually slewing the system
+ * clock and thus changing operating points, we'll have to find a way to
+ * communicate with the CPU freq driver;
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/wait.h>
+#include <linux/kmod.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <asm/prom.h>
+#include <asm/machdep.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/sections.h>
+#include <asm/smu.h>
+
+#include "windfarm.h"
+#include "windfarm_pid.h"
+
+#define VERSION "0.4"
+
+#undef DEBUG
+
+#ifdef DEBUG
+#define DBG(args...)	printk(args)
+#else
+#define DBG(args...)	do { } while(0)
+#endif
+
+/* define this to force CPU overtemp to 74 degree, useful for testing
+ * the overtemp code
+ */
+#undef HACKED_OVERTEMP
+
+static int wf_smu_mach_model;	/* machine model id */
+
+static struct device *wf_smu_dev;
+
+/* Controls & sensors */
+static struct wf_sensor	*sensor_cpu_power;
+static struct wf_sensor	*sensor_cpu_temp;
+static struct wf_sensor	*sensor_hd_temp;
+static struct wf_control *fan_cpu_main;
+static struct wf_control *fan_hd;
+static struct wf_control *fan_system;
+static struct wf_control *cpufreq_clamp;
+
+/* Set to kick the control loop into life */
+static int wf_smu_all_controls_ok, wf_smu_all_sensors_ok, wf_smu_started;
+
+/* Failure handling.. could be nicer */
+#define FAILURE_FAN		0x01
+#define FAILURE_SENSOR		0x02
+#define FAILURE_OVERTEMP	0x04
+
+static unsigned int wf_smu_failure_state;
+static int wf_smu_readjust, wf_smu_skipping;
+
+/*
+ * ****** System Fans Control Loop ******
+ *
+ */
+
+/* Parameters for the System Fans control loop. Parameters
+ * not in this table such as interval, history size, ...
+ * are common to all versions and thus hard coded for now.
+ */
+struct wf_smu_sys_fans_param {
+	int	model_id;
+	s32	itarget;
+	s32	gd, gp, gr;
+
+	s16	offset0;
+	u16	scale0;
+	s16	offset1;
+	u16	scale1;
+};
+
+#define WF_SMU_SYS_FANS_INTERVAL	5
+#define WF_SMU_SYS_FANS_HISTORY_SIZE	2
+
+/* State data used by the system fans control loop
+ */
+struct wf_smu_sys_fans_state {
+	int			ticks;
+	s32			sys_setpoint;
+	s32			hd_setpoint;
+	s16			offset0;
+	u16			scale0;
+	s16			offset1;
+	u16			scale1;
+	struct wf_pid_state	pid;
+};
+
+/*
+ * Configs for SMU Sytem Fan control loop
+ */
+static struct wf_smu_sys_fans_param wf_smu_sys_all_params[] = {
+	/* Model ID 2 */
+	{
+		.model_id	= 2,
+		.itarget	= 0x3a0000,
+		.gd		= 0x15400000,
+		.gp		= 0x00200000,
+		.gr		= 0x000002fd,
+		.offset0	= 0xff38,
+		.scale0		= 0x0ccd,
+		.offset1	= 0x0208,
+		.scale1		= 0x07ae,
+	},
+	/* Model ID 3 */
+	{
+		.model_id	= 2,
+		.itarget	= 0x350000,
+		.gd		= 0x08e00000,
+		.gp		= 0x00566666,
+		.gr		= 0x0000072b,
+		.offset0	= 0xff38,
+		.scale0		= 0x0ccd,
+		.offset1	= 0x0000,
+		.scale1		= 0x0000,
+	},
+	/* Model ID 5 */
+	{
+		.model_id	= 2,
+		.itarget	= 0x3a0000,
+		.gd		= 0x15400000,
+		.gp		= 0x00233333,
+		.gr		= 0x000002fd,
+		.offset0	= 0x0000,
+		.scale0		= 0x1000,
+		.offset1	= 0x0091,
+		.scale1		= 0x0bae,
+	},
+};
+#define WF_SMU_SYS_FANS_NUM_CONFIGS ARRAY_SIZE(wf_smu_sys_all_params)
+
+static struct wf_smu_sys_fans_state *wf_smu_sys_fans;
+
+/*
+ * ****** CPU Fans Control Loop ******
+ *
+ */
+
+
+#define WF_SMU_CPU_FANS_INTERVAL	1
+#define WF_SMU_CPU_FANS_MAX_HISTORY	16
+#define WF_SMU_CPU_FANS_SIBLING_SCALE	0x00001000
+#define WF_SMU_CPU_FANS_SIBLING_OFFSET	0xfffffb50
+
+/* State data used by the cpu fans control loop
+ */
+struct wf_smu_cpu_fans_state {
+	int			ticks;
+	s32			cpu_setpoint;
+	s32			scale;
+	s32			offset;
+	struct wf_cpu_pid_state	pid;
+};
+
+static struct wf_smu_cpu_fans_state *wf_smu_cpu_fans;
+
+
+
+/*
+ * ***** Implementation *****
+ *
+ */
+
+static void wf_smu_create_sys_fans(void)
+{
+	struct wf_smu_sys_fans_param *param = NULL;
+	struct wf_pid_param pid_param;
+	int i;
+
+	/* First, locate the params for this model */
+	for (i = 0; i < WF_SMU_SYS_FANS_NUM_CONFIGS; i++)
+		if (wf_smu_sys_all_params[i].model_id == wf_smu_mach_model) {
+			param = &wf_smu_sys_all_params[i];
+			break;
+		}
+
+	/* No params found, put fans to max */
+	if (param == NULL) {
+		printk(KERN_WARNING "windfarm: System fan config not found "
+		       "for this machine model, max fan speed\n");
+		goto fail;
+	}
+
+	/* Alloc & initialize state */
+	wf_smu_sys_fans = kmalloc(sizeof(struct wf_smu_sys_fans_state),
+				  GFP_KERNEL);
+	if (wf_smu_sys_fans == NULL) {
+		printk(KERN_WARNING "windfarm: Memory allocation error"
+		       " max fan speed\n");
+		goto fail;
+	}
+	wf_smu_sys_fans->ticks = 1;
+	wf_smu_sys_fans->scale0 = param->scale0;
+	wf_smu_sys_fans->offset0 = param->offset0;
+	wf_smu_sys_fans->scale1 = param->scale1;
+	wf_smu_sys_fans->offset1 = param->offset1;
+
+	/* Fill PID params */
+	pid_param.gd = param->gd;
+	pid_param.gp = param->gp;
+	pid_param.gr = param->gr;
+	pid_param.interval = WF_SMU_SYS_FANS_INTERVAL;
+	pid_param.history_len = WF_SMU_SYS_FANS_HISTORY_SIZE;
+	pid_param.itarget = param->itarget;
+	pid_param.min = fan_system->ops->get_min(fan_system);
+	pid_param.max = fan_system->ops->get_max(fan_system);
+	if (fan_hd) {
+		pid_param.min =
+			max(pid_param.min,fan_hd->ops->get_min(fan_hd));
+		pid_param.max =
+			min(pid_param.max,fan_hd->ops->get_max(fan_hd));
+	}
+	wf_pid_init(&wf_smu_sys_fans->pid, &pid_param);
+
+	DBG("wf: System Fan control initialized.\n");
+	DBG("    itarged=%d.%03d, min=%d RPM, max=%d RPM\n",
+	    FIX32TOPRINT(pid_param.itarget), pid_param.min, pid_param.max);
+	return;
+
+ fail:
+
+	if (fan_system)
+		wf_control_set_max(fan_system);
+	if (fan_hd)
+		wf_control_set_max(fan_hd);
+}
+
+static void wf_smu_sys_fans_tick(struct wf_smu_sys_fans_state *st)
+{
+	s32 new_setpoint, temp, scaled, cputarget;
+	int rc;
+
+	if (--st->ticks != 0) {
+		if (wf_smu_readjust)
+			goto readjust;
+		return;
+	}
+	st->ticks = WF_SMU_SYS_FANS_INTERVAL;
+
+	rc = sensor_hd_temp->ops->get_value(sensor_hd_temp, &temp);
+	if (rc) {
+		printk(KERN_WARNING "windfarm: HD temp sensor error %d\n",
+		       rc);
+		wf_smu_failure_state |= FAILURE_SENSOR;
+		return;
+	}
+
+	DBG("wf_smu: System Fans tick ! HD temp: %d.%03d\n",
+	    FIX32TOPRINT(temp));
+
+	if (temp > (st->pid.param.itarget + 0x50000))
+		wf_smu_failure_state |= FAILURE_OVERTEMP;
+
+	new_setpoint = wf_pid_run(&st->pid, temp);
+
+	DBG("wf_smu: new_setpoint: %d RPM\n", (int)new_setpoint);
+
+	scaled = ((((s64)new_setpoint) * (s64)st->scale0) >> 12) + st->offset0;
+
+	DBG("wf_smu: scaled setpoint: %d RPM\n", (int)scaled);
+
+	cputarget = wf_smu_cpu_fans ? wf_smu_cpu_fans->pid.target : 0;
+	cputarget = ((((s64)cputarget) * (s64)st->scale1) >> 12) + st->offset1;
+	scaled = max(scaled, cputarget);
+	scaled = max(scaled, st->pid.param.min);
+	scaled = min(scaled, st->pid.param.max);
+
+	DBG("wf_smu: adjusted setpoint: %d RPM\n", (int)scaled);
+
+	if (st->sys_setpoint == scaled && new_setpoint == st->hd_setpoint)
+		return;
+	st->sys_setpoint = scaled;
+	st->hd_setpoint = new_setpoint;
+ readjust:
+	if (fan_system && wf_smu_failure_state == 0) {
+		rc = fan_system->ops->set_value(fan_system, st->sys_setpoint);
+		if (rc) {
+			printk(KERN_WARNING "windfarm: Sys fan error %d\n",
+			       rc);
+			wf_smu_failure_state |= FAILURE_FAN;
+		}
+	}
+	if (fan_hd && wf_smu_failure_state == 0) {
+		rc = fan_hd->ops->set_value(fan_hd, st->hd_setpoint);
+		if (rc) {
+			printk(KERN_WARNING "windfarm: HD fan error %d\n",
+			       rc);
+			wf_smu_failure_state |= FAILURE_FAN;
+		}
+	}
+}
+
+static void wf_smu_create_cpu_fans(void)
+{
+	struct wf_cpu_pid_param pid_param;
+	struct smu_sdbp_header *hdr;
+	struct smu_sdbp_cpupiddata *piddata;
+	struct smu_sdbp_fvt *fvt;
+	s32 tmax, tdelta, maxpow, powadj;
+
+	/* First, locate the PID params in SMU SBD */
+	hdr = smu_get_sdb_partition(SMU_SDB_CPUPIDDATA_ID, NULL);
+	if (hdr == 0) {
+		printk(KERN_WARNING "windfarm: CPU PID fan config not found "
+		       "max fan speed\n");
+		goto fail;
+	}
+	piddata = (struct smu_sdbp_cpupiddata *)&hdr[1];
+
+	/* Get the FVT params for operating point 0 (the only supported one
+	 * for now) in order to get tmax
+	 */
+	hdr = smu_get_sdb_partition(SMU_SDB_FVT_ID, NULL);
+	if (hdr) {
+		fvt = (struct smu_sdbp_fvt *)&hdr[1];
+		tmax = ((s32)fvt->maxtemp) << 16;
+	} else
+		tmax = 0x5e0000; /* 94 degree default */
+
+	/* Alloc & initialize state */
+	wf_smu_cpu_fans = kmalloc(sizeof(struct wf_smu_cpu_fans_state),
+				  GFP_KERNEL);
+	if (wf_smu_cpu_fans == NULL)
+		goto fail;
+       	wf_smu_cpu_fans->ticks = 1;
+
+	wf_smu_cpu_fans->scale = WF_SMU_CPU_FANS_SIBLING_SCALE;
+	wf_smu_cpu_fans->offset = WF_SMU_CPU_FANS_SIBLING_OFFSET;
+
+	/* Fill PID params */
+	pid_param.interval = WF_SMU_CPU_FANS_INTERVAL;
+	pid_param.history_len = piddata->history_len;
+	if (pid_param.history_len > WF_CPU_PID_MAX_HISTORY) {
+		printk(KERN_WARNING "windfarm: History size overflow on "
+		       "CPU control loop (%d)\n", piddata->history_len);
+		pid_param.history_len = WF_CPU_PID_MAX_HISTORY;
+	}
+	pid_param.gd = piddata->gd;
+	pid_param.gp = piddata->gp;
+	pid_param.gr = piddata->gr / pid_param.history_len;
+
+	tdelta = ((s32)piddata->target_temp_delta) << 16;
+	maxpow = ((s32)piddata->max_power) << 16;
+	powadj = ((s32)piddata->power_adj) << 16;
+
+	pid_param.tmax = tmax;
+	pid_param.ttarget = tmax - tdelta;
+	pid_param.pmaxadj = maxpow - powadj;
+
+	pid_param.min = fan_cpu_main->ops->get_min(fan_cpu_main);
+	pid_param.max = fan_cpu_main->ops->get_max(fan_cpu_main);
+
+	wf_cpu_pid_init(&wf_smu_cpu_fans->pid, &pid_param);
+
+	DBG("wf: CPU Fan control initialized.\n");
+	DBG("    ttarged=%d.%03d, tmax=%d.%03d, min=%d RPM, max=%d RPM\n",
+	    FIX32TOPRINT(pid_param.ttarget), FIX32TOPRINT(pid_param.tmax),
+	    pid_param.min, pid_param.max);
+
+	return;
+
+ fail:
+	printk(KERN_WARNING "windfarm: CPU fan config not found\n"
+	       "for this machine model, max fan speed\n");
+
+	if (cpufreq_clamp)
+		wf_control_set_max(cpufreq_clamp);
+	if (fan_cpu_main)
+		wf_control_set_max(fan_cpu_main);
+}
+
+static void wf_smu_cpu_fans_tick(struct wf_smu_cpu_fans_state *st)
+{
+	s32 new_setpoint, temp, power, systarget;
+	int rc;
+
+	if (--st->ticks != 0) {
+		if (wf_smu_readjust)
+			goto readjust;
+		return;
+	}
+	st->ticks = WF_SMU_CPU_FANS_INTERVAL;
+
+	rc = sensor_cpu_temp->ops->get_value(sensor_cpu_temp, &temp);
+	if (rc) {
+		printk(KERN_WARNING "windfarm: CPU temp sensor error %d\n",
+		       rc);
+		wf_smu_failure_state |= FAILURE_SENSOR;
+		return;
+	}
+
+	rc = sensor_cpu_power->ops->get_value(sensor_cpu_power, &power);
+	if (rc) {
+		printk(KERN_WARNING "windfarm: CPU power sensor error %d\n",
+		       rc);
+		wf_smu_failure_state |= FAILURE_SENSOR;
+		return;
+	}
+
+	DBG("wf_smu: CPU Fans tick ! CPU temp: %d.%03d, power: %d.%03d\n",
+	    FIX32TOPRINT(temp), FIX32TOPRINT(power));
+
+#ifdef HACKED_OVERTEMP
+	if (temp > 0x4a0000)
+		wf_smu_failure_state |= FAILURE_OVERTEMP;
+#else
+	if (temp > st->pid.param.tmax)
+		wf_smu_failure_state |= FAILURE_OVERTEMP;
+#endif
+	new_setpoint = wf_cpu_pid_run(&st->pid, power, temp);
+
+	DBG("wf_smu: new_setpoint: %d RPM\n", (int)new_setpoint);
+
+	systarget = wf_smu_sys_fans ? wf_smu_sys_fans->pid.target : 0;
+	systarget = ((((s64)systarget) * (s64)st->scale) >> 12)
+		+ st->offset;
+	new_setpoint = max(new_setpoint, systarget);
+	new_setpoint = max(new_setpoint, st->pid.param.min);
+	new_setpoint = min(new_setpoint, st->pid.param.max);
+
+	DBG("wf_smu: adjusted setpoint: %d RPM\n", (int)new_setpoint);
+
+	if (st->cpu_setpoint == new_setpoint)
+		return;
+	st->cpu_setpoint = new_setpoint;
+ readjust:
+	if (fan_cpu_main && wf_smu_failure_state == 0) {
+		rc = fan_cpu_main->ops->set_value(fan_cpu_main,
+						  st->cpu_setpoint);
+		if (rc) {
+			printk(KERN_WARNING "windfarm: CPU main fan"
+			       " error %d\n", rc);
+			wf_smu_failure_state |= FAILURE_FAN;
+		}
+	}
+}
+
+
+/*
+ * ****** Attributes ******
+ *
+ */
+
+#define BUILD_SHOW_FUNC_FIX(name, data)				\
+static ssize_t show_##name(struct device *dev,                  \
+			   struct device_attribute *attr,       \
+			   char *buf)	                        \
+{								\
+	ssize_t r;						\
+	s32 val = 0;                                            \
+	data->ops->get_value(data, &val);                       \
+	r = sprintf(buf, "%d.%03d", FIX32TOPRINT(val)); 	\
+	return r;						\
+}                                                               \
+static DEVICE_ATTR(name,S_IRUGO,show_##name, NULL);
+
+
+#define BUILD_SHOW_FUNC_INT(name, data)				\
+static ssize_t show_##name(struct device *dev,                  \
+			   struct device_attribute *attr,       \
+			   char *buf)	                        \
+{								\
+	s32 val = 0;                                            \
+	data->ops->get_value(data, &val);                       \
+	return sprintf(buf, "%d", val);  			\
+}                                                               \
+static DEVICE_ATTR(name,S_IRUGO,show_##name, NULL);
+
+BUILD_SHOW_FUNC_INT(cpu_fan, fan_cpu_main);
+BUILD_SHOW_FUNC_INT(sys_fan, fan_system);
+BUILD_SHOW_FUNC_INT(hd_fan, fan_hd);
+
+BUILD_SHOW_FUNC_FIX(cpu_temp, sensor_cpu_temp);
+BUILD_SHOW_FUNC_FIX(cpu_power, sensor_cpu_power);
+BUILD_SHOW_FUNC_FIX(hd_temp, sensor_hd_temp);
+
+/*
+ * ****** Setup / Init / Misc ... ******
+ *
+ */
+
+static void wf_smu_tick(void)
+{
+	unsigned int last_failure = wf_smu_failure_state;
+	unsigned int new_failure;
+
+	if (!wf_smu_started) {
+		DBG("wf: creating control loops !\n");
+		wf_smu_create_sys_fans();
+		wf_smu_create_cpu_fans();
+		wf_smu_started = 1;
+	}
+
+	/* Skipping ticks */
+	if (wf_smu_skipping && --wf_smu_skipping)
+		return;
+
+	wf_smu_failure_state = 0;
+	if (wf_smu_sys_fans)
+		wf_smu_sys_fans_tick(wf_smu_sys_fans);
+	if (wf_smu_cpu_fans)
+		wf_smu_cpu_fans_tick(wf_smu_cpu_fans);
+
+	wf_smu_readjust = 0;
+	new_failure = wf_smu_failure_state & ~last_failure;
+
+	/* If entering failure mode, clamp cpufreq and ramp all
+	 * fans to full speed.
+	 */
+	if (wf_smu_failure_state && !last_failure) {
+		if (cpufreq_clamp)
+			wf_control_set_max(cpufreq_clamp);
+		if (fan_system)
+			wf_control_set_max(fan_system);
+		if (fan_cpu_main)
+			wf_control_set_max(fan_cpu_main);
+		if (fan_hd)
+			wf_control_set_max(fan_hd);
+	}
+
+	/* If leaving failure mode, unclamp cpufreq and readjust
+	 * all fans on next iteration
+	 */
+	if (!wf_smu_failure_state && last_failure) {
+		if (cpufreq_clamp)
+			wf_control_set_min(cpufreq_clamp);
+		wf_smu_readjust = 1;
+	}
+
+	/* Overtemp condition detected, notify and start skipping a couple
+	 * ticks to let the temperature go down
+	 */
+	if (new_failure & FAILURE_OVERTEMP) {
+		wf_set_overtemp();
+		wf_smu_skipping = 2;
+	}
+
+	/* We only clear the overtemp condition if overtemp is cleared
+	 * _and_ no other failure is present. Since a sensor error will
+	 * clear the overtemp condition (can't measure temperature) at
+	 * the control loop levels, but we don't want to keep it clear
+	 * here in this case
+	 */
+	if (new_failure == 0 && last_failure & FAILURE_OVERTEMP)
+		wf_clear_overtemp();
+}
+
+static void wf_smu_new_control(struct wf_control *ct)
+{
+	if (wf_smu_all_controls_ok)
+		return;
+
+	if (fan_cpu_main == NULL && !strcmp(ct->name, "cpu-fan")) {
+		if (wf_get_control(ct) == 0) {
+			fan_cpu_main = ct;
+			device_create_file(wf_smu_dev, &dev_attr_cpu_fan);
+		}
+	}
+
+	if (fan_system == NULL && !strcmp(ct->name, "system-fan")) {
+		if (wf_get_control(ct) == 0) {
+			fan_system = ct;
+			device_create_file(wf_smu_dev, &dev_attr_sys_fan);
+		}
+	}
+
+	if (cpufreq_clamp == NULL && !strcmp(ct->name, "cpufreq-clamp")) {
+		if (wf_get_control(ct) == 0)
+			cpufreq_clamp = ct;
+	}
+
+	/* Darwin property list says the HD fan is only for model ID
+	 * 0, 1, 2 and 3
+	 */
+
+	if (wf_smu_mach_model > 3) {
+		if (fan_system && fan_cpu_main && cpufreq_clamp)
+			wf_smu_all_controls_ok = 1;
+		return;
+	}
+
+	if (fan_hd == NULL && !strcmp(ct->name, "drive-bay-fan")) {
+		if (wf_get_control(ct) == 0) {
+			fan_hd = ct;
+			device_create_file(wf_smu_dev, &dev_attr_hd_fan);
+		}
+	}
+
+	if (fan_system && fan_hd && fan_cpu_main && cpufreq_clamp)
+		wf_smu_all_controls_ok = 1;
+}
+
+static void wf_smu_new_sensor(struct wf_sensor *sr)
+{
+	if (wf_smu_all_sensors_ok)
+		return;
+
+	if (sensor_cpu_power == NULL && !strcmp(sr->name, "cpu-power")) {
+		if (wf_get_sensor(sr) == 0) {
+			sensor_cpu_power = sr;
+			device_create_file(wf_smu_dev, &dev_attr_cpu_power);
+		}
+	}
+
+	if (sensor_cpu_temp == NULL && !strcmp(sr->name, "cpu-temp")) {
+		if (wf_get_sensor(sr) == 0) {
+			sensor_cpu_temp = sr;
+			device_create_file(wf_smu_dev, &dev_attr_cpu_temp);
+		}
+	}
+
+	if (sensor_hd_temp == NULL && !strcmp(sr->name, "hd-temp")) {
+		if (wf_get_sensor(sr) == 0) {
+			sensor_hd_temp = sr;
+			device_create_file(wf_smu_dev, &dev_attr_hd_temp);
+		}
+	}
+
+	if (sensor_cpu_power && sensor_cpu_temp && sensor_hd_temp)
+		wf_smu_all_sensors_ok = 1;
+}
+
+
+static int wf_smu_notify(struct notifier_block *self,
+			       unsigned long event, void *data)
+{
+	switch(event) {
+	case WF_EVENT_NEW_CONTROL:
+		DBG("wf: new control %s detected\n",
+		    ((struct wf_control *)data)->name);
+		wf_smu_new_control(data);
+		wf_smu_readjust = 1;
+		break;
+	case WF_EVENT_NEW_SENSOR:
+		DBG("wf: new sensor %s detected\n",
+		    ((struct wf_sensor *)data)->name);
+		wf_smu_new_sensor(data);
+		break;
+	case WF_EVENT_TICK:
+		if (wf_smu_all_controls_ok && wf_smu_all_sensors_ok)
+			wf_smu_tick();
+	}
+
+	return 0;
+}
+
+static struct notifier_block wf_smu_events = {
+	.notifier_call	= wf_smu_notify,
+};
+
+static int wf_init_pm(void)
+{
+	struct smu_sdbp_header *hdr;
+
+	hdr = smu_get_sdb_partition(SMU_SDB_SENSORTREE_ID, NULL);
+	if (hdr != 0) {
+		struct smu_sdbp_sensortree *st =
+			(struct smu_sdbp_sensortree *)&hdr[1];
+		wf_smu_mach_model = st->model_id;
+	}
+
+	printk(KERN_INFO "windfarm: Initializing for iMacG5 model ID %d\n",
+	       wf_smu_mach_model);
+
+	return 0;
+}
+
+static int wf_smu_probe(struct device *ddev)
+{
+	wf_smu_dev = ddev;
+
+	wf_register_client(&wf_smu_events);
+
+	return 0;
+}
+
+static int wf_smu_remove(struct device *ddev)
+{
+	wf_unregister_client(&wf_smu_events);
+
+	/* XXX We don't have yet a guarantee that our callback isn't
+	 * in progress when returning from wf_unregister_client, so
+	 * we add an arbitrary delay. I'll have to fix that in the core
+	 */
+	msleep(1000);
+
+	/* Release all sensors */
+	/* One more crappy race: I don't think we have any guarantee here
+	 * that the attribute callback won't race with the sensor beeing
+	 * disposed of, and I'm not 100% certain what best way to deal
+	 * with that except by adding locks all over... I'll do that
+	 * eventually but heh, who ever rmmod this module anyway ?
+	 */
+	if (sensor_cpu_power) {
+		device_remove_file(wf_smu_dev, &dev_attr_cpu_power);
+		wf_put_sensor(sensor_cpu_power);
+	}
+	if (sensor_cpu_temp) {
+		device_remove_file(wf_smu_dev, &dev_attr_cpu_temp);
+		wf_put_sensor(sensor_cpu_temp);
+	}
+	if (sensor_hd_temp) {
+		device_remove_file(wf_smu_dev, &dev_attr_hd_temp);
+		wf_put_sensor(sensor_hd_temp);
+	}
+
+	/* Release all controls */
+	if (fan_cpu_main) {
+		device_remove_file(wf_smu_dev, &dev_attr_cpu_fan);
+		wf_put_control(fan_cpu_main);
+	}
+	if (fan_hd) {
+		device_remove_file(wf_smu_dev, &dev_attr_hd_fan);
+		wf_put_control(fan_hd);
+	}
+	if (fan_system) {
+		device_remove_file(wf_smu_dev, &dev_attr_sys_fan);
+		wf_put_control(fan_system);
+	}
+	if (cpufreq_clamp)
+		wf_put_control(cpufreq_clamp);
+
+	/* Destroy control loops state structures */
+	if (wf_smu_sys_fans)
+		kfree(wf_smu_sys_fans);
+	if (wf_smu_cpu_fans)
+		kfree(wf_smu_cpu_fans);
+
+	wf_smu_dev = NULL;
+
+	return 0;
+}
+
+static struct device_driver wf_smu_driver = {
+        .name = "windfarm",
+        .bus = &platform_bus_type,
+        .probe = wf_smu_probe,
+        .remove = wf_smu_remove,
+};
+
+
+static int __init wf_smu_init(void)
+{
+	int rc = -ENODEV;
+
+	if (machine_is_compatible("PowerMac8,1") ||
+	    machine_is_compatible("PowerMac8,2"))
+		rc = wf_init_pm();
+
+	if (rc == 0) {
+#ifdef MODULE
+		request_module("windfarm_smu_controls");
+		request_module("windfarm_smu_sensors");
+		request_module("windfarm_lm75_sensor");
+
+#endif /* MODULE */
+		driver_register(&wf_smu_driver);
+	}
+
+	return rc;
+}
+
+static void __exit wf_smu_exit(void)
+{
+
+	driver_unregister(&wf_smu_driver);
+}
+
+
+module_init(wf_smu_init);
+module_exit(wf_smu_exit);
+
+MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
+MODULE_DESCRIPTION("Thermal control logic for iMac G5");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/macintosh/windfarm_pm91.c b/drivers/macintosh/windfarm_pm91.c
new file mode 100644
index 0000000..43243cf
--- /dev/null
+++ b/drivers/macintosh/windfarm_pm91.c
@@ -0,0 +1,814 @@
+/*
+ * Windfarm PowerMac thermal control. SMU based 1 CPU desktop control loops
+ *
+ * (c) Copyright 2005 Benjamin Herrenschmidt, IBM Corp.
+ *                    <benh@kernel.crashing.org>
+ *
+ * Released under the term of the GNU GPL v2.
+ *
+ * The algorithm used is the PID control algorithm, used the same
+ * way the published Darwin code does, using the same values that
+ * are present in the Darwin 8.2 snapshot property lists (note however
+ * that none of the code has been re-used, it's a complete re-implementation
+ *
+ * The various control loops found in Darwin config file are:
+ *
+ * PowerMac9,1
+ * ===========
+ *
+ * Has 3 control loops: CPU fans is similar to PowerMac8,1 (though it doesn't
+ * try to play with other control loops fans). Drive bay is rather basic PID
+ * with one sensor and one fan. Slots area is a bit different as the Darwin
+ * driver is supposed to be capable of working in a special "AGP" mode which
+ * involves the presence of an AGP sensor and an AGP fan (possibly on the
+ * AGP card itself). I can't deal with that special mode as I don't have
+ * access to those additional sensor/fans for now (though ultimately, it would
+ * be possible to add sensor objects for them) so I'm only implementing the
+ * basic PCI slot control loop
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/wait.h>
+#include <linux/kmod.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <asm/prom.h>
+#include <asm/machdep.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/sections.h>
+#include <asm/smu.h>
+
+#include "windfarm.h"
+#include "windfarm_pid.h"
+
+#define VERSION "0.4"
+
+#undef DEBUG
+
+#ifdef DEBUG
+#define DBG(args...)	printk(args)
+#else
+#define DBG(args...)	do { } while(0)
+#endif
+
+/* define this to force CPU overtemp to 74 degree, useful for testing
+ * the overtemp code
+ */
+#undef HACKED_OVERTEMP
+
+static struct device *wf_smu_dev;
+
+/* Controls & sensors */
+static struct wf_sensor	*sensor_cpu_power;
+static struct wf_sensor	*sensor_cpu_temp;
+static struct wf_sensor	*sensor_hd_temp;
+static struct wf_sensor	*sensor_slots_power;
+static struct wf_control *fan_cpu_main;
+static struct wf_control *fan_cpu_second;
+static struct wf_control *fan_cpu_third;
+static struct wf_control *fan_hd;
+static struct wf_control *fan_slots;
+static struct wf_control *cpufreq_clamp;
+
+/* Set to kick the control loop into life */
+static int wf_smu_all_controls_ok, wf_smu_all_sensors_ok, wf_smu_started;
+
+/* Failure handling.. could be nicer */
+#define FAILURE_FAN		0x01
+#define FAILURE_SENSOR		0x02
+#define FAILURE_OVERTEMP	0x04
+
+static unsigned int wf_smu_failure_state;
+static int wf_smu_readjust, wf_smu_skipping;
+
+/*
+ * ****** CPU Fans Control Loop ******
+ *
+ */
+
+
+#define WF_SMU_CPU_FANS_INTERVAL	1
+#define WF_SMU_CPU_FANS_MAX_HISTORY	16
+
+/* State data used by the cpu fans control loop
+ */
+struct wf_smu_cpu_fans_state {
+	int			ticks;
+	s32			cpu_setpoint;
+	struct wf_cpu_pid_state	pid;
+};
+
+static struct wf_smu_cpu_fans_state *wf_smu_cpu_fans;
+
+
+
+/*
+ * ****** Drive Fan Control Loop ******
+ *
+ */
+
+struct wf_smu_drive_fans_state {
+	int			ticks;
+	s32			setpoint;
+	struct wf_pid_state	pid;
+};
+
+static struct wf_smu_drive_fans_state *wf_smu_drive_fans;
+
+/*
+ * ****** Slots Fan Control Loop ******
+ *
+ */
+
+struct wf_smu_slots_fans_state {
+	int			ticks;
+	s32			setpoint;
+	struct wf_pid_state	pid;
+};
+
+static struct wf_smu_slots_fans_state *wf_smu_slots_fans;
+
+/*
+ * ***** Implementation *****
+ *
+ */
+
+
+static void wf_smu_create_cpu_fans(void)
+{
+	struct wf_cpu_pid_param pid_param;
+	struct smu_sdbp_header *hdr;
+	struct smu_sdbp_cpupiddata *piddata;
+	struct smu_sdbp_fvt *fvt;
+	s32 tmax, tdelta, maxpow, powadj;
+
+	/* First, locate the PID params in SMU SBD */
+	hdr = smu_get_sdb_partition(SMU_SDB_CPUPIDDATA_ID, NULL);
+	if (hdr == 0) {
+		printk(KERN_WARNING "windfarm: CPU PID fan config not found "
+		       "max fan speed\n");
+		goto fail;
+	}
+	piddata = (struct smu_sdbp_cpupiddata *)&hdr[1];
+
+	/* Get the FVT params for operating point 0 (the only supported one
+	 * for now) in order to get tmax
+	 */
+	hdr = smu_get_sdb_partition(SMU_SDB_FVT_ID, NULL);
+	if (hdr) {
+		fvt = (struct smu_sdbp_fvt *)&hdr[1];
+		tmax = ((s32)fvt->maxtemp) << 16;
+	} else
+		tmax = 0x5e0000; /* 94 degree default */
+
+	/* Alloc & initialize state */
+	wf_smu_cpu_fans = kmalloc(sizeof(struct wf_smu_cpu_fans_state),
+				  GFP_KERNEL);
+	if (wf_smu_cpu_fans == NULL)
+		goto fail;
+       	wf_smu_cpu_fans->ticks = 1;
+
+	/* Fill PID params */
+	pid_param.interval = WF_SMU_CPU_FANS_INTERVAL;
+	pid_param.history_len = piddata->history_len;
+	if (pid_param.history_len > WF_CPU_PID_MAX_HISTORY) {
+		printk(KERN_WARNING "windfarm: History size overflow on "
+		       "CPU control loop (%d)\n", piddata->history_len);
+		pid_param.history_len = WF_CPU_PID_MAX_HISTORY;
+	}
+	pid_param.gd = piddata->gd;
+	pid_param.gp = piddata->gp;
+	pid_param.gr = piddata->gr / pid_param.history_len;
+
+	tdelta = ((s32)piddata->target_temp_delta) << 16;
+	maxpow = ((s32)piddata->max_power) << 16;
+	powadj = ((s32)piddata->power_adj) << 16;
+
+	pid_param.tmax = tmax;
+	pid_param.ttarget = tmax - tdelta;
+	pid_param.pmaxadj = maxpow - powadj;
+
+	pid_param.min = fan_cpu_main->ops->get_min(fan_cpu_main);
+	pid_param.max = fan_cpu_main->ops->get_max(fan_cpu_main);
+
+	wf_cpu_pid_init(&wf_smu_cpu_fans->pid, &pid_param);
+
+	DBG("wf: CPU Fan control initialized.\n");
+	DBG("    ttarged=%d.%03d, tmax=%d.%03d, min=%d RPM, max=%d RPM\n",
+	    FIX32TOPRINT(pid_param.ttarget), FIX32TOPRINT(pid_param.tmax),
+	    pid_param.min, pid_param.max);
+
+	return;
+
+ fail:
+	printk(KERN_WARNING "windfarm: CPU fan config not found\n"
+	       "for this machine model, max fan speed\n");
+
+	if (cpufreq_clamp)
+		wf_control_set_max(cpufreq_clamp);
+	if (fan_cpu_main)
+		wf_control_set_max(fan_cpu_main);
+}
+
+static void wf_smu_cpu_fans_tick(struct wf_smu_cpu_fans_state *st)
+{
+	s32 new_setpoint, temp, power;
+	int rc;
+
+	if (--st->ticks != 0) {
+		if (wf_smu_readjust)
+			goto readjust;
+		return;
+	}
+	st->ticks = WF_SMU_CPU_FANS_INTERVAL;
+
+	rc = sensor_cpu_temp->ops->get_value(sensor_cpu_temp, &temp);
+	if (rc) {
+		printk(KERN_WARNING "windfarm: CPU temp sensor error %d\n",
+		       rc);
+		wf_smu_failure_state |= FAILURE_SENSOR;
+		return;
+	}
+
+	rc = sensor_cpu_power->ops->get_value(sensor_cpu_power, &power);
+	if (rc) {
+		printk(KERN_WARNING "windfarm: CPU power sensor error %d\n",
+		       rc);
+		wf_smu_failure_state |= FAILURE_SENSOR;
+		return;
+	}
+
+	DBG("wf_smu: CPU Fans tick ! CPU temp: %d.%03d, power: %d.%03d\n",
+	    FIX32TOPRINT(temp), FIX32TOPRINT(power));
+
+#ifdef HACKED_OVERTEMP
+	if (temp > 0x4a0000)
+		wf_smu_failure_state |= FAILURE_OVERTEMP;
+#else
+	if (temp > st->pid.param.tmax)
+		wf_smu_failure_state |= FAILURE_OVERTEMP;
+#endif
+	new_setpoint = wf_cpu_pid_run(&st->pid, power, temp);
+
+	DBG("wf_smu: new_setpoint: %d RPM\n", (int)new_setpoint);
+
+	if (st->cpu_setpoint == new_setpoint)
+		return;
+	st->cpu_setpoint = new_setpoint;
+ readjust:
+	if (fan_cpu_main && wf_smu_failure_state == 0) {
+		rc = fan_cpu_main->ops->set_value(fan_cpu_main,
+						  st->cpu_setpoint);
+		if (rc) {
+			printk(KERN_WARNING "windfarm: CPU main fan"
+			       " error %d\n", rc);
+			wf_smu_failure_state |= FAILURE_FAN;
+		}
+	}
+	if (fan_cpu_second && wf_smu_failure_state == 0) {
+		rc = fan_cpu_second->ops->set_value(fan_cpu_second,
+						    st->cpu_setpoint);
+		if (rc) {
+			printk(KERN_WARNING "windfarm: CPU second fan"
+			       " error %d\n", rc);
+			wf_smu_failure_state |= FAILURE_FAN;
+		}
+	}
+	if (fan_cpu_third && wf_smu_failure_state == 0) {
+		rc = fan_cpu_main->ops->set_value(fan_cpu_third,
+						  st->cpu_setpoint);
+		if (rc) {
+			printk(KERN_WARNING "windfarm: CPU third fan"
+			       " error %d\n", rc);
+			wf_smu_failure_state |= FAILURE_FAN;
+		}
+	}
+}
+
+static void wf_smu_create_drive_fans(void)
+{
+	struct wf_pid_param param = {
+		.interval	= 5,
+		.history_len	= 2,
+		.gd		= 0x01e00000,
+		.gp		= 0x00500000,
+		.gr		= 0x00000000,
+		.itarget	= 0x00200000,
+	};
+
+	/* Alloc & initialize state */
+	wf_smu_drive_fans = kmalloc(sizeof(struct wf_smu_drive_fans_state),
+					GFP_KERNEL);
+	if (wf_smu_drive_fans == NULL) {
+		printk(KERN_WARNING "windfarm: Memory allocation error"
+		       " max fan speed\n");
+		goto fail;
+	}
+       	wf_smu_drive_fans->ticks = 1;
+
+	/* Fill PID params */
+	param.additive = (fan_hd->type == WF_CONTROL_RPM_FAN);
+	param.min = fan_hd->ops->get_min(fan_hd);
+	param.max = fan_hd->ops->get_max(fan_hd);
+	wf_pid_init(&wf_smu_drive_fans->pid, &param);
+
+	DBG("wf: Drive Fan control initialized.\n");
+	DBG("    itarged=%d.%03d, min=%d RPM, max=%d RPM\n",
+	    FIX32TOPRINT(param.itarget), param.min, param.max);
+	return;
+
+ fail:
+	if (fan_hd)
+		wf_control_set_max(fan_hd);
+}
+
+static void wf_smu_drive_fans_tick(struct wf_smu_drive_fans_state *st)
+{
+	s32 new_setpoint, temp;
+	int rc;
+
+	if (--st->ticks != 0) {
+		if (wf_smu_readjust)
+			goto readjust;
+		return;
+	}
+	st->ticks = st->pid.param.interval;
+
+	rc = sensor_hd_temp->ops->get_value(sensor_hd_temp, &temp);
+	if (rc) {
+		printk(KERN_WARNING "windfarm: HD temp sensor error %d\n",
+		       rc);
+		wf_smu_failure_state |= FAILURE_SENSOR;
+		return;
+	}
+
+	DBG("wf_smu: Drive Fans tick ! HD temp: %d.%03d\n",
+	    FIX32TOPRINT(temp));
+
+	if (temp > (st->pid.param.itarget + 0x50000))
+		wf_smu_failure_state |= FAILURE_OVERTEMP;
+
+	new_setpoint = wf_pid_run(&st->pid, temp);
+
+	DBG("wf_smu: new_setpoint: %d\n", (int)new_setpoint);
+
+	if (st->setpoint == new_setpoint)
+		return;
+	st->setpoint = new_setpoint;
+ readjust:
+	if (fan_hd && wf_smu_failure_state == 0) {
+		rc = fan_hd->ops->set_value(fan_hd, st->setpoint);
+		if (rc) {
+			printk(KERN_WARNING "windfarm: HD fan error %d\n",
+			       rc);
+			wf_smu_failure_state |= FAILURE_FAN;
+		}
+	}
+}
+
+static void wf_smu_create_slots_fans(void)
+{
+	struct wf_pid_param param = {
+		.interval	= 1,
+		.history_len	= 8,
+		.gd		= 0x00000000,
+		.gp		= 0x00000000,
+		.gr		= 0x00020000,
+		.itarget	= 0x00000000
+	};
+
+	/* Alloc & initialize state */
+	wf_smu_slots_fans = kmalloc(sizeof(struct wf_smu_slots_fans_state),
+					GFP_KERNEL);
+	if (wf_smu_slots_fans == NULL) {
+		printk(KERN_WARNING "windfarm: Memory allocation error"
+		       " max fan speed\n");
+		goto fail;
+	}
+       	wf_smu_slots_fans->ticks = 1;
+
+	/* Fill PID params */
+	param.additive = (fan_slots->type == WF_CONTROL_RPM_FAN);
+	param.min = fan_slots->ops->get_min(fan_slots);
+	param.max = fan_slots->ops->get_max(fan_slots);
+	wf_pid_init(&wf_smu_slots_fans->pid, &param);
+
+	DBG("wf: Slots Fan control initialized.\n");
+	DBG("    itarged=%d.%03d, min=%d RPM, max=%d RPM\n",
+	    FIX32TOPRINT(param.itarget), param.min, param.max);
+	return;
+
+ fail:
+	if (fan_slots)
+		wf_control_set_max(fan_slots);
+}
+
+static void wf_smu_slots_fans_tick(struct wf_smu_slots_fans_state *st)
+{
+	s32 new_setpoint, power;
+	int rc;
+
+	if (--st->ticks != 0) {
+		if (wf_smu_readjust)
+			goto readjust;
+		return;
+	}
+	st->ticks = st->pid.param.interval;
+
+	rc = sensor_slots_power->ops->get_value(sensor_slots_power, &power);
+	if (rc) {
+		printk(KERN_WARNING "windfarm: Slots power sensor error %d\n",
+		       rc);
+		wf_smu_failure_state |= FAILURE_SENSOR;
+		return;
+	}
+
+	DBG("wf_smu: Slots Fans tick ! Slots power: %d.%03d\n",
+	    FIX32TOPRINT(power));
+
+#if 0 /* Check what makes a good overtemp condition */
+	if (power > (st->pid.param.itarget + 0x50000))
+		wf_smu_failure_state |= FAILURE_OVERTEMP;
+#endif
+
+	new_setpoint = wf_pid_run(&st->pid, power);
+
+	DBG("wf_smu: new_setpoint: %d\n", (int)new_setpoint);
+
+	if (st->setpoint == new_setpoint)
+		return;
+	st->setpoint = new_setpoint;
+ readjust:
+	if (fan_slots && wf_smu_failure_state == 0) {
+		rc = fan_slots->ops->set_value(fan_slots, st->setpoint);
+		if (rc) {
+			printk(KERN_WARNING "windfarm: Slots fan error %d\n",
+			       rc);
+			wf_smu_failure_state |= FAILURE_FAN;
+		}
+	}
+}
+
+
+/*
+ * ****** Attributes ******
+ *
+ */
+
+#define BUILD_SHOW_FUNC_FIX(name, data)				\
+static ssize_t show_##name(struct device *dev,                  \
+			   struct device_attribute *attr,       \
+			   char *buf)	                        \
+{								\
+	ssize_t r;						\
+	s32 val = 0;                                            \
+	data->ops->get_value(data, &val);                       \
+	r = sprintf(buf, "%d.%03d", FIX32TOPRINT(val)); 	\
+	return r;						\
+}                                                               \
+static DEVICE_ATTR(name,S_IRUGO,show_##name, NULL);
+
+
+#define BUILD_SHOW_FUNC_INT(name, data)				\
+static ssize_t show_##name(struct device *dev,                  \
+			   struct device_attribute *attr,       \
+			   char *buf)	                        \
+{								\
+	s32 val = 0;                                            \
+	data->ops->get_value(data, &val);                       \
+	return sprintf(buf, "%d", val);  			\
+}                                                               \
+static DEVICE_ATTR(name,S_IRUGO,show_##name, NULL);
+
+BUILD_SHOW_FUNC_INT(cpu_fan, fan_cpu_main);
+BUILD_SHOW_FUNC_INT(hd_fan, fan_hd);
+BUILD_SHOW_FUNC_INT(slots_fan, fan_slots);
+
+BUILD_SHOW_FUNC_FIX(cpu_temp, sensor_cpu_temp);
+BUILD_SHOW_FUNC_FIX(cpu_power, sensor_cpu_power);
+BUILD_SHOW_FUNC_FIX(hd_temp, sensor_hd_temp);
+BUILD_SHOW_FUNC_FIX(slots_power, sensor_slots_power);
+
+/*
+ * ****** Setup / Init / Misc ... ******
+ *
+ */
+
+static void wf_smu_tick(void)
+{
+	unsigned int last_failure = wf_smu_failure_state;
+	unsigned int new_failure;
+
+	if (!wf_smu_started) {
+		DBG("wf: creating control loops !\n");
+		wf_smu_create_drive_fans();
+		wf_smu_create_slots_fans();
+		wf_smu_create_cpu_fans();
+		wf_smu_started = 1;
+	}
+
+	/* Skipping ticks */
+	if (wf_smu_skipping && --wf_smu_skipping)
+		return;
+
+	wf_smu_failure_state = 0;
+	if (wf_smu_drive_fans)
+		wf_smu_drive_fans_tick(wf_smu_drive_fans);
+	if (wf_smu_slots_fans)
+		wf_smu_slots_fans_tick(wf_smu_slots_fans);
+	if (wf_smu_cpu_fans)
+		wf_smu_cpu_fans_tick(wf_smu_cpu_fans);
+
+	wf_smu_readjust = 0;
+	new_failure = wf_smu_failure_state & ~last_failure;
+
+	/* If entering failure mode, clamp cpufreq and ramp all
+	 * fans to full speed.
+	 */
+	if (wf_smu_failure_state && !last_failure) {
+		if (cpufreq_clamp)
+			wf_control_set_max(cpufreq_clamp);
+		if (fan_cpu_main)
+			wf_control_set_max(fan_cpu_main);
+		if (fan_cpu_second)
+			wf_control_set_max(fan_cpu_second);
+		if (fan_cpu_third)
+			wf_control_set_max(fan_cpu_third);
+		if (fan_hd)
+			wf_control_set_max(fan_hd);
+		if (fan_slots)
+			wf_control_set_max(fan_slots);
+	}
+
+	/* If leaving failure mode, unclamp cpufreq and readjust
+	 * all fans on next iteration
+	 */
+	if (!wf_smu_failure_state && last_failure) {
+		if (cpufreq_clamp)
+			wf_control_set_min(cpufreq_clamp);
+		wf_smu_readjust = 1;
+	}
+
+	/* Overtemp condition detected, notify and start skipping a couple
+	 * ticks to let the temperature go down
+	 */
+	if (new_failure & FAILURE_OVERTEMP) {
+		wf_set_overtemp();
+		wf_smu_skipping = 2;
+	}
+
+	/* We only clear the overtemp condition if overtemp is cleared
+	 * _and_ no other failure is present. Since a sensor error will
+	 * clear the overtemp condition (can't measure temperature) at
+	 * the control loop levels, but we don't want to keep it clear
+	 * here in this case
+	 */
+	if (new_failure == 0 && last_failure & FAILURE_OVERTEMP)
+		wf_clear_overtemp();
+}
+
+
+static void wf_smu_new_control(struct wf_control *ct)
+{
+	if (wf_smu_all_controls_ok)
+		return;
+
+	if (fan_cpu_main == NULL && !strcmp(ct->name, "cpu-rear-fan-0")) {
+		if (wf_get_control(ct) == 0) {
+			fan_cpu_main = ct;
+			device_create_file(wf_smu_dev, &dev_attr_cpu_fan);
+		}
+	}
+
+	if (fan_cpu_second == NULL && !strcmp(ct->name, "cpu-rear-fan-1")) {
+		if (wf_get_control(ct) == 0)
+			fan_cpu_second = ct;
+	}
+
+	if (fan_cpu_third == NULL && !strcmp(ct->name, "cpu-front-fan-0")) {
+		if (wf_get_control(ct) == 0)
+			fan_cpu_third = ct;
+	}
+
+	if (cpufreq_clamp == NULL && !strcmp(ct->name, "cpufreq-clamp")) {
+		if (wf_get_control(ct) == 0)
+			cpufreq_clamp = ct;
+	}
+
+	if (fan_hd == NULL && !strcmp(ct->name, "drive-bay-fan")) {
+		if (wf_get_control(ct) == 0) {
+			fan_hd = ct;
+			device_create_file(wf_smu_dev, &dev_attr_hd_fan);
+		}
+	}
+
+	if (fan_slots == NULL && !strcmp(ct->name, "slots-fan")) {
+		if (wf_get_control(ct) == 0) {
+			fan_slots = ct;
+			device_create_file(wf_smu_dev, &dev_attr_slots_fan);
+		}
+	}
+
+	if (fan_cpu_main && (fan_cpu_second || fan_cpu_third) && fan_hd &&
+	    fan_slots && cpufreq_clamp)
+		wf_smu_all_controls_ok = 1;
+}
+
+static void wf_smu_new_sensor(struct wf_sensor *sr)
+{
+	if (wf_smu_all_sensors_ok)
+		return;
+
+	if (sensor_cpu_power == NULL && !strcmp(sr->name, "cpu-power")) {
+		if (wf_get_sensor(sr) == 0) {
+			sensor_cpu_power = sr;
+			device_create_file(wf_smu_dev, &dev_attr_cpu_power);
+		}
+	}
+
+	if (sensor_cpu_temp == NULL && !strcmp(sr->name, "cpu-temp")) {
+		if (wf_get_sensor(sr) == 0) {
+			sensor_cpu_temp = sr;
+			device_create_file(wf_smu_dev, &dev_attr_cpu_temp);
+		}
+	}
+
+	if (sensor_hd_temp == NULL && !strcmp(sr->name, "hd-temp")) {
+		if (wf_get_sensor(sr) == 0) {
+			sensor_hd_temp = sr;
+			device_create_file(wf_smu_dev, &dev_attr_hd_temp);
+		}
+	}
+
+	if (sensor_slots_power == NULL && !strcmp(sr->name, "slots-power")) {
+		if (wf_get_sensor(sr) == 0) {
+			sensor_slots_power = sr;
+			device_create_file(wf_smu_dev, &dev_attr_slots_power);
+		}
+	}
+
+	if (sensor_cpu_power && sensor_cpu_temp &&
+	    sensor_hd_temp && sensor_slots_power)
+		wf_smu_all_sensors_ok = 1;
+}
+
+
+static int wf_smu_notify(struct notifier_block *self,
+			       unsigned long event, void *data)
+{
+	switch(event) {
+	case WF_EVENT_NEW_CONTROL:
+		DBG("wf: new control %s detected\n",
+		    ((struct wf_control *)data)->name);
+		wf_smu_new_control(data);
+		wf_smu_readjust = 1;
+		break;
+	case WF_EVENT_NEW_SENSOR:
+		DBG("wf: new sensor %s detected\n",
+		    ((struct wf_sensor *)data)->name);
+		wf_smu_new_sensor(data);
+		break;
+	case WF_EVENT_TICK:
+		if (wf_smu_all_controls_ok && wf_smu_all_sensors_ok)
+			wf_smu_tick();
+	}
+
+	return 0;
+}
+
+static struct notifier_block wf_smu_events = {
+	.notifier_call	= wf_smu_notify,
+};
+
+static int wf_init_pm(void)
+{
+	printk(KERN_INFO "windfarm: Initializing for Desktop G5 model\n");
+
+	return 0;
+}
+
+static int wf_smu_probe(struct device *ddev)
+{
+	wf_smu_dev = ddev;
+
+	wf_register_client(&wf_smu_events);
+
+	return 0;
+}
+
+static int wf_smu_remove(struct device *ddev)
+{
+	wf_unregister_client(&wf_smu_events);
+
+	/* XXX We don't have yet a guarantee that our callback isn't
+	 * in progress when returning from wf_unregister_client, so
+	 * we add an arbitrary delay. I'll have to fix that in the core
+	 */
+	msleep(1000);
+
+	/* Release all sensors */
+	/* One more crappy race: I don't think we have any guarantee here
+	 * that the attribute callback won't race with the sensor beeing
+	 * disposed of, and I'm not 100% certain what best way to deal
+	 * with that except by adding locks all over... I'll do that
+	 * eventually but heh, who ever rmmod this module anyway ?
+	 */
+	if (sensor_cpu_power) {
+		device_remove_file(wf_smu_dev, &dev_attr_cpu_power);
+		wf_put_sensor(sensor_cpu_power);
+	}
+	if (sensor_cpu_temp) {
+		device_remove_file(wf_smu_dev, &dev_attr_cpu_temp);
+		wf_put_sensor(sensor_cpu_temp);
+	}
+	if (sensor_hd_temp) {
+		device_remove_file(wf_smu_dev, &dev_attr_hd_temp);
+		wf_put_sensor(sensor_hd_temp);
+	}
+	if (sensor_slots_power) {
+		device_remove_file(wf_smu_dev, &dev_attr_slots_power);
+		wf_put_sensor(sensor_slots_power);
+	}
+
+	/* Release all controls */
+	if (fan_cpu_main) {
+		device_remove_file(wf_smu_dev, &dev_attr_cpu_fan);
+		wf_put_control(fan_cpu_main);
+	}
+	if (fan_cpu_second)
+		wf_put_control(fan_cpu_second);
+	if (fan_cpu_third)
+		wf_put_control(fan_cpu_third);
+	if (fan_hd) {
+		device_remove_file(wf_smu_dev, &dev_attr_hd_fan);
+		wf_put_control(fan_hd);
+	}
+	if (fan_slots) {
+		device_remove_file(wf_smu_dev, &dev_attr_slots_fan);
+		wf_put_control(fan_slots);
+	}
+	if (cpufreq_clamp)
+		wf_put_control(cpufreq_clamp);
+
+	/* Destroy control loops state structures */
+	if (wf_smu_slots_fans)
+		kfree(wf_smu_cpu_fans);
+	if (wf_smu_drive_fans)
+		kfree(wf_smu_cpu_fans);
+	if (wf_smu_cpu_fans)
+		kfree(wf_smu_cpu_fans);
+
+	wf_smu_dev = NULL;
+
+	return 0;
+}
+
+static struct device_driver wf_smu_driver = {
+        .name = "windfarm",
+        .bus = &platform_bus_type,
+        .probe = wf_smu_probe,
+        .remove = wf_smu_remove,
+};
+
+
+static int __init wf_smu_init(void)
+{
+	int rc = -ENODEV;
+
+	if (machine_is_compatible("PowerMac9,1"))
+		rc = wf_init_pm();
+
+	if (rc == 0) {
+#ifdef MODULE
+		request_module("windfarm_smu_controls");
+		request_module("windfarm_smu_sensors");
+		request_module("windfarm_lm75_sensor");
+
+#endif /* MODULE */
+		driver_register(&wf_smu_driver);
+	}
+
+	return rc;
+}
+
+static void __exit wf_smu_exit(void)
+{
+
+	driver_unregister(&wf_smu_driver);
+}
+
+
+module_init(wf_smu_init);
+module_exit(wf_smu_exit);
+
+MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
+MODULE_DESCRIPTION("Thermal control logic for PowerMac9,1");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/macintosh/windfarm_smu_controls.c b/drivers/macintosh/windfarm_smu_controls.c
new file mode 100644
index 0000000..2c3158c
--- /dev/null
+++ b/drivers/macintosh/windfarm_smu_controls.c
@@ -0,0 +1,282 @@
+/*
+ * Windfarm PowerMac thermal control. SMU based controls
+ *
+ * (c) Copyright 2005 Benjamin Herrenschmidt, IBM Corp.
+ *                    <benh@kernel.crashing.org>
+ *
+ * Released under the term of the GNU GPL v2.
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/wait.h>
+#include <asm/prom.h>
+#include <asm/machdep.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/sections.h>
+#include <asm/smu.h>
+
+#include "windfarm.h"
+
+#define VERSION "0.3"
+
+#undef DEBUG
+
+#ifdef DEBUG
+#define DBG(args...)	printk(args)
+#else
+#define DBG(args...)	do { } while(0)
+#endif
+
+/*
+ * SMU fans control object
+ */
+
+static LIST_HEAD(smu_fans);
+
+struct smu_fan_control {
+	struct list_head	link;
+	int    			fan_type;	/* 0 = rpm, 1 = pwm */
+	u32			reg;		/* index in SMU */
+	s32			value;		/* current value */
+	s32			min, max;	/* min/max values */
+	struct wf_control	ctrl;
+};
+#define to_smu_fan(c) container_of(c, struct smu_fan_control, ctrl)
+
+static int smu_set_fan(int pwm, u8 id, u16 value)
+{
+	struct smu_cmd cmd;
+	u8 buffer[16];
+	DECLARE_COMPLETION(comp);
+	int rc;
+
+	/* Fill SMU command structure */
+	cmd.cmd = SMU_CMD_FAN_COMMAND;
+	cmd.data_len = 14;
+	cmd.reply_len = 16;
+	cmd.data_buf = cmd.reply_buf = buffer;
+	cmd.status = 0;
+	cmd.done = smu_done_complete;
+	cmd.misc = &comp;
+
+	/* Fill argument buffer */
+	memset(buffer, 0, 16);
+	buffer[0] = pwm ? 0x10 : 0x00;
+	buffer[1] = 0x01 << id;
+	*((u16 *)&buffer[2 + id * 2]) = value;
+
+	rc = smu_queue_cmd(&cmd);
+	if (rc)
+		return rc;
+	wait_for_completion(&comp);
+	return cmd.status;
+}
+
+static void smu_fan_release(struct wf_control *ct)
+{
+	struct smu_fan_control *fct = to_smu_fan(ct);
+
+	kfree(fct);
+}
+
+static int smu_fan_set(struct wf_control *ct, s32 value)
+{
+	struct smu_fan_control *fct = to_smu_fan(ct);
+
+	if (value < fct->min)
+		value = fct->min;
+	if (value > fct->max)
+		value = fct->max;
+	fct->value = value;
+
+	return smu_set_fan(fct->fan_type, fct->reg, value);
+}
+
+static int smu_fan_get(struct wf_control *ct, s32 *value)
+{
+	struct smu_fan_control *fct = to_smu_fan(ct);
+	*value = fct->value; /* todo: read from SMU */
+	return 0;
+}
+
+static s32 smu_fan_min(struct wf_control *ct)
+{
+	struct smu_fan_control *fct = to_smu_fan(ct);
+	return fct->min;
+}
+
+static s32 smu_fan_max(struct wf_control *ct)
+{
+	struct smu_fan_control *fct = to_smu_fan(ct);
+	return fct->max;
+}
+
+static struct wf_control_ops smu_fan_ops = {
+	.set_value	= smu_fan_set,
+	.get_value	= smu_fan_get,
+	.get_min	= smu_fan_min,
+	.get_max	= smu_fan_max,
+	.release	= smu_fan_release,
+	.owner		= THIS_MODULE,
+};
+
+static struct smu_fan_control *smu_fan_create(struct device_node *node,
+					      int pwm_fan)
+{
+	struct smu_fan_control *fct;
+	s32 *v; u32 *reg;
+	char *l;
+
+	fct = kmalloc(sizeof(struct smu_fan_control), GFP_KERNEL);
+	if (fct == NULL)
+		return NULL;
+	fct->ctrl.ops = &smu_fan_ops;
+	l = (char *)get_property(node, "location", NULL);
+	if (l == NULL)
+		goto fail;
+
+	fct->fan_type = pwm_fan;
+	fct->ctrl.type = pwm_fan ? WF_CONTROL_PWM_FAN : WF_CONTROL_RPM_FAN;
+
+	/* We use the name & location here the same way we do for SMU sensors,
+	 * see the comment in windfarm_smu_sensors.c. The locations are a bit
+	 * less consistent here between the iMac and the desktop models, but
+	 * that is good enough for our needs for now at least.
+	 *
+	 * One problem though is that Apple seem to be inconsistent with case
+	 * and the kernel doesn't have strcasecmp =P
+	 */
+
+	fct->ctrl.name = NULL;
+
+	/* Names used on desktop models */
+	if (!strcmp(l, "Rear Fan 0") || !strcmp(l, "Rear Fan") ||
+	    !strcmp(l, "Rear fan 0") || !strcmp(l, "Rear fan"))
+		fct->ctrl.name = "cpu-rear-fan-0";
+	else if (!strcmp(l, "Rear Fan 1") || !strcmp(l, "Rear fan 1"))
+		fct->ctrl.name = "cpu-rear-fan-1";
+	else if (!strcmp(l, "Front Fan 0") || !strcmp(l, "Front Fan") ||
+		 !strcmp(l, "Front fan 0") || !strcmp(l, "Front fan"))
+		fct->ctrl.name = "cpu-front-fan-0";
+	else if (!strcmp(l, "Front Fan 1") || !strcmp(l, "Front fan 1"))
+		fct->ctrl.name = "cpu-front-fan-1";
+	else if (!strcmp(l, "Slots Fan") || !strcmp(l, "Slots fan"))
+		fct->ctrl.name = "slots-fan";
+	else if (!strcmp(l, "Drive Bay") || !strcmp(l, "Drive bay"))
+		fct->ctrl.name = "drive-bay-fan";
+
+	/* Names used on iMac models */
+	if (!strcmp(l, "System Fan") || !strcmp(l, "System fan"))
+		fct->ctrl.name = "system-fan";
+	else if (!strcmp(l, "CPU Fan") || !strcmp(l, "CPU fan"))
+		fct->ctrl.name = "cpu-fan";
+	else if (!strcmp(l, "Hard Drive") || !strcmp(l, "Hard drive"))
+		fct->ctrl.name = "drive-bay-fan";
+
+	/* Unrecognized fan, bail out */
+	if (fct->ctrl.name == NULL)
+		goto fail;
+
+	/* Get min & max values*/
+	v = (s32 *)get_property(node, "min-value", NULL);
+	if (v == NULL)
+		goto fail;
+	fct->min = *v;
+	v = (s32 *)get_property(node, "max-value", NULL);
+	if (v == NULL)
+		goto fail;
+	fct->max = *v;
+
+	/* Get "reg" value */
+	reg = (u32 *)get_property(node, "reg", NULL);
+	if (reg == NULL)
+		goto fail;
+	fct->reg = *reg;
+
+	if (wf_register_control(&fct->ctrl))
+		goto fail;
+
+	return fct;
+ fail:
+	kfree(fct);
+	return NULL;
+}
+
+
+static int __init smu_controls_init(void)
+{
+	struct device_node *smu, *fans, *fan;
+
+	if (!smu_present())
+		return -ENODEV;
+
+	smu = of_find_node_by_type(NULL, "smu");
+	if (smu == NULL)
+		return -ENODEV;
+
+	/* Look for RPM fans */
+	for (fans = NULL; (fans = of_get_next_child(smu, fans)) != NULL;)
+		if (!strcmp(fans->name, "rpm-fans"))
+			break;
+	for (fan = NULL;
+	     fans && (fan = of_get_next_child(fans, fan)) != NULL;) {
+		struct smu_fan_control *fct;
+
+		fct = smu_fan_create(fan, 0);
+		if (fct == NULL) {
+			printk(KERN_WARNING "windfarm: Failed to create SMU "
+			       "RPM fan %s\n", fan->name);
+			continue;
+		}
+		list_add(&fct->link, &smu_fans);
+	}
+	of_node_put(fans);
+
+
+	/* Look for PWM fans */
+	for (fans = NULL; (fans = of_get_next_child(smu, fans)) != NULL;)
+		if (!strcmp(fans->name, "pwm-fans"))
+			break;
+	for (fan = NULL;
+	     fans && (fan = of_get_next_child(fans, fan)) != NULL;) {
+		struct smu_fan_control *fct;
+
+		fct = smu_fan_create(fan, 1);
+		if (fct == NULL) {
+			printk(KERN_WARNING "windfarm: Failed to create SMU "
+			       "PWM fan %s\n", fan->name);
+			continue;
+		}
+		list_add(&fct->link, &smu_fans);
+	}
+	of_node_put(fans);
+	of_node_put(smu);
+
+	return 0;
+}
+
+static void __exit smu_controls_exit(void)
+{
+	struct smu_fan_control *fct;
+
+	while (!list_empty(&smu_fans)) {
+		fct = list_entry(smu_fans.next, struct smu_fan_control, link);
+		list_del(&fct->link);
+		wf_unregister_control(&fct->ctrl);
+	}
+}
+
+
+module_init(smu_controls_init);
+module_exit(smu_controls_exit);
+
+MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
+MODULE_DESCRIPTION("SMU control objects for PowerMacs thermal control");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/macintosh/windfarm_smu_sensors.c b/drivers/macintosh/windfarm_smu_sensors.c
new file mode 100644
index 0000000..b558cc2
--- /dev/null
+++ b/drivers/macintosh/windfarm_smu_sensors.c
@@ -0,0 +1,479 @@
+/*
+ * Windfarm PowerMac thermal control. SMU based sensors
+ *
+ * (c) Copyright 2005 Benjamin Herrenschmidt, IBM Corp.
+ *                    <benh@kernel.crashing.org>
+ *
+ * Released under the term of the GNU GPL v2.
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/wait.h>
+#include <asm/prom.h>
+#include <asm/machdep.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/sections.h>
+#include <asm/smu.h>
+
+#include "windfarm.h"
+
+#define VERSION "0.2"
+
+#undef DEBUG
+
+#ifdef DEBUG
+#define DBG(args...)	printk(args)
+#else
+#define DBG(args...)	do { } while(0)
+#endif
+
+/*
+ * Various SMU "partitions" calibration objects for which we
+ * keep pointers here for use by bits & pieces of the driver
+ */
+static struct smu_sdbp_cpuvcp *cpuvcp;
+static int  cpuvcp_version;
+static struct smu_sdbp_cpudiode *cpudiode;
+static struct smu_sdbp_slotspow *slotspow;
+static u8 *debugswitches;
+
+/*
+ * SMU basic sensors objects
+ */
+
+static LIST_HEAD(smu_ads);
+
+struct smu_ad_sensor {
+	struct list_head	link;
+	u32			reg;		/* index in SMU */
+	struct wf_sensor	sens;
+};
+#define to_smu_ads(c) container_of(c, struct smu_ad_sensor, sens)
+
+static void smu_ads_release(struct wf_sensor *sr)
+{
+	struct smu_ad_sensor *ads = to_smu_ads(sr);
+
+	kfree(ads);
+}
+
+static int smu_read_adc(u8 id, s32 *value)
+{
+	struct smu_simple_cmd	cmd;
+	DECLARE_COMPLETION(comp);
+	int rc;
+
+	rc = smu_queue_simple(&cmd, SMU_CMD_READ_ADC, 1,
+			      smu_done_complete, &comp, id);
+	if (rc)
+		return rc;
+	wait_for_completion(&comp);
+	if (cmd.cmd.status != 0)
+		return cmd.cmd.status;
+	if (cmd.cmd.reply_len != 2) {
+		printk(KERN_ERR "winfarm: read ADC 0x%x returned %d bytes !\n",
+		       id, cmd.cmd.reply_len);
+		return -EIO;
+	}
+	*value = *((u16 *)cmd.buffer);
+	return 0;
+}
+
+static int smu_cputemp_get(struct wf_sensor *sr, s32 *value)
+{
+	struct smu_ad_sensor *ads = to_smu_ads(sr);
+	int rc;
+	s32 val;
+	s64 scaled;
+
+	rc = smu_read_adc(ads->reg, &val);
+	if (rc) {
+		printk(KERN_ERR "windfarm: read CPU temp failed, err %d\n",
+		       rc);
+		return rc;
+	}
+
+	/* Ok, we have to scale & adjust, taking units into account */
+	scaled = (s64)(((u64)val) * (u64)cpudiode->m_value);
+	scaled >>= 3;
+	scaled += ((s64)cpudiode->b_value) << 9;
+	*value = (s32)(scaled << 1);
+
+	return 0;
+}
+
+static int smu_cpuamp_get(struct wf_sensor *sr, s32 *value)
+{
+	struct smu_ad_sensor *ads = to_smu_ads(sr);
+	s32 val, scaled;
+	int rc;
+
+	rc = smu_read_adc(ads->reg, &val);
+	if (rc) {
+		printk(KERN_ERR "windfarm: read CPU current failed, err %d\n",
+		       rc);
+		return rc;
+	}
+
+	/* Ok, we have to scale & adjust, taking units into account */
+	scaled = (s32)(val * (u32)cpuvcp->curr_scale);
+	scaled += (s32)cpuvcp->curr_offset;
+	*value = scaled << 4;
+
+	return 0;
+}
+
+static int smu_cpuvolt_get(struct wf_sensor *sr, s32 *value)
+{
+	struct smu_ad_sensor *ads = to_smu_ads(sr);
+	s32 val, scaled;
+	int rc;
+
+	rc = smu_read_adc(ads->reg, &val);
+	if (rc) {
+		printk(KERN_ERR "windfarm: read CPU voltage failed, err %d\n",
+		       rc);
+		return rc;
+	}
+
+	/* Ok, we have to scale & adjust, taking units into account */
+	scaled = (s32)(val * (u32)cpuvcp->volt_scale);
+	scaled += (s32)cpuvcp->volt_offset;
+	*value = scaled << 4;
+
+	return 0;
+}
+
+static int smu_slotspow_get(struct wf_sensor *sr, s32 *value)
+{
+	struct smu_ad_sensor *ads = to_smu_ads(sr);
+	s32 val, scaled;
+	int rc;
+
+	rc = smu_read_adc(ads->reg, &val);
+	if (rc) {
+		printk(KERN_ERR "windfarm: read slots power failed, err %d\n",
+		       rc);
+		return rc;
+	}
+
+	/* Ok, we have to scale & adjust, taking units into account */
+	scaled = (s32)(val * (u32)slotspow->pow_scale);
+	scaled += (s32)slotspow->pow_offset;
+	*value = scaled << 4;
+
+	return 0;
+}
+
+
+static struct wf_sensor_ops smu_cputemp_ops = {
+	.get_value	= smu_cputemp_get,
+	.release	= smu_ads_release,
+	.owner		= THIS_MODULE,
+};
+static struct wf_sensor_ops smu_cpuamp_ops = {
+	.get_value	= smu_cpuamp_get,
+	.release	= smu_ads_release,
+	.owner		= THIS_MODULE,
+};
+static struct wf_sensor_ops smu_cpuvolt_ops = {
+	.get_value	= smu_cpuvolt_get,
+	.release	= smu_ads_release,
+	.owner		= THIS_MODULE,
+};
+static struct wf_sensor_ops smu_slotspow_ops = {
+	.get_value	= smu_slotspow_get,
+	.release	= smu_ads_release,
+	.owner		= THIS_MODULE,
+};
+
+
+static struct smu_ad_sensor *smu_ads_create(struct device_node *node)
+{
+	struct smu_ad_sensor *ads;
+	char *c, *l;
+	u32 *v;
+
+	ads = kmalloc(sizeof(struct smu_ad_sensor), GFP_KERNEL);
+	if (ads == NULL)
+		return NULL;
+	c = (char *)get_property(node, "device_type", NULL);
+	l = (char *)get_property(node, "location", NULL);
+	if (c == NULL || l == NULL)
+		goto fail;
+
+	/* We currently pick the sensors based on the OF name and location
+	 * properties, while Darwin uses the sensor-id's.
+	 * The problem with the IDs is that they are model specific while it
+	 * looks like apple has been doing a reasonably good job at keeping
+	 * the names and locations consistents so I'll stick with the names
+	 * and locations for now.
+	 */
+	if (!strcmp(c, "temp-sensor") &&
+	    !strcmp(l, "CPU T-Diode")) {
+		ads->sens.ops = &smu_cputemp_ops;
+		ads->sens.name = "cpu-temp";
+	} else if (!strcmp(c, "current-sensor") &&
+		   !strcmp(l, "CPU Current")) {
+		ads->sens.ops = &smu_cpuamp_ops;
+		ads->sens.name = "cpu-current";
+	} else if (!strcmp(c, "voltage-sensor") &&
+		   !strcmp(l, "CPU Voltage")) {
+		ads->sens.ops = &smu_cpuvolt_ops;
+		ads->sens.name = "cpu-voltage";
+	} else if (!strcmp(c, "power-sensor") &&
+		   !strcmp(l, "Slots Power")) {
+		ads->sens.ops = &smu_slotspow_ops;
+		ads->sens.name = "slots-power";
+		if (slotspow == NULL) {
+			DBG("wf: slotspow partition (%02x) not found\n",
+			    SMU_SDB_SLOTSPOW_ID);
+			goto fail;
+		}
+	} else
+		goto fail;
+
+	v = (u32 *)get_property(node, "reg", NULL);
+	if (v == NULL)
+		goto fail;
+	ads->reg = *v;
+
+	if (wf_register_sensor(&ads->sens))
+		goto fail;
+	return ads;
+ fail:
+	kfree(ads);
+	return NULL;
+}
+
+/*
+ * SMU Power combo sensor object
+ */
+
+struct smu_cpu_power_sensor {
+	struct list_head	link;
+	struct wf_sensor	*volts;
+	struct wf_sensor	*amps;
+	int			fake_volts : 1;
+	int			quadratic : 1;
+	struct wf_sensor	sens;
+};
+#define to_smu_cpu_power(c) container_of(c, struct smu_cpu_power_sensor, sens)
+
+static struct smu_cpu_power_sensor *smu_cpu_power;
+
+static void smu_cpu_power_release(struct wf_sensor *sr)
+{
+	struct smu_cpu_power_sensor *pow = to_smu_cpu_power(sr);
+
+	if (pow->volts)
+		wf_put_sensor(pow->volts);
+	if (pow->amps)
+		wf_put_sensor(pow->amps);
+	kfree(pow);
+}
+
+static int smu_cpu_power_get(struct wf_sensor *sr, s32 *value)
+{
+	struct smu_cpu_power_sensor *pow = to_smu_cpu_power(sr);
+	s32 volts, amps, power;
+	u64 tmps, tmpa, tmpb;
+	int rc;
+
+	rc = pow->amps->ops->get_value(pow->amps, &amps);
+	if (rc)
+		return rc;
+
+	if (pow->fake_volts) {
+		*value = amps * 12 - 0x30000;
+		return 0;
+	}
+
+	rc = pow->volts->ops->get_value(pow->volts, &volts);
+	if (rc)
+		return rc;
+
+	power = (s32)((((u64)volts) * ((u64)amps)) >> 16);
+	if (!pow->quadratic) {
+		*value = power;
+		return 0;
+	}
+	tmps = (((u64)power) * ((u64)power)) >> 16;
+	tmpa = ((u64)cpuvcp->power_quads[0]) * tmps;
+	tmpb = ((u64)cpuvcp->power_quads[1]) * ((u64)power);
+	*value = (tmpa >> 28) + (tmpb >> 28) + (cpuvcp->power_quads[2] >> 12);
+
+	return 0;
+}
+
+static struct wf_sensor_ops smu_cpu_power_ops = {
+	.get_value	= smu_cpu_power_get,
+	.release	= smu_cpu_power_release,
+	.owner		= THIS_MODULE,
+};
+
+
+static struct smu_cpu_power_sensor *
+smu_cpu_power_create(struct wf_sensor *volts, struct wf_sensor *amps)
+{
+	struct smu_cpu_power_sensor *pow;
+
+	pow = kmalloc(sizeof(struct smu_cpu_power_sensor), GFP_KERNEL);
+	if (pow == NULL)
+		return NULL;
+	pow->sens.ops = &smu_cpu_power_ops;
+	pow->sens.name = "cpu-power";
+
+	wf_get_sensor(volts);
+	pow->volts = volts;
+	wf_get_sensor(amps);
+	pow->amps = amps;
+
+	/* Some early machines need a faked voltage */
+	if (debugswitches && ((*debugswitches) & 0x80)) {
+		printk(KERN_INFO "windfarm: CPU Power sensor using faked"
+		       " voltage !\n");
+		pow->fake_volts = 1;
+	} else
+		pow->fake_volts = 0;
+
+	/* Try to use quadratic transforms on PowerMac8,1 and 9,1 for now,
+	 * I yet have to figure out what's up with 8,2 and will have to
+	 * adjust for later, unless we can 100% trust the SDB partition...
+	 */
+	if ((machine_is_compatible("PowerMac8,1") ||
+	     machine_is_compatible("PowerMac8,2") ||
+	     machine_is_compatible("PowerMac9,1")) &&
+	    cpuvcp_version >= 2) {
+		pow->quadratic = 1;
+		DBG("windfarm: CPU Power using quadratic transform\n");
+	} else
+		pow->quadratic = 0;
+
+	if (wf_register_sensor(&pow->sens))
+		goto fail;
+	return pow;
+ fail:
+	kfree(pow);
+	return NULL;
+}
+
+static int smu_fetch_param_partitions(void)
+{
+	struct smu_sdbp_header *hdr;
+
+	/* Get CPU voltage/current/power calibration data */
+	hdr = smu_get_sdb_partition(SMU_SDB_CPUVCP_ID, NULL);
+	if (hdr == NULL) {
+		DBG("wf: cpuvcp partition (%02x) not found\n",
+		    SMU_SDB_CPUVCP_ID);
+		return -ENODEV;
+	}
+	cpuvcp = (struct smu_sdbp_cpuvcp *)&hdr[1];
+	/* Keep version around */
+	cpuvcp_version = hdr->version;
+
+	/* Get CPU diode calibration data */
+	hdr = smu_get_sdb_partition(SMU_SDB_CPUDIODE_ID, NULL);
+	if (hdr == NULL) {
+		DBG("wf: cpudiode partition (%02x) not found\n",
+		    SMU_SDB_CPUDIODE_ID);
+		return -ENODEV;
+	}
+	cpudiode = (struct smu_sdbp_cpudiode *)&hdr[1];
+
+	/* Get slots power calibration data if any */
+	hdr = smu_get_sdb_partition(SMU_SDB_SLOTSPOW_ID, NULL);
+	if (hdr != NULL)
+		slotspow = (struct smu_sdbp_slotspow *)&hdr[1];
+
+	/* Get debug switches if any */
+	hdr = smu_get_sdb_partition(SMU_SDB_DEBUG_SWITCHES_ID, NULL);
+	if (hdr != NULL)
+		debugswitches = (u8 *)&hdr[1];
+
+	return 0;
+}
+
+static int __init smu_sensors_init(void)
+{
+	struct device_node *smu, *sensors, *s;
+	struct smu_ad_sensor *volt_sensor = NULL, *curr_sensor = NULL;
+	int rc;
+
+	if (!smu_present())
+		return -ENODEV;
+
+	/* Get parameters partitions */
+	rc = smu_fetch_param_partitions();
+	if (rc)
+		return rc;
+
+	smu = of_find_node_by_type(NULL, "smu");
+	if (smu == NULL)
+		return -ENODEV;
+
+	/* Look for sensors subdir */
+	for (sensors = NULL;
+	     (sensors = of_get_next_child(smu, sensors)) != NULL;)
+		if (!strcmp(sensors->name, "sensors"))
+			break;
+
+	of_node_put(smu);
+
+	/* Create basic sensors */
+	for (s = NULL;
+	     sensors && (s = of_get_next_child(sensors, s)) != NULL;) {
+		struct smu_ad_sensor *ads;
+
+		ads = smu_ads_create(s);
+		if (ads == NULL)
+			continue;
+		list_add(&ads->link, &smu_ads);
+		/* keep track of cpu voltage & current */
+		if (!strcmp(ads->sens.name, "cpu-voltage"))
+			volt_sensor = ads;
+		else if (!strcmp(ads->sens.name, "cpu-current"))
+			curr_sensor = ads;
+	}
+
+	of_node_put(sensors);
+
+	/* Create CPU power sensor if possible */
+	if (volt_sensor && curr_sensor)
+		smu_cpu_power = smu_cpu_power_create(&volt_sensor->sens,
+						     &curr_sensor->sens);
+
+	return 0;
+}
+
+static void __exit smu_sensors_exit(void)
+{
+	struct smu_ad_sensor *ads;
+
+	/* dispose of power sensor */
+	if (smu_cpu_power)
+		wf_unregister_sensor(&smu_cpu_power->sens);
+
+	/* dispose of basic sensors */
+	while (!list_empty(&smu_ads)) {
+		ads = list_entry(smu_ads.next, struct smu_ad_sensor, link);
+		list_del(&ads->link);
+		wf_unregister_sensor(&ads->sens);
+	}
+}
+
+
+module_init(smu_sensors_init);
+module_exit(smu_sensors_exit);
+
+MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
+MODULE_DESCRIPTION("SMU sensor objects for PowerMacs thermal control");
+MODULE_LICENSE("GPL");
+
diff --git a/fs/proc/proc_devtree.c b/fs/proc/proc_devtree.c
index 6fd57f1..fb117b7 100644
--- a/fs/proc/proc_devtree.c
+++ b/fs/proc/proc_devtree.c
@@ -49,6 +49,39 @@
  */
 
 /*
+ * Add a property to a node
+ */
+static struct proc_dir_entry *
+__proc_device_tree_add_prop(struct proc_dir_entry *de, struct property *pp)
+{
+	struct proc_dir_entry *ent;
+
+	/*
+	 * Unfortunately proc_register puts each new entry
+	 * at the beginning of the list.  So we rearrange them.
+	 */
+	ent = create_proc_read_entry(pp->name,
+				     strncmp(pp->name, "security-", 9)
+				     ? S_IRUGO : S_IRUSR, de,
+				     property_read_proc, pp);
+	if (ent == NULL)
+		return NULL;
+
+	if (!strncmp(pp->name, "security-", 9))
+		ent->size = 0; /* don't leak number of password chars */
+	else
+		ent->size = pp->length;
+
+	return ent;
+}
+
+
+void proc_device_tree_add_prop(struct proc_dir_entry *pde, struct property *prop)
+{
+	__proc_device_tree_add_prop(pde, prop);
+}
+
+/*
  * Process a node, adding entries for its children and its properties.
  */
 void proc_device_tree_add_node(struct device_node *np,
@@ -57,11 +90,9 @@
 	struct property *pp;
 	struct proc_dir_entry *ent;
 	struct device_node *child;
-	struct proc_dir_entry *list = NULL, **lastp;
 	const char *p;
 
 	set_node_proc_entry(np, de);
-	lastp = &list;
 	for (child = NULL; (child = of_get_next_child(np, child));) {
 		p = strrchr(child->full_name, '/');
 		if (!p)
@@ -71,9 +102,6 @@
 		ent = proc_mkdir(p, de);
 		if (ent == 0)
 			break;
-		*lastp = ent;
-		ent->next = NULL;
-		lastp = &ent->next;
 		proc_device_tree_add_node(child, ent);
 	}
 	of_node_put(child);
@@ -84,7 +112,7 @@
 		 * properties are quite unimportant for us though, thus we
 		 * simply "skip" them here, but we do have to check.
 		 */
-		for (ent = list; ent != NULL; ent = ent->next)
+		for (ent = de->subdir; ent != NULL; ent = ent->next)
 			if (!strcmp(ent->name, pp->name))
 				break;
 		if (ent != NULL) {
@@ -94,25 +122,10 @@
 			continue;
 		}
 
-		/*
-		 * Unfortunately proc_register puts each new entry
-		 * at the beginning of the list.  So we rearrange them.
-		 */
-		ent = create_proc_read_entry(pp->name,
-					     strncmp(pp->name, "security-", 9)
-					     ? S_IRUGO : S_IRUSR, de,
-					     property_read_proc, pp);
+		ent = __proc_device_tree_add_prop(de, pp);
 		if (ent == 0)
 			break;
-		if (!strncmp(pp->name, "security-", 9))
-		     ent->size = 0; /* don't leak number of password chars */
-		else
-		     ent->size = pp->length;
-		ent->next = NULL;
-		*lastp = ent;
-		lastp = &ent->next;
 	}
-	de->subdir = list;
 }
 
 /*
diff --git a/include/asm-ppc/ide.h b/include/asm-powerpc/ide.h
similarity index 83%
rename from include/asm-ppc/ide.h
rename to include/asm-powerpc/ide.h
index 7d6e659..da5f640 100644
--- a/include/asm-ppc/ide.h
+++ b/include/asm-powerpc/ide.h
@@ -1,24 +1,27 @@
 /*
- *  linux/include/asm-ppc/ide.h
+ *  Copyright (C) 1994-1996 Linus Torvalds & authors
  *
- *  Copyright (C) 1994-1996 Linus Torvalds & authors */
-
-/*
- *  This file contains the ppc architecture specific IDE code.
+ *  This file contains the powerpc architecture specific IDE code.
  */
-
-#ifndef __ASMPPC_IDE_H
-#define __ASMPPC_IDE_H
+#ifndef _ASM_POWERPC_IDE_H
+#define _ASM_POWERPC_IDE_H
 
 #ifdef __KERNEL__
 
+#ifndef __powerpc64__
 #include <linux/sched.h>
 #include <asm/mpc8xx.h>
-
-#ifndef MAX_HWIFS
-#define MAX_HWIFS	8
 #endif
 
+#ifndef MAX_HWIFS
+#ifdef __powerpc64__
+#define MAX_HWIFS	10
+#else
+#define MAX_HWIFS	8
+#endif
+#endif
+
+#ifndef  __powerpc64__
 #include <linux/config.h>
 #include <linux/hdreg.h>
 #include <linux/ioport.h>
@@ -59,9 +62,6 @@
 	return 0;
 }
 
-#define IDE_ARCH_OBSOLETE_INIT
-#define ide_default_io_ctl(base)	((base) + 0x206) /* obsolete */
-
 #ifdef CONFIG_PCI
 #define ide_init_default_irq(base)	(0)
 #else
@@ -73,6 +73,11 @@
 #define ide_ack_intr(hwif) (hwif->hw.ack_intr ? hwif->hw.ack_intr(hwif) : 1)
 #endif
 
+#endif /* __powerpc64__ */
+
+#define IDE_ARCH_OBSOLETE_INIT
+#define ide_default_io_ctl(base)	((base) + 0x206) /* obsolete */
+
 #endif /* __KERNEL__ */
 
-#endif /* __ASMPPC_IDE_H */
+#endif /* _ASM_POWERPC_IDE_H */
diff --git a/include/asm-powerpc/machdep.h b/include/asm-powerpc/machdep.h
index fa03864..5670f0c 100644
--- a/include/asm-powerpc/machdep.h
+++ b/include/asm-powerpc/machdep.h
@@ -82,7 +82,6 @@
 	void		(*iommu_dev_setup)(struct pci_dev *dev);
 	void		(*iommu_bus_setup)(struct pci_bus *bus);
 	void		(*irq_bus_setup)(struct pci_bus *bus);
-	int		(*set_dabr)(unsigned long dabr);
 #endif
 
 	int		(*probe)(int platform);
@@ -158,6 +157,9 @@
 	   platform, called once per cpu. */
 	void		(*enable_pmcs)(void);
 
+	/* Set DABR for this platform, leave empty for default implemenation */
+	int		(*set_dabr)(unsigned long dabr);
+
 #ifdef CONFIG_PPC32	/* XXX for now */
 	/* A general init function, called by ppc_init in init/main.c.
 	   May be NULL. */
diff --git a/include/asm-powerpc/ppc-pci.h b/include/asm-powerpc/ppc-pci.h
index a88728f..13aacff 100644
--- a/include/asm-powerpc/ppc-pci.h
+++ b/include/asm-powerpc/ppc-pci.h
@@ -34,6 +34,7 @@
 
 void pci_devs_phb_init(void);
 void pci_devs_phb_init_dynamic(struct pci_controller *phb);
+void __devinit scan_phb(struct pci_controller *hose);
 
 /* PCI address cache management routines */
 void pci_addr_cache_insert_device(struct pci_dev *dev);
diff --git a/include/asm-powerpc/prom.h b/include/asm-powerpc/prom.h
index 7587bf5..f999df1 100644
--- a/include/asm-powerpc/prom.h
+++ b/include/asm-powerpc/prom.h
@@ -203,7 +203,7 @@
 extern int prom_n_size_cells(struct device_node* np);
 extern int prom_n_intr_cells(struct device_node* np);
 extern void prom_get_irq_senses(unsigned char *senses, int off, int max);
-extern void prom_add_property(struct device_node* np, struct property* prop);
+extern int prom_add_property(struct device_node* np, struct property* prop);
 
 #ifdef CONFIG_PPC32
 /*
diff --git a/include/asm-powerpc/reg.h b/include/asm-powerpc/reg.h
index da84841..489cf4c 100644
--- a/include/asm-powerpc/reg.h
+++ b/include/asm-powerpc/reg.h
@@ -396,6 +396,9 @@
 #define SPRN_VRSAVE	0x100	/* Vector Register Save Register */
 #define SPRN_XER	0x001	/* Fixed Point Exception Register */
 
+#define SPRN_SCOMC	0x114	/* SCOM Access Control */
+#define SPRN_SCOMD	0x115	/* SCOM Access DATA */
+
 /* Performance monitor SPRs */
 #ifdef CONFIG_PPC64
 #define SPRN_MMCR0	795
@@ -594,7 +597,11 @@
 		mtspr(SPRN_CTRLT, ctrl);
 	}
 }
-#endif
+
+extern unsigned long scom970_read(unsigned int address);
+extern void scom970_write(unsigned int address, unsigned long value);
+
+#endif /* CONFIG_PPC64 */
 
 #define __get_SP()	({unsigned long sp; \
 			asm volatile("mr %0,1": "=r" (sp)); sp;})
diff --git a/include/asm-powerpc/smp.h b/include/asm-powerpc/smp.h
index 8bcdd0fa..98581e5a 100644
--- a/include/asm-powerpc/smp.h
+++ b/include/asm-powerpc/smp.h
@@ -86,7 +86,6 @@
 #else
 /* for UP */
 #define smp_setup_cpu_maps()
-#define smp_release_cpus()
 
 #endif /* CONFIG_SMP */
 
@@ -94,6 +93,9 @@
 #define get_hard_smp_processor_id(CPU) (paca[(CPU)].hw_cpu_id)
 #define set_hard_smp_processor_id(CPU, VAL) \
 	do { (paca[(CPU)].hw_cpu_id = (VAL)); } while (0)
+
+extern void smp_release_cpus(void);
+
 #else
 /* 32-bit */
 #ifndef CONFIG_SMP
diff --git a/include/asm-powerpc/smu.h b/include/asm-powerpc/smu.h
index dee8eef..76c29a9 100644
--- a/include/asm-powerpc/smu.h
+++ b/include/asm-powerpc/smu.h
@@ -20,16 +20,52 @@
 /*
  * Partition info commands
  *
- * I do not know what those are for at this point
+ * These commands are used to retreive the sdb-partition-XX datas from
+ * the SMU. The lenght is always 2. First byte is the subcommand code
+ * and second byte is the partition ID.
+ *
+ * The reply is 6 bytes:
+ *
+ *  - 0..1 : partition address
+ *  - 2    : a byte containing the partition ID
+ *  - 3    : length (maybe other bits are rest of header ?)
+ *
+ * The data must then be obtained with calls to another command:
+ * SMU_CMD_MISC_ee_GET_DATABLOCK_REC (described below).
  */
 #define SMU_CMD_PARTITION_COMMAND		0x3e
+#define   SMU_CMD_PARTITION_LATEST		0x01
+#define   SMU_CMD_PARTITION_BASE		0x02
+#define   SMU_CMD_PARTITION_UPDATE		0x03
 
 
 /*
  * Fan control
  *
- * This is a "mux" for fan control commands, first byte is the
- * "sub" command.
+ * This is a "mux" for fan control commands. The command seem to
+ * act differently based on the number of arguments. With 1 byte
+ * of argument, this seem to be queries for fans status, setpoint,
+ * etc..., while with 0xe arguments, we will set the fans speeds.
+ *
+ * Queries (1 byte arg):
+ * ---------------------
+ *
+ * arg=0x01: read RPM fans status
+ * arg=0x02: read RPM fans setpoint
+ * arg=0x11: read PWM fans status
+ * arg=0x12: read PWM fans setpoint
+ *
+ * the "status" queries return the current speed while the "setpoint" ones
+ * return the programmed/target speed. It _seems_ that the result is a bit
+ * mask in the first byte of active/available fans, followed by 6 words (16
+ * bits) containing the requested speed.
+ *
+ * Setpoint (14 bytes arg):
+ * ------------------------
+ *
+ * first arg byte is 0 for RPM fans and 0x10 for PWM. Second arg byte is the
+ * mask of fans affected by the command. Followed by 6 words containing the
+ * setpoint value for selected fans in the mask (or 0 if mask value is 0)
  */
 #define SMU_CMD_FAN_COMMAND			0x4a
 
@@ -144,7 +180,11 @@
  *  - lenght 8 ("VSLEWxyz") has 3 additional bytes appended, and is
  *    used to set the voltage slewing point. The SMU replies with "DONE"
  * I yet have to figure out their exact meaning of those 3 bytes in
- * both cases.
+ * both cases. They seem to be:
+ *  x = processor mask
+ *  y = op. point index
+ *  z = processor freq. step index
+ * I haven't yet decyphered result codes
  *
  */
 #define SMU_CMD_POWER_COMMAND			0xaa
@@ -152,6 +192,14 @@
 #define   SMU_CMD_POWER_SHUTDOWN		"SHUTDOWN"
 #define   SMU_CMD_POWER_VOLTAGE_SLEW		"VSLEW"
 
+/*
+ * Read ADC sensors
+ *
+ * This command takes one byte of parameter: the sensor ID (or "reg"
+ * value in the device-tree) and returns a 16 bits value
+ */
+#define SMU_CMD_READ_ADC			0xd8
+
 /* Misc commands
  *
  * This command seem to be a grab bag of various things
@@ -172,6 +220,25 @@
  * Misc commands
  *
  * This command seem to be a grab bag of various things
+ *
+ * SMU_CMD_MISC_ee_GET_DATABLOCK_REC is used, among others, to
+ * transfer blocks of data from the SMU. So far, I've decrypted it's
+ * usage to retreive partition data. In order to do that, you have to
+ * break your transfer in "chunks" since that command cannot transfer
+ * more than a chunk at a time. The chunk size used by OF is 0xe bytes,
+ * but it seems that the darwin driver will let you do 0x1e bytes if
+ * your "PMU" version is >= 0x30. You can get the "PMU" version apparently
+ * either in the last 16 bits of property "smu-version-pmu" or as the 16
+ * bytes at offset 1 of "smu-version-info"
+ *
+ * For each chunk, the command takes 7 bytes of arguments:
+ *  byte 0: subcommand code (0x02)
+ *  byte 1: 0x04 (always, I don't know what it means, maybe the address
+ *                space to use or some other nicety. It's hard coded in OF)
+ *  byte 2..5: SMU address of the chunk (big endian 32 bits)
+ *  byte 6: size to transfer (up to max chunk size)
+ *
+ * The data is returned directly
  */
 #define SMU_CMD_MISC_ee_COMMAND			0xee
 #define   SMU_CMD_MISC_ee_GET_DATABLOCK_REC	0x02
@@ -333,6 +400,128 @@
 
 #endif /* __KERNEL__ */
 
+
+/*
+ * - SMU "sdb" partitions informations -
+ */
+
+
+/*
+ * Partition header format
+ */
+struct smu_sdbp_header {
+	__u8	id;
+	__u8	len;
+	__u8	version;
+	__u8	flags;
+};
+
+
+ /*
+ * demangle 16 and 32 bits integer in some SMU partitions
+ * (currently, afaik, this concerns only the FVT partition
+ * (0x12)
+ */
+#define SMU_U16_MIX(x)	le16_to_cpu(x);
+#define SMU_U32_MIX(x)  ((((x) & 0xff00ff00u) >> 8)|(((x) & 0x00ff00ffu) << 8))
+
+
+/* This is the definition of the SMU sdb-partition-0x12 table (called
+ * CPU F/V/T operating points in Darwin). The definition for all those
+ * SMU tables should be moved to some separate file
+ */
+#define SMU_SDB_FVT_ID			0x12
+
+struct smu_sdbp_fvt {
+	__u32	sysclk;			/* Base SysClk frequency in Hz for
+					 * this operating point. Value need to
+					 * be unmixed with SMU_U32_MIX()
+					 */
+	__u8	pad;
+	__u8	maxtemp;		/* Max temp. supported by this
+					 * operating point
+					 */
+
+	__u16	volts[3];		/* CPU core voltage for the 3
+					 * PowerTune modes, a mode with
+					 * 0V = not supported. Value need
+					 * to be unmixed with SMU_U16_MIX()
+					 */
+};
+
+/* This partition contains voltage & current sensor calibration
+ * informations
+ */
+#define SMU_SDB_CPUVCP_ID		0x21
+
+struct smu_sdbp_cpuvcp {
+	__u16	volt_scale;		/* u4.12 fixed point */
+	__s16	volt_offset;		/* s4.12 fixed point */
+	__u16	curr_scale;		/* u4.12 fixed point */
+	__s16	curr_offset;		/* s4.12 fixed point */
+	__s32	power_quads[3];		/* s4.28 fixed point */
+};
+
+/* This partition contains CPU thermal diode calibration
+ */
+#define SMU_SDB_CPUDIODE_ID		0x18
+
+struct smu_sdbp_cpudiode {
+	__u16	m_value;		/* u1.15 fixed point */
+	__s16	b_value;		/* s10.6 fixed point */
+
+};
+
+/* This partition contains Slots power calibration
+ */
+#define SMU_SDB_SLOTSPOW_ID		0x78
+
+struct smu_sdbp_slotspow {
+	__u16	pow_scale;		/* u4.12 fixed point */
+	__s16	pow_offset;		/* s4.12 fixed point */
+};
+
+/* This partition contains machine specific version information about
+ * the sensor/control layout
+ */
+#define SMU_SDB_SENSORTREE_ID		0x25
+
+struct smu_sdbp_sensortree {
+	u8	model_id;
+	u8	unknown[3];
+};
+
+/* This partition contains CPU thermal control PID informations. So far
+ * only single CPU machines have been seen with an SMU, so we assume this
+ * carries only informations for those
+ */
+#define SMU_SDB_CPUPIDDATA_ID		0x17
+
+struct smu_sdbp_cpupiddata {
+	u8	unknown1;
+	u8	target_temp_delta;
+	u8	unknown2;
+	u8	history_len;
+	s16	power_adj;
+	u16	max_power;
+	s32	gp,gr,gd;
+};
+
+
+/* Other partitions without known structures */
+#define SMU_SDB_DEBUG_SWITCHES_ID	0x05
+
+#ifdef __KERNEL__
+/*
+ * This returns the pointer to an SMU "sdb" partition data or NULL
+ * if not found. The data format is described below
+ */
+extern struct smu_sdbp_header *smu_get_sdb_partition(int id,
+						     unsigned int *size);
+
+#endif /* __KERNEL__ */
+
+
 /*
  * - Userland interface -
  */
@@ -365,8 +554,10 @@
 	__u32		cmdtype;
 #define SMU_CMDTYPE_SMU			0	/* SMU command */
 #define SMU_CMDTYPE_WANTS_EVENTS	1	/* switch fd to events mode */
+#define SMU_CMDTYPE_GET_PARTITION	2	/* retreive an sdb partition */
 
 	__u8		cmd;			/* SMU command byte */
+	__u8		pad[3];			/* padding */
 	__u32		data_len;		/* Lenght of data following */
 };
 
diff --git a/include/asm-powerpc/xmon.h b/include/asm-powerpc/xmon.h
index 43f7129..ace2072 100644
--- a/include/asm-powerpc/xmon.h
+++ b/include/asm-powerpc/xmon.h
@@ -7,6 +7,7 @@
 extern int xmon(struct pt_regs *excp);
 extern void xmon_printf(const char *fmt, ...);
 extern void xmon_init(int);
+extern void xmon_map_scc(void);
 
 #endif
 #endif
diff --git a/include/asm-ppc/btext.h b/include/asm-ppc/btext.h
index 36c7640..ccaefab 100644
--- a/include/asm-ppc/btext.h
+++ b/include/asm-ppc/btext.h
@@ -17,18 +17,18 @@
 extern boot_infos_t disp_bi;
 extern int boot_text_mapped;
 
-void btext_init(boot_infos_t *bi);
-void btext_welcome(void);
-void btext_prepare_BAT(void);
-void btext_setup_display(int width, int height, int depth, int pitch,
-			 unsigned long address);
-void map_boot_text(void);
-void btext_update_display(unsigned long phys, int width, int height,
-			  int depth, int pitch);
+extern void init_boot_display(void);
+extern void btext_welcome(void);
+extern void btext_prepare_BAT(void);
+extern void btext_setup_display(int width, int height, int depth, int pitch,
+				unsigned long address);
+extern void map_boot_text(void);
+extern void btext_update_display(unsigned long phys, int width, int height,
+				 int depth, int pitch);
 
-void btext_drawchar(char c);
-void btext_drawstring(const char *str);
-void btext_drawhex(unsigned long v);
+extern void btext_drawchar(char c);
+extern void btext_drawstring(const char *str);
+extern void btext_drawhex(unsigned long v);
 
 #endif /* __KERNEL__ */
 #endif /* __PPC_BTEXT_H */
diff --git a/include/asm-ppc/io.h b/include/asm-ppc/io.h
index f7f614d..2bfdf9c 100644
--- a/include/asm-ppc/io.h
+++ b/include/asm-ppc/io.h
@@ -237,9 +237,9 @@
 #define outsl(port, buf, nl)	_outsl_ns((port)+___IO_BASE, (buf), (nl))
 
 /*
- * On powermacs, we will get a machine check exception if we
- * try to read data from a non-existent I/O port.  Because the
- * machine check is an asynchronous exception, it isn't
+ * On powermacs and 8xx we will get a machine check exception 
+ * if we try to read data from a non-existent I/O port. Because
+ * the machine check is an asynchronous exception, it isn't
  * well-defined which instruction SRR0 will point to when the
  * exception occurs.
  * With the sequence below (twi; isync; nop), we have found that
@@ -258,7 +258,7 @@
 {							\
 	unsigned int x;					\
 	__asm__ __volatile__(				\
-			op "	%0,0,%1\n"		\
+		"0:"	op "	%0,0,%1\n"		\
 		"1:	twi	0,%0,0\n"		\
 		"2:	isync\n"			\
 		"3:	nop\n"				\
@@ -269,6 +269,7 @@
 		".previous\n"				\
 		".section __ex_table,\"a\"\n"		\
 		"	.align	2\n"			\
+		"	.long	0b,5b\n"		\
 		"	.long	1b,5b\n"		\
 		"	.long	2b,5b\n"		\
 		"	.long	3b,5b\n"		\
@@ -282,11 +283,12 @@
 extern __inline__ void name(unsigned int val, unsigned int port) \
 {							\
 	__asm__ __volatile__(				\
-		op " %0,0,%1\n"				\
+		"0:" op " %0,0,%1\n"			\
 		"1:	sync\n"				\
 		"2:\n"					\
 		".section __ex_table,\"a\"\n"		\
 		"	.align	2\n"			\
+		"	.long	0b,2b\n"		\
 		"	.long	1b,2b\n"		\
 		".previous"				\
 		: : "r" (val), "r" (port + ___IO_BASE));	\
diff --git a/include/asm-ppc/kgdb.h b/include/asm-ppc/kgdb.h
index 1d3c927..b617dac 100644
--- a/include/asm-ppc/kgdb.h
+++ b/include/asm-ppc/kgdb.h
@@ -31,7 +31,7 @@
 /* For taking exceptions
  * these are defined in traps.c
  */
-extern void (*debugger)(struct pt_regs *regs);
+extern int (*debugger)(struct pt_regs *regs);
 extern int (*debugger_bpt)(struct pt_regs *regs);
 extern int (*debugger_sstep)(struct pt_regs *regs);
 extern int (*debugger_iabr_match)(struct pt_regs *regs);
diff --git a/include/asm-ppc/prom.h b/include/asm-ppc/prom.h
index 75c0637..3e39827 100644
--- a/include/asm-ppc/prom.h
+++ b/include/asm-ppc/prom.h
@@ -93,7 +93,7 @@
 extern int machine_is_compatible(const char *compat);
 extern unsigned char *get_property(struct device_node *node, const char *name,
 				   int *lenp);
-extern void prom_add_property(struct device_node* np, struct property* prop);
+extern int prom_add_property(struct device_node* np, struct property* prop);
 extern void prom_get_irq_senses(unsigned char *, int, int);
 extern int prom_n_addr_cells(struct device_node* np);
 extern int prom_n_size_cells(struct device_node* np);
diff --git a/include/asm-ppc64/ide.h b/include/asm-ppc64/ide.h
deleted file mode 100644
index 0aae1c5..0000000
--- a/include/asm-ppc64/ide.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- *  linux/include/asm-ppc/ide.h
- *
- *  Copyright (C) 1994-1996 Linus Torvalds & authors
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- */
-
-/*
- *  This file contains the ppc64 architecture specific IDE code.
- */
-
-#ifndef __ASMPPC64_IDE_H
-#define __ASMPPC64_IDE_H
-
-#ifdef __KERNEL__
-
-#ifndef MAX_HWIFS
-# define MAX_HWIFS	10
-#endif
-
-#define IDE_ARCH_OBSOLETE_INIT
-#define ide_default_io_ctl(base)	((base) + 0x206) /* obsolete */
-
-#endif /* __KERNEL__ */
-
-#endif /* __ASMPPC64_IDE_H */
diff --git a/include/asm-ppc64/pci.h b/include/asm-ppc64/pci.h
index 342e2d7..fafdf88 100644
--- a/include/asm-ppc64/pci.h
+++ b/include/asm-ppc64/pci.h
@@ -162,6 +162,14 @@
 
 extern struct pci_controller *init_phb_dynamic(struct device_node *dn);
 
+extern struct pci_dev *of_create_pci_dev(struct device_node *node,
+					struct pci_bus *bus, int devfn);
+
+extern void of_scan_pci_bridge(struct device_node *node,
+				struct pci_dev *dev);
+
+extern void of_scan_bus(struct device_node *node, struct pci_bus *bus);
+
 extern int pci_read_irq_line(struct pci_dev *dev);
 
 extern void pcibios_add_platform_entries(struct pci_dev *dev);
diff --git a/include/asm-ppc64/ppcdebug.h b/include/asm-ppc64/ppcdebug.h
deleted file mode 100644
index fd7f696..0000000
--- a/include/asm-ppc64/ppcdebug.h
+++ /dev/null
@@ -1,108 +0,0 @@
-#ifndef __PPCDEBUG_H
-#define __PPCDEBUG_H
-/********************************************************************
- * Author: Adam Litke, IBM Corp
- * (c) 2001
- *
- * This file contains definitions and macros for a runtime debugging
- * system for ppc64 (This should also work on 32 bit with a few    
- * adjustments.                                                   
- *
- * 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 <asm/udbg.h>
-#include <stdarg.h>
-
-#define PPCDBG_BITVAL(X)     ((1UL)<<((unsigned long)(X)))
-
-/* Defined below are the bit positions of various debug flags in the
- * ppc64_debug_switch variable.
- * -- When adding new values, please enter them into trace names below -- 
- *
- * Values 62 & 63 can be used to stress the hardware page table management
- * code.  They must be set statically, any attempt to change them dynamically
- * would be a very bad idea.
- */
-#define PPCDBG_MMINIT        PPCDBG_BITVAL(0)
-#define PPCDBG_MM            PPCDBG_BITVAL(1)
-#define PPCDBG_SYS32         PPCDBG_BITVAL(2)
-#define PPCDBG_SYS32NI       PPCDBG_BITVAL(3)
-#define PPCDBG_SYS32X	     PPCDBG_BITVAL(4)
-#define PPCDBG_SYS32M	     PPCDBG_BITVAL(5)
-#define PPCDBG_SYS64         PPCDBG_BITVAL(6)
-#define PPCDBG_SYS64NI       PPCDBG_BITVAL(7)
-#define PPCDBG_SYS64X	     PPCDBG_BITVAL(8)
-#define PPCDBG_SIGNAL        PPCDBG_BITVAL(9)
-#define PPCDBG_SIGNALXMON    PPCDBG_BITVAL(10)
-#define PPCDBG_BINFMT32      PPCDBG_BITVAL(11)
-#define PPCDBG_BINFMT64      PPCDBG_BITVAL(12)
-#define PPCDBG_BINFMTXMON    PPCDBG_BITVAL(13)
-#define PPCDBG_BINFMT_32ADDR PPCDBG_BITVAL(14)
-#define PPCDBG_ALIGNFIXUP    PPCDBG_BITVAL(15)
-#define PPCDBG_TCEINIT       PPCDBG_BITVAL(16)
-#define PPCDBG_TCE           PPCDBG_BITVAL(17)
-#define PPCDBG_PHBINIT       PPCDBG_BITVAL(18)
-#define PPCDBG_SMP           PPCDBG_BITVAL(19)
-#define PPCDBG_BOOT          PPCDBG_BITVAL(20)
-#define PPCDBG_BUSWALK       PPCDBG_BITVAL(21)
-#define PPCDBG_PROM	     PPCDBG_BITVAL(22)
-#define PPCDBG_RTAS	     PPCDBG_BITVAL(23)
-#define PPCDBG_HTABSTRESS    PPCDBG_BITVAL(62)
-#define PPCDBG_HTABSIZE      PPCDBG_BITVAL(63)
-#define PPCDBG_NONE          (0UL)
-#define PPCDBG_ALL           (0xffffffffUL)
-
-/* The default initial value for the debug switch */
-#define PPC_DEBUG_DEFAULT    0 
-/* #define PPC_DEBUG_DEFAULT    PPCDBG_ALL        */
-
-#define PPCDBG_NUM_FLAGS     64
-
-extern u64 ppc64_debug_switch;
-
-#ifdef WANT_PPCDBG_TAB
-/* A table of debug switch names to allow name lookup in xmon 
- * (and whoever else wants it.
- */
-char *trace_names[PPCDBG_NUM_FLAGS] = {
-	/* Known debug names */
-	"mminit", 	"mm",
-	"syscall32", 	"syscall32_ni", "syscall32x",	"syscall32m",
-	"syscall64", 	"syscall64_ni", "syscall64x",
-	"signal",	"signal_xmon",
-	"binfmt32",	"binfmt64",	"binfmt_xmon",	"binfmt_32addr",
-	"alignfixup",   "tceinit",      "tce",          "phb_init",     
-	"smp",          "boot",         "buswalk",	"prom",
-	"rtas"
-};
-#else
-extern char *trace_names[64];
-#endif /* WANT_PPCDBG_TAB */
-
-#ifdef CONFIG_PPCDBG
-/* Macro to conditionally print debug based on debug_switch */
-#define PPCDBG(...) udbg_ppcdbg(__VA_ARGS__)
-
-/* Macro to conditionally call a debug routine based on debug_switch */
-#define PPCDBGCALL(FLAGS,FUNCTION) ifppcdebug(FLAGS) FUNCTION
-
-/* Macros to test for debug states */
-#define ifppcdebug(FLAGS) if (udbg_ifdebug(FLAGS))
-#define ppcdebugset(FLAGS) (udbg_ifdebug(FLAGS))
-#define PPCDBG_BINFMT (test_thread_flag(TIF_32BIT) ? PPCDBG_BINFMT32 : PPCDBG_BINFMT64)
-
-#else
-#define PPCDBG(...) do {;} while (0)
-#define PPCDBGCALL(FLAGS,FUNCTION) do {;} while (0)
-#define ifppcdebug(...) if (0)
-#define ppcdebugset(FLAGS) (0)
-#endif /* CONFIG_PPCDBG */
-
-#endif /*__PPCDEBUG_H */
diff --git a/include/asm-ppc64/prom.h b/include/asm-ppc64/prom.h
index bdb4717..76bb026 100644
--- a/include/asm-ppc64/prom.h
+++ b/include/asm-ppc64/prom.h
@@ -213,6 +213,6 @@
 extern int prom_n_size_cells(struct device_node* np);
 extern int prom_n_intr_cells(struct device_node* np);
 extern void prom_get_irq_senses(unsigned char *senses, int off, int max);
-extern void prom_add_property(struct device_node* np, struct property* prop);
+extern int prom_add_property(struct device_node* np, struct property* prop);
 
 #endif /* _PPC64_PROM_H */
diff --git a/include/asm-ppc64/udbg.h b/include/asm-ppc64/udbg.h
index 8192fb8..e3b9279 100644
--- a/include/asm-ppc64/udbg.h
+++ b/include/asm-ppc64/udbg.h
@@ -23,9 +23,6 @@
 
 extern void register_early_udbg_console(void);
 extern void udbg_printf(const char *fmt, ...);
-extern void udbg_ppcdbg(unsigned long flags, const char *fmt, ...);
-extern unsigned long udbg_ifdebug(unsigned long flags);
-extern void __init ppcdbg_initialize(void);
 
 extern void udbg_init_uart(void __iomem *comport, unsigned int speed);
 
diff --git a/include/linux/proc_fs.h b/include/linux/proc_fs.h
index 0563581..65ceeaa 100644
--- a/include/linux/proc_fs.h
+++ b/include/linux/proc_fs.h
@@ -139,15 +139,12 @@
 /*
  * proc_devtree.c
  */
-struct device_node;
-extern void proc_device_tree_init(void);
 #ifdef CONFIG_PROC_DEVICETREE
+struct device_node;
+struct property;
+extern void proc_device_tree_init(void);
 extern void proc_device_tree_add_node(struct device_node *, struct proc_dir_entry *);
-#else /* !CONFIG_PROC_DEVICETREE */
-static inline void proc_device_tree_add_node(struct device_node *np, struct proc_dir_entry *pde)
-{
-	return;
-}
+extern void proc_device_tree_add_prop(struct proc_dir_entry *pde, struct property *prop);
 #endif /* CONFIG_PROC_DEVICETREE */
 
 extern struct proc_dir_entry *proc_symlink(const char *,