浏览代码

Merge git://git.kernel.org/pub/scm/linux/kernel/git/paulus/powerpc-merge

Linus Torvalds 19 年之前
父节点
当前提交
f093182d31
共有 88 个文件被更改,包括 5134 次插入579 次删除
  1. 9 0
      arch/powerpc/Kconfig
  2. 206 55
      arch/powerpc/configs/g5_defconfig
  3. 70 0
      arch/powerpc/kernel/misc_64.S
  4. 1 3
      arch/powerpc/kernel/process.c
  5. 18 3
      arch/powerpc/kernel/prom.c
  6. 6 5
      arch/powerpc/kernel/prom_init.c
  7. 2 3
      arch/powerpc/kernel/rtas.c
  8. 40 0
      arch/powerpc/kernel/setup-common.c
  9. 1 0
      arch/powerpc/kernel/setup_32.c
  10. 1 45
      arch/powerpc/kernel/setup_64.c
  11. 0 1
      arch/powerpc/kernel/signal_32.c
  12. 0 1
      arch/powerpc/kernel/signal_64.c
  13. 0 1
      arch/powerpc/kernel/smp.c
  14. 1 4
      arch/powerpc/kernel/time.c
  15. 0 11
      arch/powerpc/kernel/traps.c
  16. 1 0
      arch/powerpc/lib/locks.c
  17. 17 0
      arch/powerpc/mm/fault.c
  18. 3 7
      arch/powerpc/mm/hash_utils_64.c
  19. 0 1
      arch/powerpc/mm/init_64.c
  20. 1 1
      arch/powerpc/mm/mem.c
  21. 1 0
      arch/powerpc/mm/numa.c
  22. 0 1
      arch/powerpc/mm/pgtable_64.c
  23. 23 1
      arch/powerpc/oprofile/op_model_power4.c
  24. 0 5
      arch/powerpc/platforms/iseries/irq.c
  25. 0 37
      arch/powerpc/platforms/iseries/pci.c
  26. 0 4
      arch/powerpc/platforms/iseries/setup.c
  27. 0 1
      arch/powerpc/platforms/iseries/smp.c
  28. 2 1
      arch/powerpc/platforms/powermac/Makefile
  29. 8 7
      arch/powerpc/platforms/powermac/cpufreq_32.c
  30. 323 0
      arch/powerpc/platforms/powermac/cpufreq_64.c
  31. 0 13
      arch/powerpc/platforms/powermac/setup.c
  32. 1 1
      arch/powerpc/platforms/pseries/iommu.c
  33. 2 1
      arch/powerpc/platforms/pseries/lpar.c
  34. 0 10
      arch/powerpc/platforms/pseries/plpar_wrappers.h
  35. 1 1
      arch/powerpc/platforms/pseries/ras.c
  36. 11 7
      arch/powerpc/platforms/pseries/setup.c
  37. 3 2
      arch/powerpc/sysdev/i8259.c
  38. 0 1
      arch/powerpc/sysdev/u3_iommu.c
  39. 125 20
      arch/ppc/kernel/misc.S
  40. 8 4
      arch/ppc/kernel/traps.c
  41. 7 6
      arch/ppc/syslib/m8xx_wdt.c
  42. 3 1
      arch/ppc/syslib/prom.c
  43. 3 2
      arch/ppc/xmon/xmon.c
  44. 11 0
      arch/ppc64/Kconfig
  45. 0 4
      arch/ppc64/Kconfig.debug
  46. 1 0
      arch/ppc64/kernel/idle.c
  47. 1 0
      arch/ppc64/kernel/machine_kexec.c
  48. 71 1
      arch/ppc64/kernel/misc.S
  49. 9 8
      arch/ppc64/kernel/pci.c
  50. 20 5
      arch/ppc64/kernel/prom.c
  51. 1 2
      arch/ppc64/kernel/prom_init.c
  52. 1 5
      arch/ppc64/kernel/rtas_pci.c
  53. 0 55
      arch/ppc64/kernel/udbg.c
  54. 10 10
      drivers/block/swim3.c
  55. 6 3
      drivers/ide/ppc/pmac.c
  56. 19 0
      drivers/macintosh/Kconfig
  57. 9 0
      drivers/macintosh/Makefile
  58. 168 6
      drivers/macintosh/smu.c
  59. 5 5
      drivers/macintosh/via-pmu.c
  60. 131 0
      drivers/macintosh/windfarm.h
  61. 426 0
      drivers/macintosh/windfarm_core.c
  62. 105 0
      drivers/macintosh/windfarm_cpufreq_clamp.c
  63. 263 0
      drivers/macintosh/windfarm_lm75_sensor.c
  64. 145 0
      drivers/macintosh/windfarm_pid.c
  65. 84 0
      drivers/macintosh/windfarm_pid.h
  66. 879 0
      drivers/macintosh/windfarm_pm81.c
  67. 814 0
      drivers/macintosh/windfarm_pm91.c
  68. 282 0
      drivers/macintosh/windfarm_smu_controls.c
  69. 479 0
      drivers/macintosh/windfarm_smu_sensors.c
  70. 35 22
      fs/proc/proc_devtree.c
  71. 17 12
      include/asm-powerpc/ide.h
  72. 3 1
      include/asm-powerpc/machdep.h
  73. 1 0
      include/asm-powerpc/ppc-pci.h
  74. 1 1
      include/asm-powerpc/prom.h
  75. 8 1
      include/asm-powerpc/reg.h
  76. 3 1
      include/asm-powerpc/smp.h
  77. 195 4
      include/asm-powerpc/smu.h
  78. 1 0
      include/asm-powerpc/xmon.h
  79. 11 11
      include/asm-ppc/btext.h
  80. 7 5
      include/asm-ppc/io.h
  81. 1 1
      include/asm-ppc/kgdb.h
  82. 1 1
      include/asm-ppc/prom.h
  83. 0 30
      include/asm-ppc64/ide.h
  84. 8 0
      include/asm-ppc64/pci.h
  85. 0 108
      include/asm-ppc64/ppcdebug.h
  86. 1 1
      include/asm-ppc64/prom.h
  87. 0 3
      include/asm-ppc64/udbg.h
  88. 3 6
      include/linux/proc_fs.h

+ 9 - 0
arch/powerpc/Kconfig

@@ -404,6 +404,14 @@ config CPU_FREQ_PMAC
 	  this currently includes some models of iBook & Titanium
 	  this currently includes some models of iBook & Titanium
 	  PowerBook.
 	  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
 config PPC601_SYNC_FIX
 	bool "Workarounds for PPC601 bugs"
 	bool "Workarounds for PPC601 bugs"
 	depends on 6xx && (PPC_PREP || PPC_PMAC)
 	depends on 6xx && (PPC_PREP || PPC_PMAC)
@@ -484,6 +492,7 @@ source "fs/Kconfig.binfmt"
 config FORCE_MAX_ZONEORDER
 config FORCE_MAX_ZONEORDER
 	int
 	int
 	depends on PPC64
 	depends on PPC64
+	default "9" if PPC_64K_PAGES
 	default "13"
 	default "13"
 
 
 config MATH_EMULATION
 config MATH_EMULATION

+ 206 - 55
arch/powerpc/configs/g5_defconfig

@@ -1,18 +1,32 @@
 #
 #
 # Automatically generated make config: don't edit
 # 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_64BIT=y
+CONFIG_PPC_MERGE=y
 CONFIG_MMU=y
 CONFIG_MMU=y
+CONFIG_GENERIC_HARDIRQS=y
 CONFIG_RWSEM_XCHGADD_ALGORITHM=y
 CONFIG_RWSEM_XCHGADD_ALGORITHM=y
 CONFIG_GENERIC_CALIBRATE_DELAY=y
 CONFIG_GENERIC_CALIBRATE_DELAY=y
-CONFIG_GENERIC_ISA_DMA=y
+CONFIG_PPC=y
 CONFIG_EARLY_PRINTK=y
 CONFIG_EARLY_PRINTK=y
 CONFIG_COMPAT=y
 CONFIG_COMPAT=y
+CONFIG_SYSVIPC_COMPAT=y
 CONFIG_SCHED_NO_NO_OMIT_FRAME_POINTER=y
 CONFIG_SCHED_NO_NO_OMIT_FRAME_POINTER=y
 CONFIG_ARCH_MAY_HAVE_PC_FDC=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
 # Code maturity level options
@@ -67,30 +81,60 @@ CONFIG_MODVERSIONS=y
 CONFIG_MODULE_SRCVERSION_ALL=y
 CONFIG_MODULE_SRCVERSION_ALL=y
 CONFIG_KMOD=y
 CONFIG_KMOD=y
 CONFIG_STOP_MACHINE=y
 CONFIG_STOP_MACHINE=y
-CONFIG_SYSVIPC_COMPAT=y
 
 
 #
 #
 # Platform support
 # Platform support
 #
 #
-# CONFIG_PPC_ISERIES is not set
 CONFIG_PPC_MULTIPLATFORM=y
 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_PSERIES is not set
-# CONFIG_PPC_BPA is not set
 CONFIG_PPC_PMAC=y
 CONFIG_PPC_PMAC=y
+CONFIG_PPC_PMAC64=y
 # CONFIG_PPC_MAPLE is not set
 # CONFIG_PPC_MAPLE is not set
-CONFIG_PPC=y
-CONFIG_PPC64=y
+# CONFIG_PPC_CELL is not set
 CONFIG_PPC_OF=y
 CONFIG_PPC_OF=y
-CONFIG_MPIC=y
-CONFIG_ALTIVEC=y
-CONFIG_KEXEC=y
 CONFIG_U3_DART=y
 CONFIG_U3_DART=y
-CONFIG_PPC_PMAC64=y
-CONFIG_BOOTX_TEXT=y
-CONFIG_POWER4_ONLY=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_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_SELECT_MEMORY_MODEL=y
 CONFIG_ARCH_FLATMEM_ENABLE=y
 CONFIG_ARCH_FLATMEM_ENABLE=y
 CONFIG_SELECT_MEMORY_MODEL=y
 CONFIG_SELECT_MEMORY_MODEL=y
@@ -100,28 +144,21 @@ CONFIG_FLATMEM_MANUAL=y
 CONFIG_FLATMEM=y
 CONFIG_FLATMEM=y
 CONFIG_FLAT_NODE_MEM_MAP=y
 CONFIG_FLAT_NODE_MEM_MAP=y
 # CONFIG_SPARSEMEM_STATIC is not set
 # 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_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_PROC_DEVICETREE=y
 # CONFIG_CMDLINE_BOOL is not set
 # CONFIG_CMDLINE_BOOL is not set
+# CONFIG_PM is not set
+CONFIG_SECCOMP=y
 CONFIG_ISA_DMA_API=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=y
 CONFIG_PCI_DOMAINS=y
 CONFIG_PCI_DOMAINS=y
 CONFIG_PCI_LEGACY_PROC=y
 CONFIG_PCI_LEGACY_PROC=y
@@ -136,6 +173,7 @@ CONFIG_PCI_LEGACY_PROC=y
 # PCI Hotplug Support
 # PCI Hotplug Support
 #
 #
 # CONFIG_HOTPLUG_PCI is not set
 # CONFIG_HOTPLUG_PCI is not set
+CONFIG_KERNEL_START=0xc000000000000000
 
 
 #
 #
 # Networking
 # Networking
@@ -276,6 +314,10 @@ CONFIG_LLC=y
 # CONFIG_NET_DIVERT is not set
 # CONFIG_NET_DIVERT is not set
 # CONFIG_ECONET is not set
 # CONFIG_ECONET is not set
 # CONFIG_WAN_ROUTER is not set
 # CONFIG_WAN_ROUTER is not set
+
+#
+# QoS and/or fair queueing
+#
 # CONFIG_NET_SCHED is not set
 # CONFIG_NET_SCHED is not set
 CONFIG_NET_CLS_ROUTE=y
 CONFIG_NET_CLS_ROUTE=y
 
 
@@ -348,6 +390,11 @@ CONFIG_IOSCHED_NOOP=y
 CONFIG_IOSCHED_AS=y
 CONFIG_IOSCHED_AS=y
 CONFIG_IOSCHED_DEADLINE=y
 CONFIG_IOSCHED_DEADLINE=y
 CONFIG_IOSCHED_CFQ=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
 # CONFIG_ATA_OVER_ETH is not set
 
 
 #
 #
@@ -449,6 +496,7 @@ CONFIG_SCSI_SPI_ATTRS=y
 #
 #
 # SCSI low-level drivers
 # SCSI low-level drivers
 #
 #
+# CONFIG_ISCSI_TCP is not set
 # CONFIG_BLK_DEV_3W_XXXX_RAID is not set
 # CONFIG_BLK_DEV_3W_XXXX_RAID is not set
 # CONFIG_SCSI_3W_9XXX is not set
 # CONFIG_SCSI_3W_9XXX is not set
 # CONFIG_SCSI_ACARD is not set
 # CONFIG_SCSI_ACARD is not set
@@ -465,10 +513,12 @@ CONFIG_SCSI_SATA_SVW=y
 # CONFIG_SCSI_ATA_PIIX is not set
 # CONFIG_SCSI_ATA_PIIX is not set
 # CONFIG_SCSI_SATA_MV is not set
 # CONFIG_SCSI_SATA_MV is not set
 # CONFIG_SCSI_SATA_NV 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_QSTOR is not set
+# CONFIG_SCSI_SATA_PROMISE is not set
 # CONFIG_SCSI_SATA_SX4 is not set
 # CONFIG_SCSI_SATA_SX4 is not set
 # CONFIG_SCSI_SATA_SIL 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_SIS is not set
 # CONFIG_SCSI_SATA_ULI is not set
 # CONFIG_SCSI_SATA_ULI is not set
 # CONFIG_SCSI_SATA_VIA is not set
 # CONFIG_SCSI_SATA_VIA is not set
@@ -567,6 +617,9 @@ CONFIG_IEEE1394_RAWIO=y
 CONFIG_ADB_PMU=y
 CONFIG_ADB_PMU=y
 CONFIG_PMAC_SMU=y
 CONFIG_PMAC_SMU=y
 CONFIG_THERM_PM72=y
 CONFIG_THERM_PM72=y
+CONFIG_WINDFARM=y
+CONFIG_WINDFARM_PM81=y
+CONFIG_WINDFARM_PM91=y
 
 
 #
 #
 # Network device support
 # Network device support
@@ -603,6 +656,7 @@ CONFIG_SUNGEM=y
 # CONFIG_NET_TULIP is not set
 # CONFIG_NET_TULIP is not set
 # CONFIG_HP100 is not set
 # CONFIG_HP100 is not set
 # CONFIG_NET_PCI is not set
 # CONFIG_NET_PCI is not set
+# CONFIG_FEC_8XX is not set
 
 
 #
 #
 # Ethernet (1000 Mbit)
 # Ethernet (1000 Mbit)
@@ -768,6 +822,7 @@ CONFIG_MAX_RAW_DEVS=256
 # TPM devices
 # TPM devices
 #
 #
 # CONFIG_TCG_TPM is not set
 # CONFIG_TCG_TPM is not set
+# CONFIG_TELCLOCK is not set
 
 
 #
 #
 # I2C support
 # I2C support
@@ -820,6 +875,7 @@ CONFIG_I2C_PMAC_SMU=y
 # CONFIG_SENSORS_PCF8591 is not set
 # CONFIG_SENSORS_PCF8591 is not set
 # CONFIG_SENSORS_RTC8564 is not set
 # CONFIG_SENSORS_RTC8564 is not set
 # CONFIG_SENSORS_MAX6875 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_CORE is not set
 # CONFIG_I2C_DEBUG_ALGO is not set
 # CONFIG_I2C_DEBUG_ALGO is not set
 # CONFIG_I2C_DEBUG_BUS is not set
 # CONFIG_I2C_DEBUG_BUS is not set
@@ -876,10 +932,9 @@ CONFIG_FB_OF=y
 # CONFIG_FB_ASILIANT is not set
 # CONFIG_FB_ASILIANT is not set
 # CONFIG_FB_IMSTT is not set
 # CONFIG_FB_IMSTT is not set
 # CONFIG_FB_VGA16 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_MATROX is not set
 # CONFIG_FB_RADEON_OLD is not set
 # CONFIG_FB_RADEON_OLD is not set
 CONFIG_FB_RADEON=y
 CONFIG_FB_RADEON=y
@@ -924,7 +979,96 @@ CONFIG_LCD_DEVICE=y
 #
 #
 # Sound
 # 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
 # USB support
@@ -958,12 +1102,16 @@ CONFIG_USB_OHCI_LITTLE_ENDIAN=y
 #
 #
 # USB Device Class drivers
 # 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_ACM=m
 CONFIG_USB_PRINTER=y
 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=y
 # CONFIG_USB_STORAGE_DEBUG is not set
 # CONFIG_USB_STORAGE_DEBUG is not set
@@ -1074,6 +1222,7 @@ CONFIG_USB_SERIAL_KEYSPAN_USA49WLC=y
 CONFIG_USB_SERIAL_KLSI=m
 CONFIG_USB_SERIAL_KLSI=m
 CONFIG_USB_SERIAL_KOBIL_SCT=m
 CONFIG_USB_SERIAL_KOBIL_SCT=m
 CONFIG_USB_SERIAL_MCT_U232=m
 CONFIG_USB_SERIAL_MCT_U232=m
+# CONFIG_USB_SERIAL_NOKIA_DKU2 is not set
 CONFIG_USB_SERIAL_PL2303=m
 CONFIG_USB_SERIAL_PL2303=m
 # CONFIG_USB_SERIAL_HP4X is not set
 # CONFIG_USB_SERIAL_HP4X is not set
 CONFIG_USB_SERIAL_SAFE=m
 CONFIG_USB_SERIAL_SAFE=m
@@ -1310,6 +1459,20 @@ CONFIG_NLS_ISO8859_15=y
 # CONFIG_NLS_KOI8_U is not set
 # CONFIG_NLS_KOI8_U is not set
 CONFIG_NLS_UTF8=y
 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
 # Profiling support
 #
 #
@@ -1331,12 +1494,14 @@ CONFIG_DETECT_SOFTLOCKUP=y
 # CONFIG_DEBUG_KOBJECT is not set
 # CONFIG_DEBUG_KOBJECT is not set
 # CONFIG_DEBUG_INFO is not set
 # CONFIG_DEBUG_INFO is not set
 CONFIG_DEBUG_FS=y
 CONFIG_DEBUG_FS=y
+# CONFIG_DEBUG_VM is not set
+# CONFIG_RCU_TORTURE_TEST is not set
 # CONFIG_DEBUG_STACKOVERFLOW is not set
 # CONFIG_DEBUG_STACKOVERFLOW is not set
 # CONFIG_KPROBES is not set
 # CONFIG_KPROBES is not set
 # CONFIG_DEBUG_STACK_USAGE is not set
 # CONFIG_DEBUG_STACK_USAGE is not set
 # CONFIG_DEBUGGER is not set
 # CONFIG_DEBUGGER is not set
-# CONFIG_PPCDBG is not set
 CONFIG_IRQSTACKS=y
 CONFIG_IRQSTACKS=y
+CONFIG_BOOTX_TEXT=y
 
 
 #
 #
 # Security options
 # Security options
@@ -1376,17 +1541,3 @@ CONFIG_CRYPTO_TEST=m
 #
 #
 # Hardware crypto devices
 # 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

+ 70 - 0
arch/powerpc/kernel/misc_64.S

@@ -603,6 +603,76 @@ _GLOBAL(real_writeb)
 	blr
 	blr
 #endif /* defined(CONFIG_PPC_PMAC) || defined(CONFIG_PPC_MAPLE) */
 #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
  * Create a kernel thread
  *   kernel_thread(fn, arg, flags)
  *   kernel_thread(fn, arg, flags)

+ 1 - 3
arch/powerpc/kernel/process.c

@@ -46,10 +46,10 @@
 #include <asm/processor.h>
 #include <asm/processor.h>
 #include <asm/mmu.h>
 #include <asm/mmu.h>
 #include <asm/prom.h>
 #include <asm/prom.h>
+#include <asm/machdep.h>
 #ifdef CONFIG_PPC64
 #ifdef CONFIG_PPC64
 #include <asm/firmware.h>
 #include <asm/firmware.h>
 #include <asm/time.h>
 #include <asm/time.h>
-#include <asm/machdep.h>
 #endif
 #endif
 
 
 extern unsigned long _get_SP(void);
 extern unsigned long _get_SP(void);
@@ -203,10 +203,8 @@ int dump_spe(struct pt_regs *regs, elf_vrregset_t *evrregs)
 
 
 int set_dabr(unsigned long dabr)
 int set_dabr(unsigned long dabr)
 {
 {
-#ifdef CONFIG_PPC64
 	if (ppc_md.set_dabr)
 	if (ppc_md.set_dabr)
 		return ppc_md.set_dabr(dabr);
 		return ppc_md.set_dabr(dabr);
-#endif
 
 
 	mtspr(SPRN_DABR, dabr);
 	mtspr(SPRN_DABR, dabr);
 	return 0;
 	return 0;

+ 18 - 3
arch/powerpc/kernel/prom.c

@@ -1974,14 +1974,29 @@ EXPORT_SYMBOL(get_property);
 /*
 /*
  * Add a property to a node
  * 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;	
 	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 = &(*next)->next;
+	}
 	*next = prop;
 	*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 ! */
 /* I quickly hacked that one, check against spec ! */

+ 6 - 5
arch/powerpc/kernel/prom_init.c

@@ -403,19 +403,19 @@ static int __init prom_next_node(phandle *nodep)
 	}
 	}
 }
 }
 
 
-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)
 			       void *value, size_t valuelen)
 {
 {
 	return call_prom("getprop", 4, 1, node, ADDR(pname),
 	return call_prom("getprop", 4, 1, node, ADDR(pname),
 			 (u32)(unsigned long) value, (u32) valuelen);
 			 (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));
 	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)
 			       void *value, size_t valuelen)
 {
 {
 	return call_prom("setprop", 4, 1, node, ADDR(pname),
 	return call_prom("setprop", 4, 1, node, ADDR(pname),
@@ -1408,8 +1408,9 @@ static int __init prom_find_machine_type(void)
 	struct prom_t *_prom = &RELOC(prom);
 	struct prom_t *_prom = &RELOC(prom);
 	char compat[256];
 	char compat[256];
 	int len, i = 0;
 	int len, i = 0;
+#ifdef CONFIG_PPC64
 	phandle rtas;
 	phandle rtas;
-
+#endif
 	len = prom_getprop(_prom->root, "compatible",
 	len = prom_getprop(_prom->root, "compatible",
 			   compat, sizeof(compat)-1);
 			   compat, sizeof(compat)-1);
 	if (len > 0) {
 	if (len > 0) {
@@ -1872,7 +1873,7 @@ static void __init fixup_device_tree(void)
 	if (prom_getprop(u3, "device-rev", &u3_rev, sizeof(u3_rev))
 	if (prom_getprop(u3, "device-rev", &u3_rev, sizeof(u3_rev))
 	    == PROM_ERROR)
 	    == PROM_ERROR)
 		return;
 		return;
-	if (u3_rev != 0x35 && u3_rev != 0x37)
+	if (u3_rev < 0x35 || u3_rev > 0x39)
 		return;
 		return;
 	/* does it need fixup ? */
 	/* does it need fixup ? */
 	if (prom_getproplen(i2c, "interrupts") > 0)
 	if (prom_getproplen(i2c, "interrupts") > 0)

+ 2 - 3
arch/powerpc/kernel/rtas.c

@@ -17,6 +17,7 @@
 #include <linux/spinlock.h>
 #include <linux/spinlock.h>
 #include <linux/module.h>
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/init.h>
+#include <linux/delay.h>
 
 
 #include <asm/prom.h>
 #include <asm/prom.h>
 #include <asm/rtas.h>
 #include <asm/rtas.h>
@@ -83,7 +84,7 @@ void call_rtas_display_status_delay(unsigned char c)
 		while (width-- > 0)
 		while (width-- > 0)
 			call_rtas_display_status(' ');
 			call_rtas_display_status(' ');
 		width = 16;
 		width = 16;
-		udelay(500000);
+		mdelay(500);
 		pending_newline = 1;
 		pending_newline = 1;
 	} else {
 	} else {
 		if (pending_newline) {
 		if (pending_newline) {
@@ -608,7 +609,6 @@ asmlinkage int ppc_rtas(struct rtas_args __user *uargs)
 	return 0;
 	return 0;
 }
 }
 
 
-#ifdef CONFIG_SMP
 /* This version can't take the spinlock, because it never returns */
 /* This version can't take the spinlock, because it never returns */
 
 
 struct rtas_args rtas_stop_self_args = {
 struct rtas_args rtas_stop_self_args = {
@@ -633,7 +633,6 @@ void rtas_stop_self(void)
 
 
 	panic("Alas, I survived.\n");
 	panic("Alas, I survived.\n");
 }
 }
-#endif
 
 
 /*
 /*
  * Call early during boot, before mem init or bootmem, to retreive the RTAS
  * Call early during boot, before mem init or bootmem, to retreive the RTAS

+ 40 - 0
arch/powerpc/kernel/setup-common.c

@@ -405,6 +405,46 @@ static int __init set_preferred_console(void)
 console_initcall(set_preferred_console);
 console_initcall(set_preferred_console);
 #endif /* CONFIG_PPC_MULTIPLATFORM */
 #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
 #ifdef CONFIG_SMP
 
 
 /**
 /**

+ 1 - 0
arch/powerpc/kernel/setup_32.c

@@ -286,6 +286,7 @@ void __init setup_arch(char **cmdline_p)
 	loops_per_jiffy = 500000000 / HZ;
 	loops_per_jiffy = 500000000 / HZ;
 
 
 	unflatten_device_tree();
 	unflatten_device_tree();
+	check_for_initrd();
 	finish_device_tree();
 	finish_device_tree();
 
 
 	smp_setup_cpu_maps();
 	smp_setup_cpu_maps();

+ 1 - 45
arch/powerpc/kernel/setup_64.c

@@ -41,7 +41,6 @@
 #include <asm/elf.h>
 #include <asm/elf.h>
 #include <asm/machdep.h>
 #include <asm/machdep.h>
 #include <asm/paca.h>
 #include <asm/paca.h>
-#include <asm/ppcdebug.h>
 #include <asm/time.h>
 #include <asm/time.h>
 #include <asm/cputable.h>
 #include <asm/cputable.h>
 #include <asm/sections.h>
 #include <asm/sections.h>
@@ -60,6 +59,7 @@
 #include <asm/firmware.h>
 #include <asm/firmware.h>
 #include <asm/systemcfg.h>
 #include <asm/systemcfg.h>
 #include <asm/xmon.h>
 #include <asm/xmon.h>
+#include <asm/udbg.h>
 
 
 #ifdef DEBUG
 #ifdef DEBUG
 #define DBG(fmt...) udbg_printf(fmt)
 #define DBG(fmt...) udbg_printf(fmt)
@@ -243,12 +243,6 @@ void __init early_setup(unsigned long dt_ptr)
 
 
 	DBG(" -> early_setup()\n");
 	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
 	 * Do early initializations using the flattened device
 	 * tree, like retreiving the physical memory map or
 	 * tree, like retreiving the physical memory map or
@@ -401,43 +395,6 @@ static void __init initialize_cache_info(void)
 	DBG(" <- initialize_cache_info()\n");
 	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 
  * Do some initial setup of the system.  The parameters are those which 
@@ -521,7 +478,6 @@ void __init setup_system(void)
 
 
 	printk("-----------------------------------------------------\n");
 	printk("-----------------------------------------------------\n");
 	printk("ppc64_pft_size                = 0x%lx\n", ppc64_pft_size);
 	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("ppc64_interrupt_controller    = 0x%ld\n", ppc64_interrupt_controller);
 	printk("systemcfg                     = 0x%p\n", systemcfg);
 	printk("systemcfg                     = 0x%p\n", systemcfg);
 	printk("systemcfg->platform           = 0x%x\n", systemcfg->platform);
 	printk("systemcfg->platform           = 0x%x\n", systemcfg->platform);

+ 0 - 1
arch/powerpc/kernel/signal_32.c

@@ -44,7 +44,6 @@
 #include <asm/cacheflush.h>
 #include <asm/cacheflush.h>
 #ifdef CONFIG_PPC64
 #ifdef CONFIG_PPC64
 #include "ppc32.h"
 #include "ppc32.h"
-#include <asm/ppcdebug.h>
 #include <asm/unistd.h>
 #include <asm/unistd.h>
 #include <asm/vdso.h>
 #include <asm/vdso.h>
 #else
 #else

+ 0 - 1
arch/powerpc/kernel/signal_64.c

@@ -33,7 +33,6 @@
 #include <asm/ucontext.h>
 #include <asm/ucontext.h>
 #include <asm/uaccess.h>
 #include <asm/uaccess.h>
 #include <asm/pgtable.h>
 #include <asm/pgtable.h>
-#include <asm/ppcdebug.h>
 #include <asm/unistd.h>
 #include <asm/unistd.h>
 #include <asm/cacheflush.h>
 #include <asm/cacheflush.h>
 #include <asm/vdso.h>
 #include <asm/vdso.h>

+ 0 - 1
arch/powerpc/kernel/smp.c

@@ -40,7 +40,6 @@
 #include <asm/prom.h>
 #include <asm/prom.h>
 #include <asm/smp.h>
 #include <asm/smp.h>
 #include <asm/time.h>
 #include <asm/time.h>
-#include <asm/xmon.h>
 #include <asm/machdep.h>
 #include <asm/machdep.h>
 #include <asm/cputable.h>
 #include <asm/cputable.h>
 #include <asm/system.h>
 #include <asm/system.h>

+ 1 - 4
arch/powerpc/kernel/time.c

@@ -61,6 +61,7 @@
 #include <asm/prom.h>
 #include <asm/prom.h>
 #include <asm/irq.h>
 #include <asm/irq.h>
 #include <asm/div64.h>
 #include <asm/div64.h>
+#include <asm/smp.h>
 #ifdef CONFIG_PPC64
 #ifdef CONFIG_PPC64
 #include <asm/systemcfg.h>
 #include <asm/systemcfg.h>
 #include <asm/firmware.h>
 #include <asm/firmware.h>
@@ -119,10 +120,6 @@ static unsigned adjusting_time = 0;
 unsigned long ppc_proc_freq;
 unsigned long ppc_proc_freq;
 unsigned long ppc_tb_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;
 u64 tb_last_jiffy __cacheline_aligned_in_smp;
 unsigned long tb_last_stamp;
 unsigned long tb_last_stamp;
 
 

+ 0 - 11
arch/powerpc/kernel/traps.c

@@ -39,7 +39,6 @@
 #include <asm/io.h>
 #include <asm/io.h>
 #include <asm/machdep.h>
 #include <asm/machdep.h>
 #include <asm/rtas.h>
 #include <asm/rtas.h>
-#include <asm/xmon.h>
 #include <asm/pmc.h>
 #include <asm/pmc.h>
 #ifdef CONFIG_PPC32
 #ifdef CONFIG_PPC32
 #include <asm/reg.h>
 #include <asm/reg.h>
@@ -748,22 +747,12 @@ static int check_bug_trap(struct pt_regs *regs)
 		return 0;
 		return 0;
 	if (bug->line & BUG_WARNING_TRAP) {
 	if (bug->line & BUG_WARNING_TRAP) {
 		/* this is a WARN_ON rather than BUG/BUG_ON */
 		/* 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",
 		printk(KERN_ERR "Badness in %s at %s:%ld\n",
 		       bug->function, bug->file,
 		       bug->function, bug->file,
 		       bug->line & ~BUG_WARNING_TRAP);
 		       bug->line & ~BUG_WARNING_TRAP);
 		dump_stack();
 		dump_stack();
 		return 1;
 		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",
 	printk(KERN_CRIT "kernel BUG in %s at %s:%ld!\n",
 	       bug->function, bug->file, bug->line);
 	       bug->function, bug->file, bug->line);
 
 

+ 1 - 0
arch/powerpc/lib/locks.c

@@ -23,6 +23,7 @@
 #if defined(CONFIG_PPC_SPLPAR) || defined(CONFIG_PPC_ISERIES)
 #if defined(CONFIG_PPC_SPLPAR) || defined(CONFIG_PPC_ISERIES)
 #include <asm/hvcall.h>
 #include <asm/hvcall.h>
 #include <asm/iseries/hv_call.h>
 #include <asm/iseries/hv_call.h>
+#include <asm/smp.h>
 
 
 void __spin_yield(raw_spinlock_t *lock)
 void __spin_yield(raw_spinlock_t *lock)
 {
 {

+ 17 - 0
arch/powerpc/mm/fault.c

@@ -389,5 +389,22 @@ void bad_page_fault(struct pt_regs *regs, unsigned long address, int sig)
 	}
 	}
 
 
 	/* kernel has accessed a bad area */
 	/* 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);
 	die("Kernel access of bad area", regs, sig);
 }
 }

+ 3 - 7
arch/powerpc/mm/hash_utils_64.c

@@ -33,7 +33,6 @@
 #include <linux/init.h>
 #include <linux/init.h>
 #include <linux/signal.h>
 #include <linux/signal.h>
 
 
-#include <asm/ppcdebug.h>
 #include <asm/processor.h>
 #include <asm/processor.h>
 #include <asm/pgtable.h>
 #include <asm/pgtable.h>
 #include <asm/mmu.h>
 #include <asm/mmu.h>
@@ -409,12 +408,6 @@ void __init htab_initialize(void)
 	htab_size_bytes = htab_get_table_size();
 	htab_size_bytes = htab_get_table_size();
 	pteg_count = htab_size_bytes >> 7;
 	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;
 	htab_hash_mask = pteg_count - 1;
 
 
 	if (systemcfg->platform & PLATFORM_LPAR) {
 	if (systemcfg->platform & PLATFORM_LPAR) {
@@ -514,6 +507,9 @@ unsigned int hash_page_do_lazy_icache(unsigned int pp, pte_t pte, int trap)
 {
 {
 	struct page *page;
 	struct page *page;
 
 
+	if (!pfn_valid(pte_pfn(pte)))
+		return pp;
+
 	page = pte_page(pte);
 	page = pte_page(pte);
 
 
 	/* page is dirty */
 	/* page is dirty */

+ 0 - 1
arch/powerpc/mm/init_64.c

@@ -57,7 +57,6 @@
 #include <asm/processor.h>
 #include <asm/processor.h>
 #include <asm/mmzone.h>
 #include <asm/mmzone.h>
 #include <asm/cputable.h>
 #include <asm/cputable.h>
-#include <asm/ppcdebug.h>
 #include <asm/sections.h>
 #include <asm/sections.h>
 #include <asm/system.h>
 #include <asm/system.h>
 #include <asm/iommu.h>
 #include <asm/iommu.h>

+ 1 - 1
arch/powerpc/mm/mem.c

@@ -358,7 +358,7 @@ void __init mem_init(void)
 	}
 	}
 
 
 	codesize = (unsigned long)&_sdata - (unsigned long)&_stext;
 	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;
 	initsize = (unsigned long)&__init_end - (unsigned long)&__init_begin;
 	bsssize = (unsigned long)&__bss_stop - (unsigned long)&__bss_start;
 	bsssize = (unsigned long)&__bss_stop - (unsigned long)&__bss_start;
 
 

+ 1 - 0
arch/powerpc/mm/numa.c

@@ -21,6 +21,7 @@
 #include <asm/machdep.h>
 #include <asm/machdep.h>
 #include <asm/abs_addr.h>
 #include <asm/abs_addr.h>
 #include <asm/system.h>
 #include <asm/system.h>
+#include <asm/smp.h>
 
 
 static int numa_enabled = 1;
 static int numa_enabled = 1;
 
 

+ 0 - 1
arch/powerpc/mm/pgtable_64.c

@@ -59,7 +59,6 @@
 #include <asm/processor.h>
 #include <asm/processor.h>
 #include <asm/mmzone.h>
 #include <asm/mmzone.h>
 #include <asm/cputable.h>
 #include <asm/cputable.h>
-#include <asm/ppcdebug.h>
 #include <asm/sections.h>
 #include <asm/sections.h>
 #include <asm/system.h>
 #include <asm/system.h>
 #include <asm/iommu.h>
 #include <asm/iommu.h>

+ 23 - 1
arch/powerpc/oprofile/op_model_power4.c

@@ -17,6 +17,7 @@
 #include <asm/systemcfg.h>
 #include <asm/systemcfg.h>
 #include <asm/rtas.h>
 #include <asm/rtas.h>
 #include <asm/oprofile_impl.h>
 #include <asm/oprofile_impl.h>
+#include <asm/reg.h>
 
 
 #define dbg(args...)
 #define dbg(args...)
 
 
@@ -81,6 +82,26 @@ static void power4_reg_setup(struct op_counter_config *ctr,
 
 
 extern void ppc64_enable_pmcs(void);
 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)
 static void power4_cpu_setup(void *unused)
 {
 {
 	unsigned int mmcr0 = mmcr0_val;
 	unsigned int mmcr0 = mmcr0_val;
@@ -98,7 +119,8 @@ static void power4_cpu_setup(void *unused)
 
 
 	mtspr(SPRN_MMCR1, mmcr1_val);
 	mtspr(SPRN_MMCR1, mmcr1_val);
 
 
-	mmcra |= MMCRA_SAMPLE_ENABLE;
+	if (mmcra_must_set_sample())
+		mmcra |= MMCRA_SAMPLE_ENABLE;
 	mtspr(SPRN_MMCRA, mmcra);
 	mtspr(SPRN_MMCRA, mmcra);
 
 
 	dbg("setup on cpu %d, mmcr0 %lx\n", smp_processor_id(),
 	dbg("setup on cpu %d, mmcr0 %lx\n", smp_processor_id(),

+ 0 - 5
arch/powerpc/platforms/iseries/irq.c

@@ -35,7 +35,6 @@
 #include <linux/irq.h>
 #include <linux/irq.h>
 #include <linux/spinlock.h>
 #include <linux/spinlock.h>
 
 
-#include <asm/ppcdebug.h>
 #include <asm/iseries/hv_types.h>
 #include <asm/iseries/hv_types.h>
 #include <asm/iseries/hv_lp_event.h>
 #include <asm/iseries/hv_lp_event.h>
 #include <asm/iseries/hv_call_xm.h>
 #include <asm/iseries/hv_call_xm.h>
@@ -227,8 +226,6 @@ static void iSeries_enable_IRQ(unsigned int irq)
 	/* Unmask secondary INTA */
 	/* Unmask secondary INTA */
 	mask = 0x80000000;
 	mask = 0x80000000;
 	HvCallPci_unmaskInterrupts(bus, subBus, deviceId, mask);
 	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 */
 /* This is called by iSeries_activate_IRQs */
@@ -310,8 +307,6 @@ static void iSeries_disable_IRQ(unsigned int irq)
 	/* Mask secondary INTA   */
 	/* Mask secondary INTA   */
 	mask = 0x80000000;
 	mask = 0x80000000;
 	HvCallPci_maskInterrupts(bus, subBus, deviceId, mask);
 	HvCallPci_maskInterrupts(bus, subBus, deviceId, mask);
-	PPCDBG(PPCDBG_BUSWALK, "iSeries_disable_IRQ 0x%02X.%02X.%02X 0x%04X\n",
-			bus, subBus, deviceId, irq);
 }
 }
 
 
 /*
 /*

+ 0 - 37
arch/powerpc/platforms/iseries/pci.c

@@ -32,7 +32,6 @@
 #include <asm/prom.h>
 #include <asm/prom.h>
 #include <asm/machdep.h>
 #include <asm/machdep.h>
 #include <asm/pci-bridge.h>
 #include <asm/pci-bridge.h>
-#include <asm/ppcdebug.h>
 #include <asm/iommu.h>
 #include <asm/iommu.h>
 #include <asm/abs_addr.h>
 #include <asm/abs_addr.h>
 
 
@@ -207,10 +206,6 @@ static struct device_node *build_device_node(HvBusNumber Bus,
 	struct device_node *node;
 	struct device_node *node;
 	struct pci_dn *pdn;
 	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);
 	node = kmalloc(sizeof(struct device_node), GFP_KERNEL);
 	if (node == NULL)
 	if (node == NULL)
 		return NULL;
 		return NULL;
@@ -243,8 +238,6 @@ unsigned long __init find_and_init_phbs(void)
 	struct pci_controller *phb;
 	struct pci_controller *phb;
 	HvBusNumber bus;
 	HvBusNumber bus;
 
 
-	PPCDBG(PPCDBG_BUSWALK, "find_and_init_phbs Entry\n");
-
 	/* Check all possible buses. */
 	/* Check all possible buses. */
 	for (bus = 0; bus < 256; bus++) {
 	for (bus = 0; bus < 256; bus++) {
 		int ret = HvCallXm_testBus(bus);
 		int ret = HvCallXm_testBus(bus);
@@ -261,9 +254,6 @@ unsigned long __init find_and_init_phbs(void)
 			phb->last_busno = bus;
 			phb->last_busno = bus;
 			phb->ops = &iSeries_pci_ops;
 			phb->ops = &iSeries_pci_ops;
 
 
-			PPCDBG(PPCDBG_BUSWALK, "PCI:Create iSeries pci_controller(%p), Bus: %04X\n",
-					phb, bus);
-
 			/* Find and connect the devices. */
 			/* Find and connect the devices. */
 			scan_PHB_slots(phb);
 			scan_PHB_slots(phb);
 		}
 		}
@@ -285,11 +275,9 @@ unsigned long __init find_and_init_phbs(void)
  */
  */
 void iSeries_pcibios_init(void)
 void iSeries_pcibios_init(void)
 {
 {
-	PPCDBG(PPCDBG_BUSWALK, "iSeries_pcibios_init Entry.\n");
 	iomm_table_initialize();
 	iomm_table_initialize();
 	find_and_init_phbs();
 	find_and_init_phbs();
 	io_page_mask = -1;
 	io_page_mask = -1;
-	PPCDBG(PPCDBG_BUSWALK, "iSeries_pcibios_init Exit.\n");
 }
 }
 
 
 /*
 /*
@@ -301,8 +289,6 @@ void __init iSeries_pci_final_fixup(void)
 	struct device_node *node;
 	struct device_node *node;
 	int DeviceCount = 0;
 	int DeviceCount = 0;
 
 
-	PPCDBG(PPCDBG_BUSWALK, "iSeries_pcibios_fixup Entry.\n");
-
 	/* Fix up at the device node and pci_dev relationship */
 	/* Fix up at the device node and pci_dev relationship */
 	mf_display_src(0xC9000100);
 	mf_display_src(0xC9000100);
 
 
@@ -316,9 +302,6 @@ void __init iSeries_pci_final_fixup(void)
 			++DeviceCount;
 			++DeviceCount;
 			pdev->sysdata = (void *)node;
 			pdev->sysdata = (void *)node;
 			PCI_DN(node)->pcidev = pdev;
 			PCI_DN(node)->pcidev = pdev;
-			PPCDBG(PPCDBG_BUSWALK,
-					"pdev 0x%p <==> DevNode 0x%p\n",
-					pdev, node);
 			allocate_device_bars(pdev);
 			allocate_device_bars(pdev);
 			iSeries_Device_Information(pdev, DeviceCount);
 			iSeries_Device_Information(pdev, DeviceCount);
 			iommu_devnode_init_iSeries(node);
 			iommu_devnode_init_iSeries(node);
@@ -333,13 +316,10 @@ void __init iSeries_pci_final_fixup(void)
 
 
 void pcibios_fixup_bus(struct pci_bus *PciBus)
 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)
 void pcibios_fixup_resources(struct pci_dev *pdev)
 {
 {
-	PPCDBG(PPCDBG_BUSWALK, "fixup_resources pdev %p\n", pdev);
 }
 }
 
 
 /*
 /*
@@ -401,9 +381,6 @@ static void scan_EADS_bridge(HvBusNumber bus, HvSubBusNumber SubBus,
 			printk("found device at bus %d idsel %d func %d (AgentId %x)\n",
 			printk("found device at bus %d idsel %d func %d (AgentId %x)\n",
 			       bus, IdSel, Function, AgentId);
 			       bus, IdSel, Function, AgentId);
 			/*  Connect EADs: 0x18.00.12 = 0x00 */
 			/*  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,
 			HvRc = HvCallPci_getBusUnitInfo(bus, SubBus, AgentId,
 					iseries_hv_addr(BridgeInfo),
 					iseries_hv_addr(BridgeInfo),
 					sizeof(struct HvCallPci_BridgeInfo));
 					sizeof(struct HvCallPci_BridgeInfo));
@@ -414,14 +391,6 @@ static void scan_EADS_bridge(HvBusNumber bus, HvSubBusNumber SubBus,
 					BridgeInfo->maxAgents,
 					BridgeInfo->maxAgents,
 					BridgeInfo->maxSubBusNumber,
 					BridgeInfo->maxSubBusNumber,
 					BridgeInfo->logicalSlotNumber);
 					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 ==
 				if (BridgeInfo->busUnitInfo.deviceType ==
 						HvCallPci_BridgeDevice)  {
 						HvCallPci_BridgeDevice)  {
 					/* Scan_Bridge_Slot...: 0x18.00.12 */
 					/* Scan_Bridge_Slot...: 0x18.00.12 */
@@ -454,9 +423,6 @@ static int scan_bridge_slot(HvBusNumber Bus,
 
 
 	/* iSeries_allocate_IRQ.: 0x18.00.12(0xA3) */
 	/* iSeries_allocate_IRQ.: 0x18.00.12(0xA3) */
 	Irq = iSeries_allocate_IRQ(Bus, 0, EADsIdSel);
 	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.
 	 * Connect all functions of any device found.
@@ -482,9 +448,6 @@ static int scan_bridge_slot(HvBusNumber Bus,
 			printk("read vendor ID: %x\n", VendorId);
 			printk("read vendor ID: %x\n", VendorId);
 
 
 			/* FoundDevice: 0x18.28.10 = 0x12AE */
 			/* 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,
 			HvRc = HvCallPci_configStore8(Bus, SubBus, AgentId,
 						      PCI_INTERRUPT_LINE, Irq);
 						      PCI_INTERRUPT_LINE, Irq);
 			if (HvRc != 0)
 			if (HvRc != 0)

+ 0 - 4
arch/powerpc/platforms/iseries/setup.c

@@ -71,8 +71,6 @@ extern void hvlog(char *fmt, ...);
 #endif
 #endif
 
 
 /* Function Prototypes */
 /* Function Prototypes */
-extern void ppcdbg_initialize(void);
-
 static void build_iSeries_Memory_Map(void);
 static void build_iSeries_Memory_Map(void);
 static void iseries_shared_idle(void);
 static void iseries_shared_idle(void);
 static void iseries_dedicated_idle(void);
 static void iseries_dedicated_idle(void);
@@ -309,8 +307,6 @@ static void __init iSeries_init_early(void)
 
 
 	ppc64_firmware_features = FW_FEATURE_ISERIES;
 	ppc64_firmware_features = FW_FEATURE_ISERIES;
 
 
-	ppcdbg_initialize();
-
 	ppc64_interrupt_controller = IC_ISERIES;
 	ppc64_interrupt_controller = IC_ISERIES;
 
 
 #if defined(CONFIG_BLK_DEV_INITRD)
 #if defined(CONFIG_BLK_DEV_INITRD)

+ 0 - 1
arch/powerpc/platforms/iseries/smp.c

@@ -40,7 +40,6 @@
 #include <asm/paca.h>
 #include <asm/paca.h>
 #include <asm/iseries/hv_call.h>
 #include <asm/iseries/hv_call.h>
 #include <asm/time.h>
 #include <asm/time.h>
-#include <asm/ppcdebug.h>
 #include <asm/machdep.h>
 #include <asm/machdep.h>
 #include <asm/cputable.h>
 #include <asm/cputable.h>
 #include <asm/system.h>
 #include <asm/system.h>

+ 2 - 1
arch/powerpc/platforms/powermac/Makefile

@@ -1,7 +1,8 @@
 obj-y				+= pic.o setup.o time.o feature.o pci.o \
 obj-y				+= pic.o setup.o time.o feature.o pci.o \
 				   sleep.o low_i2c.o cache.o
 				   sleep.o low_i2c.o cache.o
 obj-$(CONFIG_PMAC_BACKLIGHT)	+= backlight.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
 obj-$(CONFIG_NVRAM)		+= nvram.o
 # ppc64 pmac doesn't define CONFIG_NVRAM but needs nvram stuff
 # ppc64 pmac doesn't define CONFIG_NVRAM but needs nvram stuff
 obj-$(CONFIG_PPC64)		+= nvram.o
 obj-$(CONFIG_PPC64)		+= nvram.o

+ 8 - 7
arch/powerpc/platforms/powermac/cpufreq.c → arch/powerpc/platforms/powermac/cpufreq_32.c

@@ -397,18 +397,16 @@ static int pmac_cpufreq_target(	struct cpufreq_policy *policy,
 					unsigned int relation)
 					unsigned int relation)
 {
 {
 	unsigned int    newstate = 0;
 	unsigned int    newstate = 0;
+	int		rc;
 
 
 	if (cpufreq_frequency_table_target(policy, pmac_cpu_freqs,
 	if (cpufreq_frequency_table_target(policy, pmac_cpu_freqs,
 			target_freq, relation, &newstate))
 			target_freq, relation, &newstate))
 		return -EINVAL;
 		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)
 static int pmac_cpufreq_cpu_init(struct cpufreq_policy *policy)
@@ -474,6 +472,8 @@ static int pmac_cpufreq_resume(struct cpufreq_policy *policy)
 	do_set_cpu_speed(sleep_freq == low_freq ?
 	do_set_cpu_speed(sleep_freq == low_freq ?
 			 CPUFREQ_LOW : CPUFREQ_HIGH, 0);
 			 CPUFREQ_LOW : CPUFREQ_HIGH, 0);
 
 
+	ppc_proc_freq = cur_freq * 1000ul;
+
 	no_schedule = 0;
 	no_schedule = 0;
 	return 0;
 	return 0;
 }
 }
@@ -547,7 +547,7 @@ static int pmac_cpufreq_init_MacRISC3(struct device_node *cpunode)
 		 */
 		 */
 		if (low_freq < 98000000)
 		if (low_freq < 98000000)
 			low_freq = 101000000;
 			low_freq = 101000000;
-			
+
 		/* Convert those to CPU core clocks */
 		/* Convert those to CPU core clocks */
 		low_freq = (low_freq * (*ratio)) / 2000;
 		low_freq = (low_freq * (*ratio)) / 2000;
 		hi_freq = (hi_freq * (*ratio)) / 2000;
 		hi_freq = (hi_freq * (*ratio)) / 2000;
@@ -714,6 +714,7 @@ out:
 
 
 	pmac_cpu_freqs[CPUFREQ_LOW].frequency = low_freq;
 	pmac_cpu_freqs[CPUFREQ_LOW].frequency = low_freq;
 	pmac_cpu_freqs[CPUFREQ_HIGH].frequency = hi_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 "Registering PowerMac CPU frequency driver\n");
 	printk(KERN_INFO "Low: %d Mhz, High: %d Mhz, Boot: %d Mhz\n",
 	printk(KERN_INFO "Low: %d Mhz, High: %d Mhz, Boot: %d Mhz\n",

+ 323 - 0
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");

+ 0 - 13
arch/powerpc/platforms/powermac/setup.c

@@ -193,18 +193,6 @@ static void pmac_show_cpuinfo(struct seq_file *m)
 		   pmac_newworld ? "NewWorld" : "OldWorld");
 		   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
 #ifndef CONFIG_ADB_CUDA
 int find_via_cuda(void)
 int find_via_cuda(void)
 {
 {
@@ -767,7 +755,6 @@ struct machdep_calls __initdata pmac_md = {
 	.setup_arch		= pmac_setup_arch,
 	.setup_arch		= pmac_setup_arch,
 	.init_early		= pmac_init_early,
 	.init_early		= pmac_init_early,
 	.show_cpuinfo		= pmac_show_cpuinfo,
 	.show_cpuinfo		= pmac_show_cpuinfo,
-	.show_percpuinfo	= pmac_show_percpuinfo,
 	.init_IRQ		= pmac_pic_init,
 	.init_IRQ		= pmac_pic_init,
 	.get_irq		= mpic_get_irq,	/* changed later */
 	.get_irq		= mpic_get_irq,	/* changed later */
 	.pcibios_fixup		= pmac_pcibios_fixup,
 	.pcibios_fixup		= pmac_pcibios_fixup,

+ 1 - 1
arch/powerpc/platforms/pseries/iommu.c

@@ -37,7 +37,6 @@
 #include <asm/io.h>
 #include <asm/io.h>
 #include <asm/prom.h>
 #include <asm/prom.h>
 #include <asm/rtas.h>
 #include <asm/rtas.h>
-#include <asm/ppcdebug.h>
 #include <asm/iommu.h>
 #include <asm/iommu.h>
 #include <asm/pci-bridge.h>
 #include <asm/pci-bridge.h>
 #include <asm/machdep.h>
 #include <asm/machdep.h>
@@ -47,6 +46,7 @@
 #include <asm/firmware.h>
 #include <asm/firmware.h>
 #include <asm/tce.h>
 #include <asm/tce.h>
 #include <asm/ppc-pci.h>
 #include <asm/ppc-pci.h>
+#include <asm/udbg.h>
 
 
 #include "plpar_wrappers.h"
 #include "plpar_wrappers.h"
 
 

+ 2 - 1
arch/powerpc/platforms/pseries/lpar.c

@@ -31,13 +31,14 @@
 #include <asm/machdep.h>
 #include <asm/machdep.h>
 #include <asm/abs_addr.h>
 #include <asm/abs_addr.h>
 #include <asm/mmu_context.h>
 #include <asm/mmu_context.h>
-#include <asm/ppcdebug.h>
 #include <asm/iommu.h>
 #include <asm/iommu.h>
 #include <asm/tlbflush.h>
 #include <asm/tlbflush.h>
 #include <asm/tlb.h>
 #include <asm/tlb.h>
 #include <asm/prom.h>
 #include <asm/prom.h>
 #include <asm/abs_addr.h>
 #include <asm/abs_addr.h>
 #include <asm/cputable.h>
 #include <asm/cputable.h>
+#include <asm/udbg.h>
+#include <asm/smp.h>
 
 
 #include "plpar_wrappers.h"
 #include "plpar_wrappers.h"
 
 

+ 0 - 10
arch/powerpc/platforms/pseries/plpar_wrappers.h

@@ -107,14 +107,4 @@ static inline long plpar_put_term_char(unsigned long termno, unsigned long len,
 			lbuf[1]);
 			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 */
 #endif /* _PSERIES_PLPAR_WRAPPERS_H */

+ 1 - 1
arch/powerpc/platforms/pseries/ras.c

@@ -48,7 +48,7 @@
 #include <asm/ptrace.h>
 #include <asm/ptrace.h>
 #include <asm/machdep.h>
 #include <asm/machdep.h>
 #include <asm/rtas.h>
 #include <asm/rtas.h>
-#include <asm/ppcdebug.h>
+#include <asm/udbg.h>
 
 
 static unsigned char ras_log_buf[RTAS_ERROR_LOG_MAX];
 static unsigned char ras_log_buf[RTAS_ERROR_LOG_MAX];
 static DEFINE_SPINLOCK(ras_log_buf_lock);
 static DEFINE_SPINLOCK(ras_log_buf_lock);

+ 11 - 7
arch/powerpc/platforms/pseries/setup.c

@@ -65,6 +65,7 @@
 #include <asm/ppc-pci.h>
 #include <asm/ppc-pci.h>
 #include <asm/i8259.h>
 #include <asm/i8259.h>
 #include <asm/udbg.h>
 #include <asm/udbg.h>
+#include <asm/smp.h>
 
 
 #include "plpar_wrappers.h"
 #include "plpar_wrappers.h"
 
 
@@ -353,14 +354,15 @@ static void pSeries_mach_cpu_die(void)
 
 
 static int pseries_set_dabr(unsigned long dabr)
 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
  * Early initialization.  Relocation is on but do not reference unbolted pages
@@ -396,8 +398,10 @@ static void __init pSeries_init_early(void)
 		DBG("Hello World !\n");
 		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;
 		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();
 	iommu_init_early_pSeries();
 
 

+ 3 - 2
arch/powerpc/sysdev/i8259.c

@@ -207,6 +207,9 @@ void __init i8259_init(unsigned long intack_addr, int offset)
 
 
 	spin_unlock_irqrestore(&i8259_lock, flags);
 	spin_unlock_irqrestore(&i8259_lock, flags);
 
 
+	for (i = 0; i < NUM_ISA_INTERRUPTS; ++i)
+		irq_desc[offset + i].handler = &i8259_pic;
+
 	/* reserve our resources */
 	/* reserve our resources */
 	setup_irq(offset + 2, &i8259_irqaction);
 	setup_irq(offset + 2, &i8259_irqaction);
 	request_resource(&ioport_resource, &pic1_iores);
 	request_resource(&ioport_resource, &pic1_iores);
@@ -216,6 +219,4 @@ void __init i8259_init(unsigned long intack_addr, int offset)
 	if (intack_addr != 0)
 	if (intack_addr != 0)
 		pci_intack = ioremap(intack_addr, 1);
 		pci_intack = ioremap(intack_addr, 1);
 
 
-	for (i = 0; i < NUM_ISA_INTERRUPTS; ++i)
-		irq_desc[offset + i].handler = &i8259_pic;
 }
 }

+ 0 - 1
arch/powerpc/sysdev/u3_iommu.c

@@ -37,7 +37,6 @@
 #include <linux/vmalloc.h>
 #include <linux/vmalloc.h>
 #include <asm/io.h>
 #include <asm/io.h>
 #include <asm/prom.h>
 #include <asm/prom.h>
-#include <asm/ppcdebug.h>
 #include <asm/iommu.h>
 #include <asm/iommu.h>
 #include <asm/pci-bridge.h>
 #include <asm/pci-bridge.h>
 #include <asm/machdep.h>
 #include <asm/machdep.h>

+ 125 - 20
arch/ppc/kernel/misc.S

@@ -25,6 +25,11 @@
 #include <asm/thread_info.h>
 #include <asm/thread_info.h>
 #include <asm/asm-offsets.h>
 #include <asm/asm-offsets.h>
 
 
+#ifdef CONFIG_8xx
+#define ISYNC_8xx isync
+#else
+#define ISYNC_8xx
+#endif
 	.text
 	.text
 
 
 	.align	5
 	.align	5
@@ -800,8 +805,18 @@ _GLOBAL(_insb)
 	subi	r4,r4,1
 	subi	r4,r4,1
 	blelr-
 	blelr-
 00:	lbz	r5,0(r3)
 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
 	bdnz	00b
 	blr
 	blr
 
 
@@ -811,8 +826,18 @@ _GLOBAL(_outsb)
 	subi	r4,r4,1
 	subi	r4,r4,1
 	blelr-
 	blelr-
 00:	lbzu	r5,1(r4)
 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
 	bdnz	00b
 	blr
 	blr
 
 
@@ -822,8 +847,18 @@ _GLOBAL(_insw)
 	subi	r4,r4,2
 	subi	r4,r4,2
 	blelr-
 	blelr-
 00:	lhbrx	r5,0,r3
 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
 	bdnz	00b
 	blr
 	blr
 
 
@@ -833,8 +868,18 @@ _GLOBAL(_outsw)
 	subi	r4,r4,2
 	subi	r4,r4,2
 	blelr-
 	blelr-
 00:	lhzu	r5,2(r4)
 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
 	bdnz	00b
 	blr
 	blr
 
 
@@ -844,8 +889,18 @@ _GLOBAL(_insl)
 	subi	r4,r4,4
 	subi	r4,r4,4
 	blelr-
 	blelr-
 00:	lwbrx	r5,0,r3
 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
 	bdnz	00b
 	blr
 	blr
 
 
@@ -855,8 +910,18 @@ _GLOBAL(_outsl)
 	subi	r4,r4,4
 	subi	r4,r4,4
 	blelr-
 	blelr-
 00:	lwzu	r5,4(r4)
 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
 	bdnz	00b
 	blr
 	blr
 
 
@@ -867,8 +932,18 @@ _GLOBAL(_insw_ns)
 	subi	r4,r4,2
 	subi	r4,r4,2
 	blelr-
 	blelr-
 00:	lhz	r5,0(r3)
 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
 	bdnz	00b
 	blr
 	blr
 
 
@@ -879,8 +954,18 @@ _GLOBAL(_outsw_ns)
 	subi	r4,r4,2
 	subi	r4,r4,2
 	blelr-
 	blelr-
 00:	lhzu	r5,2(r4)
 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
 	bdnz	00b
 	blr
 	blr
 
 
@@ -891,8 +976,18 @@ _GLOBAL(_insl_ns)
 	subi	r4,r4,4
 	subi	r4,r4,4
 	blelr-
 	blelr-
 00:	lwz	r5,0(r3)
 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
 	bdnz	00b
 	blr
 	blr
 
 
@@ -903,8 +998,18 @@ _GLOBAL(_outsl_ns)
 	subi	r4,r4,4
 	subi	r4,r4,4
 	blelr-
 	blelr-
 00:	lwzu	r5,4(r4)
 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
 	bdnz	00b
 	blr
 	blr
 
 

+ 8 - 4
arch/ppc/kernel/traps.c

@@ -49,7 +49,7 @@ extern int xmon_sstep(struct pt_regs *regs);
 extern int xmon_iabr_match(struct pt_regs *regs);
 extern int xmon_iabr_match(struct pt_regs *regs);
 extern int xmon_dabr_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_bpt)(struct pt_regs *regs) = xmon_bpt;
 int (*debugger_sstep)(struct pt_regs *regs) = xmon_sstep;
 int (*debugger_sstep)(struct pt_regs *regs) = xmon_sstep;
 int (*debugger_iabr_match)(struct pt_regs *regs) = xmon_iabr_match;
 int (*debugger_iabr_match)(struct pt_regs *regs) = xmon_iabr_match;
@@ -57,7 +57,7 @@ int (*debugger_dabr_match)(struct pt_regs *regs) = xmon_dabr_match;
 void (*debugger_fault_handler)(struct pt_regs *regs);
 void (*debugger_fault_handler)(struct pt_regs *regs);
 #else
 #else
 #ifdef CONFIG_KGDB
 #ifdef CONFIG_KGDB
-void (*debugger)(struct pt_regs *regs);
+int (*debugger)(struct pt_regs *regs);
 int (*debugger_bpt)(struct pt_regs *regs);
 int (*debugger_bpt)(struct pt_regs *regs);
 int (*debugger_sstep)(struct pt_regs *regs);
 int (*debugger_sstep)(struct pt_regs *regs);
 int (*debugger_iabr_match)(struct pt_regs *regs);
 int (*debugger_iabr_match)(struct pt_regs *regs);
@@ -159,7 +159,7 @@ void _exception(int signr, struct pt_regs *regs, int code, unsigned long addr)
  */
  */
 static inline int check_io_access(struct pt_regs *regs)
 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;
 	unsigned long msr = regs->msr;
 	const struct exception_table_entry *entry;
 	const struct exception_table_entry *entry;
 	unsigned int *nip = (unsigned int *)regs->nip;
 	unsigned int *nip = (unsigned int *)regs->nip;
@@ -178,7 +178,11 @@ static inline int check_io_access(struct pt_regs *regs)
 			nip -= 2;
 			nip -= 2;
 		else if (*nip == 0x4c00012c)	/* isync */
 		else if (*nip == 0x4c00012c)	/* isync */
 			--nip;
 			--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 */
 			/* sync or twi */
 			unsigned int rb;
 			unsigned int rb;
 
 

+ 7 - 6
arch/ppc/syslib/m8xx_wdt.c

@@ -14,6 +14,7 @@
 #include <linux/irq.h>
 #include <linux/irq.h>
 #include <linux/kernel.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/sched.h>
+#include <asm/io.h>
 #include <asm/8xx_immap.h>
 #include <asm/8xx_immap.h>
 #include <syslib/m8xx_wdt.h>
 #include <syslib/m8xx_wdt.h>
 
 
@@ -29,8 +30,8 @@ void m8xx_wdt_reset(void)
 {
 {
 	volatile immap_t *imap = (volatile immap_t *)IMAP_ADDR;
 	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)
 static irqreturn_t m8xx_wdt_interrupt(int irq, void *dev, struct pt_regs *regs)
@@ -39,7 +40,7 @@ static irqreturn_t m8xx_wdt_interrupt(int irq, void *dev, struct pt_regs *regs)
 
 
 	m8xx_wdt_reset();
 	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;
 	return IRQ_HANDLED;
 }
 }
@@ -51,7 +52,7 @@ void __init m8xx_wdt_handler_install(bd_t * binfo)
 	u32 sypcr;
 	u32 sypcr;
 	u32 pitrtclk;
 	u32 pitrtclk;
 
 
-	sypcr = in_be32(imap->im_siu_conf.sc_sypcr);
+	sypcr = in_be32(&imap->im_siu_conf.sc_sypcr);
 
 
 	if (!(sypcr & 0x04)) {
 	if (!(sypcr & 0x04)) {
 		printk(KERN_NOTICE "m8xx_wdt: wdt disabled (SYPCR: 0x%08X)\n",
 		printk(KERN_NOTICE "m8xx_wdt: wdt disabled (SYPCR: 0x%08X)\n",
@@ -87,9 +88,9 @@ void __init m8xx_wdt_handler_install(bd_t * binfo)
 	else
 	else
 		pitc = pitrtclk * wdt_timeout / binfo->bi_intfreq / 2;
 		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))
 	if (setup_irq(PIT_INTERRUPT, &m8xx_wdt_irqaction))
 		panic("m8xx_wdt: error setting up the watchdog irq!");
 		panic("m8xx_wdt: error setting up the watchdog irq!");

+ 3 - 1
arch/ppc/syslib/prom.c

@@ -1165,7 +1165,7 @@ get_property(struct device_node *np, const char *name, int *lenp)
 /*
 /*
  * Add a property to a node
  * Add a property to a node
  */
  */
-void
+int
 prom_add_property(struct device_node* np, struct property* prop)
 prom_add_property(struct device_node* np, struct property* prop)
 {
 {
 	struct property **next = &np->properties;
 	struct property **next = &np->properties;
@@ -1174,6 +1174,8 @@ prom_add_property(struct device_node* np, struct property* prop)
 	while (*next)
 	while (*next)
 		next = &(*next)->next;
 		next = &(*next)->next;
 	*next = prop;
 	*next = prop;
+
+	return 0;
 }
 }
 
 
 /* I quickly hacked that one, check against spec ! */
 /* I quickly hacked that one, check against spec ! */

+ 3 - 2
arch/ppc/xmon/xmon.c

@@ -220,8 +220,7 @@ static void get_tb(unsigned *p)
 	p[1] = lo;
 	p[1] = lo;
 }
 }
 
 
-void
-xmon(struct pt_regs *excp)
+int xmon(struct pt_regs *excp)
 {
 {
 	struct pt_regs regs;
 	struct pt_regs regs;
 	int msr, cmd;
 	int msr, cmd;
@@ -290,6 +289,8 @@ xmon(struct pt_regs *excp)
 #endif /* CONFIG_SMP */
 #endif /* CONFIG_SMP */
 	set_msr(msr);		/* restore interrupt enable */
 	set_msr(msr);		/* restore interrupt enable */
 	get_tb(start_tb[smp_processor_id()]);
 	get_tb(start_tb[smp_processor_id()]);
+
+	return cmd != 'X';
 }
 }
 
 
 irqreturn_t
 irqreturn_t

+ 11 - 0
arch/ppc64/Kconfig

@@ -56,6 +56,7 @@ config PPC_STD_MMU
 # max order + 1
 # max order + 1
 config FORCE_MAX_ZONEORDER
 config FORCE_MAX_ZONEORDER
 	int
 	int
+	default "9" if PPC_64K_PAGES
 	default "13"
 	default "13"
 
 
 source "init/Kconfig"
 source "init/Kconfig"
@@ -173,6 +174,16 @@ config KEXEC
 	  support.  As of this writing the exact hardware interface is
 	  support.  As of this writing the exact hardware interface is
 	  strongly in flux, so no good recommendation can be made.
 	  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
 config IBMVIO
 	depends on PPC_PSERIES || PPC_ISERIES
 	depends on PPC_PSERIES || PPC_ISERIES
 	bool
 	bool

+ 0 - 4
arch/ppc64/Kconfig.debug

@@ -55,10 +55,6 @@ config XMON_DEFAULT
 	  xmon is normally disabled unless booted with 'xmon=on'.
 	  xmon is normally disabled unless booted with 'xmon=on'.
 	  Use 'xmon=off' to disable xmon init during runtime.
 	  Use 'xmon=off' to disable xmon init during runtime.
 
 
-config PPCDBG
-	bool "Include PPCDBG realtime debugging"
-	depends on DEBUG_KERNEL
-
 config IRQSTACKS
 config IRQSTACKS
 	bool "Use separate kernel stacks when processing interrupts"
 	bool "Use separate kernel stacks when processing interrupts"
 	help
 	help

+ 1 - 0
arch/ppc64/kernel/idle.c

@@ -28,6 +28,7 @@
 #include <asm/time.h>
 #include <asm/time.h>
 #include <asm/systemcfg.h>
 #include <asm/systemcfg.h>
 #include <asm/machdep.h>
 #include <asm/machdep.h>
+#include <asm/smp.h>
 
 
 extern void power4_idle(void);
 extern void power4_idle(void);
 
 

+ 1 - 0
arch/ppc64/kernel/machine_kexec.c

@@ -24,6 +24,7 @@
 #include <asm/mmu.h>
 #include <asm/mmu.h>
 #include <asm/sections.h>	/* _end */
 #include <asm/sections.h>	/* _end */
 #include <asm/prom.h>
 #include <asm/prom.h>
+#include <asm/smp.h>
 
 
 #define HASH_GROUP_SIZE 0x80	/* size of each hash group, asm/mmu.h */
 #define HASH_GROUP_SIZE 0x80	/* size of each hash group, asm/mmu.h */
 
 

+ 71 - 1
arch/ppc64/kernel/misc.S

@@ -560,7 +560,7 @@ _GLOBAL(real_readb)
 	isync
 	isync
 	blr
 	blr
 
 
-	/*
+/*
  * Do an IO access in real mode
  * Do an IO access in real mode
  */
  */
 _GLOBAL(real_writeb)
 _GLOBAL(real_writeb)
@@ -592,6 +592,76 @@ _GLOBAL(real_writeb)
 	blr
 	blr
 #endif /* defined(CONFIG_PPC_PMAC) || defined(CONFIG_PPC_MAPLE) */
 #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
  * Create a kernel thread
  *   kernel_thread(fn, arg, flags)
  *   kernel_thread(fn, arg, flags)

+ 9 - 8
arch/ppc64/kernel/pci.c

@@ -295,8 +295,8 @@ static void pci_parse_of_addrs(struct device_node *node, struct pci_dev *dev)
 	}
 	}
 }
 }
 
 
-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;
 	struct pci_dev *dev;
 	const char *type;
 	const char *type;
@@ -354,10 +354,9 @@ static struct pci_dev *of_create_pci_dev(struct device_node *node,
 
 
 	return dev;
 	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 pci_bus *bus)
 {
 {
 	struct device_node *child = NULL;
 	struct device_node *child = NULL;
@@ -381,9 +380,10 @@ static void __devinit of_scan_bus(struct device_node *node,
 
 
 	do_bus_setup(bus);
 	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;
 	struct pci_bus *bus;
 	u32 *busrange, *ranges;
 	u32 *busrange, *ranges;
@@ -464,9 +464,10 @@ static void __devinit of_scan_pci_bridge(struct device_node *node,
 	else if (mode == PCI_PROBE_NORMAL)
 	else if (mode == PCI_PROBE_NORMAL)
 		pci_scan_child_bus(bus);
 		pci_scan_child_bus(bus);
 }
 }
+EXPORT_SYMBOL(of_scan_pci_bridge);
 #endif /* CONFIG_PPC_MULTIPLATFORM */
 #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 pci_bus *bus;
 	struct device_node *node = hose->arch_data;
 	struct device_node *node = hose->arch_data;

+ 20 - 5
arch/ppc64/kernel/prom.c

@@ -31,6 +31,7 @@
 #include <linux/initrd.h>
 #include <linux/initrd.h>
 #include <linux/bitops.h>
 #include <linux/bitops.h>
 #include <linux/module.h>
 #include <linux/module.h>
+#include <linux/module.h>
 
 
 #include <asm/prom.h>
 #include <asm/prom.h>
 #include <asm/rtas.h>
 #include <asm/rtas.h>
@@ -46,7 +47,6 @@
 #include <asm/pgtable.h>
 #include <asm/pgtable.h>
 #include <asm/pci.h>
 #include <asm/pci.h>
 #include <asm/iommu.h>
 #include <asm/iommu.h>
-#include <asm/ppcdebug.h>
 #include <asm/btext.h>
 #include <asm/btext.h>
 #include <asm/sections.h>
 #include <asm/sections.h>
 #include <asm/machdep.h>
 #include <asm/machdep.h>
@@ -1866,17 +1866,32 @@ get_property(struct device_node *np, const char *name, int *lenp)
 EXPORT_SYMBOL(get_property);
 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)
 prom_add_property(struct device_node* np, struct property* prop)
 {
 {
-	struct property **next = &np->properties;
+	struct property **next;
 
 
 	prop->next = NULL;	
 	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 = &(*next)->next;
+	}
 	*next = prop;
 	*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
 #if 0

+ 1 - 2
arch/ppc64/kernel/prom_init.c

@@ -44,7 +44,6 @@
 #include <asm/pgtable.h>
 #include <asm/pgtable.h>
 #include <asm/pci.h>
 #include <asm/pci.h>
 #include <asm/iommu.h>
 #include <asm/iommu.h>
-#include <asm/ppcdebug.h>
 #include <asm/btext.h>
 #include <asm/btext.h>
 #include <asm/sections.h>
 #include <asm/sections.h>
 #include <asm/machdep.h>
 #include <asm/machdep.h>
@@ -1825,7 +1824,7 @@ static void __init fixup_device_tree(void)
 	if (prom_getprop(u3, "device-rev", &u3_rev, sizeof(u3_rev))
 	if (prom_getprop(u3, "device-rev", &u3_rev, sizeof(u3_rev))
 	    == PROM_ERROR)
 	    == PROM_ERROR)
 		return;
 		return;
-	if (u3_rev != 0x35 && u3_rev != 0x37)
+	if (u3_rev < 0x35 || u3_rev > 0x39)
 		return;
 		return;
 	/* does it need fixup ? */
 	/* does it need fixup ? */
 	if (prom_getproplen(i2c, "interrupts") > 0)
 	if (prom_getproplen(i2c, "interrupts") > 0)

+ 1 - 5
arch/ppc64/kernel/rtas_pci.c

@@ -440,7 +440,6 @@ struct pci_controller * __devinit init_phb_dynamic(struct device_node *dn)
 	struct device_node *root = of_find_node_by_path("/");
 	struct device_node *root = of_find_node_by_path("/");
 	unsigned int root_size_cells = 0;
 	unsigned int root_size_cells = 0;
 	struct pci_controller *phb;
 	struct pci_controller *phb;
-	struct pci_bus *bus;
 	int primary;
 	int primary;
 
 
 	root_size_cells = prom_n_size_cells(root);
 	root_size_cells = prom_n_size_cells(root);
@@ -456,10 +455,7 @@ struct pci_controller * __devinit init_phb_dynamic(struct device_node *dn)
 	of_node_put(root);
 	of_node_put(root);
 
 
 	pci_devs_phb_init_dynamic(phb);
 	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;
 	return phb;
 }
 }

+ 0 - 55
arch/ppc64/kernel/udbg.c

@@ -10,12 +10,10 @@
  */
  */
 
 
 #include <stdarg.h>
 #include <stdarg.h>
-#define WANT_PPCDBG_TAB /* Only defined here */
 #include <linux/config.h>
 #include <linux/config.h>
 #include <linux/types.h>
 #include <linux/types.h>
 #include <linux/sched.h>
 #include <linux/sched.h>
 #include <linux/console.h>
 #include <linux/console.h>
-#include <asm/ppcdebug.h>
 #include <asm/processor.h>
 #include <asm/processor.h>
 
 
 void (*udbg_putc)(unsigned char c);
 void (*udbg_putc)(unsigned char c);
@@ -89,59 +87,6 @@ void udbg_printf(const char *fmt, ...)
 	va_end(args);
 	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
  * Early boot console based on udbg
  */
  */

+ 10 - 10
drivers/block/swim3.c

@@ -28,6 +28,7 @@
 #include <linux/devfs_fs_kernel.h>
 #include <linux/devfs_fs_kernel.h>
 #include <linux/interrupt.h>
 #include <linux/interrupt.h>
 #include <linux/module.h>
 #include <linux/module.h>
+#include <linux/spinlock.h>
 #include <asm/io.h>
 #include <asm/io.h>
 #include <asm/dbdma.h>
 #include <asm/dbdma.h>
 #include <asm/prom.h>
 #include <asm/prom.h>
@@ -176,6 +177,7 @@ struct swim3 {
 
 
 struct floppy_state {
 struct floppy_state {
 	enum swim_state	state;
 	enum swim_state	state;
+	spinlock_t lock;
 	struct swim3 __iomem *swim3;	/* hardware registers */
 	struct swim3 __iomem *swim3;	/* hardware registers */
 	struct dbdma_regs __iomem *dma;	/* DMA controller registers */
 	struct dbdma_regs __iomem *dma;	/* DMA controller registers */
 	int	swim3_intr;	/* interrupt number for SWIM3 */
 	int	swim3_intr;	/* interrupt number for SWIM3 */
@@ -304,7 +306,6 @@ static void do_fd_request(request_queue_t * q)
 #endif /* CONFIG_PMAC_MEDIABAY */
 #endif /* CONFIG_PMAC_MEDIABAY */
 		start_request(&floppy_states[i]);
 		start_request(&floppy_states[i]);
 	}
 	}
-	sti();
 }
 }
 
 
 static void start_request(struct floppy_state *fs)
 static void start_request(struct floppy_state *fs)
@@ -370,7 +371,7 @@ static void set_timeout(struct floppy_state *fs, int nticks,
 {
 {
 	unsigned long flags;
 	unsigned long flags;
 
 
-	save_flags(flags); cli();
+	spin_lock_irqsave(&fs->lock, flags);
 	if (fs->timeout_pending)
 	if (fs->timeout_pending)
 		del_timer(&fs->timeout);
 		del_timer(&fs->timeout);
 	fs->timeout.expires = jiffies + nticks;
 	fs->timeout.expires = jiffies + nticks;
@@ -378,7 +379,7 @@ static void set_timeout(struct floppy_state *fs, int nticks,
 	fs->timeout.data = (unsigned long) fs;
 	fs->timeout.data = (unsigned long) fs;
 	add_timer(&fs->timeout);
 	add_timer(&fs->timeout);
 	fs->timeout_pending = 1;
 	fs->timeout_pending = 1;
-	restore_flags(flags);
+	spin_unlock_irqrestore(&fs->lock, flags);
 }
 }
 
 
 static inline void scan_track(struct floppy_state *fs)
 static inline void scan_track(struct floppy_state *fs)
@@ -790,14 +791,13 @@ static int grab_drive(struct floppy_state *fs, enum swim_state state,
 {
 {
 	unsigned long flags;
 	unsigned long flags;
 
 
-	save_flags(flags);
-	cli();
+	spin_lock_irqsave(&fs->lock, flags);
 	if (fs->state != idle) {
 	if (fs->state != idle) {
 		++fs->wanted;
 		++fs->wanted;
 		while (fs->state != available) {
 		while (fs->state != available) {
 			if (interruptible && signal_pending(current)) {
 			if (interruptible && signal_pending(current)) {
 				--fs->wanted;
 				--fs->wanted;
-				restore_flags(flags);
+				spin_unlock_irqrestore(&fs->lock, flags);
 				return -EINTR;
 				return -EINTR;
 			}
 			}
 			interruptible_sleep_on(&fs->wait);
 			interruptible_sleep_on(&fs->wait);
@@ -805,7 +805,7 @@ static int grab_drive(struct floppy_state *fs, enum swim_state state,
 		--fs->wanted;
 		--fs->wanted;
 	}
 	}
 	fs->state = state;
 	fs->state = state;
-	restore_flags(flags);
+	spin_unlock_irqrestore(&fs->lock, flags);
 	return 0;
 	return 0;
 }
 }
 
 
@@ -813,11 +813,10 @@ static void release_drive(struct floppy_state *fs)
 {
 {
 	unsigned long flags;
 	unsigned long flags;
 
 
-	save_flags(flags);
-	cli();
+	spin_lock_irqsave(&fs->lock, flags);
 	fs->state = idle;
 	fs->state = idle;
 	start_request(fs);
 	start_request(fs);
-	restore_flags(flags);
+	spin_unlock_irqrestore(&fs->lock, flags);
 }
 }
 
 
 static int fd_eject(struct floppy_state *fs)
 static int fd_eject(struct floppy_state *fs)
@@ -1109,6 +1108,7 @@ static int swim3_add_device(struct device_node *swim)
 		pmac_call_feature(PMAC_FTR_SWIM3_ENABLE, swim, 0, 1);
 		pmac_call_feature(PMAC_FTR_SWIM3_ENABLE, swim, 0, 1);
 	
 	
 	memset(fs, 0, sizeof(*fs));
 	memset(fs, 0, sizeof(*fs));
+	spin_lock_init(&fs->lock);
 	fs->state = idle;
 	fs->state = idle;
 	fs->swim3 = (struct swim3 __iomem *)
 	fs->swim3 = (struct swim3 __iomem *)
 		ioremap(swim->addrs[0].address, 0x200);
 		ioremap(swim->addrs[0].address, 0x200);

+ 6 - 3
drivers/ide/ppc/pmac.c

@@ -497,16 +497,19 @@ pmu_hd_blink_init(void)
 	if (pmu_get_model() != PMU_KEYLARGO_BASED)
 	if (pmu_get_model() != PMU_KEYLARGO_BASED)
 		return 0;
 		return 0;
 	
 	
-	dt = find_devices("device-tree");
+	dt = of_find_node_by_path("/");
 	if (dt == NULL)
 	if (dt == NULL)
 		return 0;
 		return 0;
 	model = (const char *)get_property(dt, "model", NULL);
 	model = (const char *)get_property(dt, "model", NULL);
 	if (model == NULL)
 	if (model == NULL)
 		return 0;
 		return 0;
 	if (strncmp(model, "PowerBook", strlen("PowerBook")) != 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;
 	    	return 0;
-	
+	}
+	of_node_put(dt);
+
 	pmu_blink_on.complete = 1;
 	pmu_blink_on.complete = 1;
 	pmu_blink_off.complete = 1;
 	pmu_blink_off.complete = 1;
 	spin_lock_init(&pmu_blink_lock);
 	spin_lock_init(&pmu_blink_lock);

+ 19 - 0
drivers/macintosh/Kconfig

@@ -169,6 +169,25 @@ config THERM_PM72
 	  This driver provides thermostat and fan control for the desktop
 	  This driver provides thermostat and fan control for the desktop
 	  G5 machines. 
 	  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
 config ANSLCD
 	tristate "Support for ANS LCD display"
 	tristate "Support for ANS LCD display"
 	depends on ADB_CUDA && PPC_PMAC
 	depends on ADB_CUDA && PPC_PMAC

+ 9 - 0
drivers/macintosh/Makefile

@@ -26,3 +26,12 @@ obj-$(CONFIG_ADB_MACIO)		+= macio-adb.o
 obj-$(CONFIG_THERM_PM72)	+= therm_pm72.o
 obj-$(CONFIG_THERM_PM72)	+= therm_pm72.o
 obj-$(CONFIG_THERM_WINDTUNNEL)	+= therm_windtunnel.o
 obj-$(CONFIG_THERM_WINDTUNNEL)	+= therm_windtunnel.o
 obj-$(CONFIG_THERM_ADT746X)	+= therm_adt746x.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

+ 168 - 6
drivers/macintosh/smu.c

@@ -47,13 +47,13 @@
 #include <asm/uaccess.h>
 #include <asm/uaccess.h>
 #include <asm/of_device.h>
 #include <asm/of_device.h>
 
 
-#define VERSION "0.6"
+#define VERSION "0.7"
 #define AUTHOR  "(c) 2005 Benjamin Herrenschmidt, IBM Corp."
 #define AUTHOR  "(c) 2005 Benjamin Herrenschmidt, IBM Corp."
 
 
 #undef DEBUG_SMU
 #undef DEBUG_SMU
 
 
 #ifdef 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
 #else
 #define DPRINTK(fmt, args...) do { } while (0)
 #define DPRINTK(fmt, args...) do { } while (0)
 #endif
 #endif
@@ -92,7 +92,7 @@ struct smu_device {
  * for now, just hard code that
  * for now, just hard code that
  */
  */
 static struct smu_device	*smu;
 static struct smu_device	*smu;
-
+static DECLARE_MUTEX(smu_part_access);
 
 
 /*
 /*
  * SMU driver low level stuff
  * SMU driver low level stuff
@@ -113,9 +113,11 @@ static void smu_start_cmd(void)
 
 
 	DPRINTK("SMU: starting cmd %x, %d bytes data\n", cmd->cmd,
 	DPRINTK("SMU: starting cmd %x, %d bytes data\n", cmd->cmd,
 		cmd->data_len);
 		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)[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 */
 	/* Fill the SMU command buffer */
 	smu->cmd_buf->cmd = cmd->cmd;
 	smu->cmd_buf->cmd = cmd->cmd;
@@ -440,7 +442,7 @@ int smu_present(void)
 EXPORT_SYMBOL(smu_present);
 EXPORT_SYMBOL(smu_present);
 
 
 
 
-int smu_init (void)
+int __init smu_init (void)
 {
 {
 	struct device_node *np;
 	struct device_node *np;
 	u32 *data;
 	u32 *data;
@@ -588,6 +590,8 @@ static void smu_expose_childs(void *unused)
 			sprintf(name, "smu-i2c-%02x", *reg);
 			sprintf(name, "smu-i2c-%02x", *reg);
 			of_platform_device_create(np, name, &smu->of_dev->dev);
 			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 @@ int smu_queue_i2c(struct smu_i2c_cmd *cmd)
 	return 0;
 	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 @@ static ssize_t smu_write(struct file *file, const char __user *buf,
 	else if (hdr.cmdtype == SMU_CMDTYPE_WANTS_EVENTS) {
 	else if (hdr.cmdtype == SMU_CMDTYPE_WANTS_EVENTS) {
 		pp->mode = smu_file_events;
 		pp->mode = smu_file_events;
 		return 0;
 		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)
 	} else if (hdr.cmdtype != SMU_CMDTYPE_SMU)
 		return -EINVAL;
 		return -EINVAL;
 	else if (pp->mode != smu_file_commands)
 	else if (pp->mode != smu_file_commands)

+ 5 - 5
drivers/macintosh/via-pmu.c

@@ -2053,6 +2053,7 @@ pmu_register_sleep_notifier(struct pmu_sleep_notifier *n)
 	__list_add(&n->list, list->prev, list);
 	__list_add(&n->list, list->prev, list);
 	return 0;
 	return 0;
 }
 }
+EXPORT_SYMBOL(pmu_register_sleep_notifier);
 
 
 int
 int
 pmu_unregister_sleep_notifier(struct pmu_sleep_notifier* n)
 pmu_unregister_sleep_notifier(struct pmu_sleep_notifier* n)
@@ -2063,6 +2064,7 @@ pmu_unregister_sleep_notifier(struct pmu_sleep_notifier* n)
 	n->list.next = NULL;
 	n->list.next = NULL;
 	return 0;
 	return 0;
 }
 }
+EXPORT_SYMBOL(pmu_unregister_sleep_notifier);
 #endif /* CONFIG_PM */
 #endif /* CONFIG_PM */
 
 
 #if defined(CONFIG_PM) && defined(CONFIG_PPC32)
 #if defined(CONFIG_PM) && defined(CONFIG_PPC32)
@@ -2667,10 +2669,10 @@ powerbook_sleep_3400(void)
 	asleep = 1;
 	asleep = 1;
 
 
 	/* Put the CPU into sleep mode */
 	/* 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;
 	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);
 	udelay(10);
 
 
 	/* OK, we're awake again, start restoring things */
 	/* OK, we're awake again, start restoring things */
@@ -3139,8 +3141,6 @@ EXPORT_SYMBOL(pmu_i2c_stdsub_write);
 EXPORT_SYMBOL(pmu_i2c_simple_read);
 EXPORT_SYMBOL(pmu_i2c_simple_read);
 EXPORT_SYMBOL(pmu_i2c_simple_write);
 EXPORT_SYMBOL(pmu_i2c_simple_write);
 #if defined(CONFIG_PM) && defined(CONFIG_PPC32)
 #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_enable_irled);
 EXPORT_SYMBOL(pmu_battery_count);
 EXPORT_SYMBOL(pmu_battery_count);
 EXPORT_SYMBOL(pmu_batteries);
 EXPORT_SYMBOL(pmu_batteries);

+ 131 - 0
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__ */

+ 426 - 0
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");
+

+ 105 - 0
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");
+

+ 263 - 0
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");
+

+ 145 - 0
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);

+ 84 - 0
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);

+ 879 - 0
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");
+

+ 814 - 0
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");
+

+ 282 - 0
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");
+

+ 479 - 0
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");
+

+ 35 - 22
fs/proc/proc_devtree.c

@@ -48,6 +48,39 @@ static int property_read_proc(char *page, char **start, off_t off,
  * and "@10" to it.
  * and "@10" to it.
  */
  */
 
 
+/*
+ * 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.
  * Process a node, adding entries for its children and its properties.
  */
  */
@@ -57,11 +90,9 @@ void proc_device_tree_add_node(struct device_node *np,
 	struct property *pp;
 	struct property *pp;
 	struct proc_dir_entry *ent;
 	struct proc_dir_entry *ent;
 	struct device_node *child;
 	struct device_node *child;
-	struct proc_dir_entry *list = NULL, **lastp;
 	const char *p;
 	const char *p;
 
 
 	set_node_proc_entry(np, de);
 	set_node_proc_entry(np, de);
-	lastp = &list;
 	for (child = NULL; (child = of_get_next_child(np, child));) {
 	for (child = NULL; (child = of_get_next_child(np, child));) {
 		p = strrchr(child->full_name, '/');
 		p = strrchr(child->full_name, '/');
 		if (!p)
 		if (!p)
@@ -71,9 +102,6 @@ void proc_device_tree_add_node(struct device_node *np,
 		ent = proc_mkdir(p, de);
 		ent = proc_mkdir(p, de);
 		if (ent == 0)
 		if (ent == 0)
 			break;
 			break;
-		*lastp = ent;
-		ent->next = NULL;
-		lastp = &ent->next;
 		proc_device_tree_add_node(child, ent);
 		proc_device_tree_add_node(child, ent);
 	}
 	}
 	of_node_put(child);
 	of_node_put(child);
@@ -84,7 +112,7 @@ void proc_device_tree_add_node(struct device_node *np,
 		 * properties are quite unimportant for us though, thus we
 		 * properties are quite unimportant for us though, thus we
 		 * simply "skip" them here, but we do have to check.
 		 * 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))
 			if (!strcmp(ent->name, pp->name))
 				break;
 				break;
 		if (ent != NULL) {
 		if (ent != NULL) {
@@ -94,25 +122,10 @@ void proc_device_tree_add_node(struct device_node *np,
 			continue;
 			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)
 		if (ent == 0)
 			break;
 			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;
 }
 }
 
 
 /*
 /*

+ 17 - 12
include/asm-ppc/ide.h → 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__
 #ifdef __KERNEL__
 
 
+#ifndef __powerpc64__
 #include <linux/sched.h>
 #include <linux/sched.h>
 #include <asm/mpc8xx.h>
 #include <asm/mpc8xx.h>
+#endif
 
 
 #ifndef MAX_HWIFS
 #ifndef MAX_HWIFS
+#ifdef __powerpc64__
+#define MAX_HWIFS	10
+#else
 #define MAX_HWIFS	8
 #define MAX_HWIFS	8
 #endif
 #endif
+#endif
 
 
+#ifndef  __powerpc64__
 #include <linux/config.h>
 #include <linux/config.h>
 #include <linux/hdreg.h>
 #include <linux/hdreg.h>
 #include <linux/ioport.h>
 #include <linux/ioport.h>
@@ -59,9 +62,6 @@ static __inline__ unsigned long ide_default_io_base(int index)
 	return 0;
 	return 0;
 }
 }
 
 
-#define IDE_ARCH_OBSOLETE_INIT
-#define ide_default_io_ctl(base)	((base) + 0x206) /* obsolete */
-
 #ifdef CONFIG_PCI
 #ifdef CONFIG_PCI
 #define ide_init_default_irq(base)	(0)
 #define ide_init_default_irq(base)	(0)
 #else
 #else
@@ -73,6 +73,11 @@ static __inline__ unsigned long ide_default_io_base(int index)
 #define ide_ack_intr(hwif) (hwif->hw.ack_intr ? hwif->hw.ack_intr(hwif) : 1)
 #define ide_ack_intr(hwif) (hwif->hw.ack_intr ? hwif->hw.ack_intr(hwif) : 1)
 #endif
 #endif
 
 
+#endif /* __powerpc64__ */
+
+#define IDE_ARCH_OBSOLETE_INIT
+#define ide_default_io_ctl(base)	((base) + 0x206) /* obsolete */
+
 #endif /* __KERNEL__ */
 #endif /* __KERNEL__ */
 
 
-#endif /* __ASMPPC_IDE_H */
+#endif /* _ASM_POWERPC_IDE_H */

+ 3 - 1
include/asm-powerpc/machdep.h

@@ -82,7 +82,6 @@ struct machdep_calls {
 	void		(*iommu_dev_setup)(struct pci_dev *dev);
 	void		(*iommu_dev_setup)(struct pci_dev *dev);
 	void		(*iommu_bus_setup)(struct pci_bus *bus);
 	void		(*iommu_bus_setup)(struct pci_bus *bus);
 	void		(*irq_bus_setup)(struct pci_bus *bus);
 	void		(*irq_bus_setup)(struct pci_bus *bus);
-	int		(*set_dabr)(unsigned long dabr);
 #endif
 #endif
 
 
 	int		(*probe)(int platform);
 	int		(*probe)(int platform);
@@ -158,6 +157,9 @@ struct machdep_calls {
 	   platform, called once per cpu. */
 	   platform, called once per cpu. */
 	void		(*enable_pmcs)(void);
 	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 */
 #ifdef CONFIG_PPC32	/* XXX for now */
 	/* A general init function, called by ppc_init in init/main.c.
 	/* A general init function, called by ppc_init in init/main.c.
 	   May be NULL. */
 	   May be NULL. */

+ 1 - 0
include/asm-powerpc/ppc-pci.h

@@ -34,6 +34,7 @@ void *traverse_pci_devices(struct device_node *start, traverse_func pre,
 
 
 void pci_devs_phb_init(void);
 void pci_devs_phb_init(void);
 void pci_devs_phb_init_dynamic(struct pci_controller *phb);
 void pci_devs_phb_init_dynamic(struct pci_controller *phb);
+void __devinit scan_phb(struct pci_controller *hose);
 
 
 /* PCI address cache management routines */
 /* PCI address cache management routines */
 void pci_addr_cache_insert_device(struct pci_dev *dev);
 void pci_addr_cache_insert_device(struct pci_dev *dev);

+ 1 - 1
include/asm-powerpc/prom.h

@@ -203,7 +203,7 @@ extern int prom_n_addr_cells(struct device_node* np);
 extern int prom_n_size_cells(struct device_node* np);
 extern int prom_n_size_cells(struct device_node* np);
 extern int prom_n_intr_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_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
 #ifdef CONFIG_PPC32
 /*
 /*

+ 8 - 1
include/asm-powerpc/reg.h

@@ -396,6 +396,9 @@
 #define SPRN_VRSAVE	0x100	/* Vector Register Save Register */
 #define SPRN_VRSAVE	0x100	/* Vector Register Save Register */
 #define SPRN_XER	0x001	/* Fixed Point Exception 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 */
 /* Performance monitor SPRs */
 #ifdef CONFIG_PPC64
 #ifdef CONFIG_PPC64
 #define SPRN_MMCR0	795
 #define SPRN_MMCR0	795
@@ -594,7 +597,11 @@ static inline void ppc64_runlatch_off(void)
 		mtspr(SPRN_CTRLT, ctrl);
 		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; \
 #define __get_SP()	({unsigned long sp; \
 			asm volatile("mr %0,1": "=r" (sp)); sp;})
 			asm volatile("mr %0,1": "=r" (sp)); sp;})

+ 3 - 1
include/asm-powerpc/smp.h

@@ -86,7 +86,6 @@ extern void __cpu_die(unsigned int cpu);
 #else
 #else
 /* for UP */
 /* for UP */
 #define smp_setup_cpu_maps()
 #define smp_setup_cpu_maps()
-#define smp_release_cpus()
 
 
 #endif /* CONFIG_SMP */
 #endif /* CONFIG_SMP */
 
 
@@ -94,6 +93,9 @@ extern void __cpu_die(unsigned int cpu);
 #define get_hard_smp_processor_id(CPU) (paca[(CPU)].hw_cpu_id)
 #define get_hard_smp_processor_id(CPU) (paca[(CPU)].hw_cpu_id)
 #define set_hard_smp_processor_id(CPU, VAL) \
 #define set_hard_smp_processor_id(CPU, VAL) \
 	do { (paca[(CPU)].hw_cpu_id = (VAL)); } while (0)
 	do { (paca[(CPU)].hw_cpu_id = (VAL)); } while (0)
+
+extern void smp_release_cpus(void);
+
 #else
 #else
 /* 32-bit */
 /* 32-bit */
 #ifndef CONFIG_SMP
 #ifndef CONFIG_SMP

+ 195 - 4
include/asm-powerpc/smu.h

@@ -20,16 +20,52 @@
 /*
 /*
  * Partition info commands
  * 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_COMMAND		0x3e
+#define   SMU_CMD_PARTITION_LATEST		0x01
+#define   SMU_CMD_PARTITION_BASE		0x02
+#define   SMU_CMD_PARTITION_UPDATE		0x03
 
 
 
 
 /*
 /*
  * Fan control
  * 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
 #define SMU_CMD_FAN_COMMAND			0x4a
 
 
@@ -144,7 +180,11 @@
  *  - lenght 8 ("VSLEWxyz") has 3 additional bytes appended, and is
  *  - lenght 8 ("VSLEWxyz") has 3 additional bytes appended, and is
  *    used to set the voltage slewing point. The SMU replies with "DONE"
  *    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
  * 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
 #define SMU_CMD_POWER_COMMAND			0xaa
@@ -152,6 +192,14 @@
 #define   SMU_CMD_POWER_SHUTDOWN		"SHUTDOWN"
 #define   SMU_CMD_POWER_SHUTDOWN		"SHUTDOWN"
 #define   SMU_CMD_POWER_VOLTAGE_SLEW		"VSLEW"
 #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
 /* Misc commands
  *
  *
  * This command seem to be a grab bag of various things
  * This command seem to be a grab bag of various things
@@ -172,6 +220,25 @@
  * Misc commands
  * Misc commands
  *
  *
  * This command seem to be a grab bag of various things
  * 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_COMMAND			0xee
 #define   SMU_CMD_MISC_ee_GET_DATABLOCK_REC	0x02
 #define   SMU_CMD_MISC_ee_GET_DATABLOCK_REC	0x02
@@ -333,6 +400,128 @@ extern int smu_queue_i2c(struct smu_i2c_cmd *cmd);
 
 
 #endif /* __KERNEL__ */
 #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 -
  * - Userland interface -
  */
  */
@@ -365,8 +554,10 @@ struct smu_user_cmd_hdr
 	__u32		cmdtype;
 	__u32		cmdtype;
 #define SMU_CMDTYPE_SMU			0	/* SMU command */
 #define SMU_CMDTYPE_SMU			0	/* SMU command */
 #define SMU_CMDTYPE_WANTS_EVENTS	1	/* switch fd to events mode */
 #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		cmd;			/* SMU command byte */
+	__u8		pad[3];			/* padding */
 	__u32		data_len;		/* Lenght of data following */
 	__u32		data_len;		/* Lenght of data following */
 };
 };
 
 

+ 1 - 0
include/asm-powerpc/xmon.h

@@ -7,6 +7,7 @@ struct pt_regs;
 extern int xmon(struct pt_regs *excp);
 extern int xmon(struct pt_regs *excp);
 extern void xmon_printf(const char *fmt, ...);
 extern void xmon_printf(const char *fmt, ...);
 extern void xmon_init(int);
 extern void xmon_init(int);
+extern void xmon_map_scc(void);
 
 
 #endif
 #endif
 #endif
 #endif

+ 11 - 11
include/asm-ppc/btext.h

@@ -17,18 +17,18 @@ extern unsigned long disp_BAT[2];
 extern boot_infos_t disp_bi;
 extern boot_infos_t disp_bi;
 extern int boot_text_mapped;
 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 /* __KERNEL__ */
 #endif /* __PPC_BTEXT_H */
 #endif /* __PPC_BTEXT_H */

+ 7 - 5
include/asm-ppc/io.h

@@ -237,9 +237,9 @@ static inline void __raw_writel(__u32 b, volatile void __iomem *addr)
 #define outsl(port, buf, nl)	_outsl_ns((port)+___IO_BASE, (buf), (nl))
 #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
  * well-defined which instruction SRR0 will point to when the
  * exception occurs.
  * exception occurs.
  * With the sequence below (twi; isync; nop), we have found that
  * With the sequence below (twi; isync; nop), we have found that
@@ -258,7 +258,7 @@ extern __inline__ unsigned int name(unsigned int port)	\
 {							\
 {							\
 	unsigned int x;					\
 	unsigned int x;					\
 	__asm__ __volatile__(				\
 	__asm__ __volatile__(				\
-			op "	%0,0,%1\n"		\
+		"0:"	op "	%0,0,%1\n"		\
 		"1:	twi	0,%0,0\n"		\
 		"1:	twi	0,%0,0\n"		\
 		"2:	isync\n"			\
 		"2:	isync\n"			\
 		"3:	nop\n"				\
 		"3:	nop\n"				\
@@ -269,6 +269,7 @@ extern __inline__ unsigned int name(unsigned int port)	\
 		".previous\n"				\
 		".previous\n"				\
 		".section __ex_table,\"a\"\n"		\
 		".section __ex_table,\"a\"\n"		\
 		"	.align	2\n"			\
 		"	.align	2\n"			\
+		"	.long	0b,5b\n"		\
 		"	.long	1b,5b\n"		\
 		"	.long	1b,5b\n"		\
 		"	.long	2b,5b\n"		\
 		"	.long	2b,5b\n"		\
 		"	.long	3b,5b\n"		\
 		"	.long	3b,5b\n"		\
@@ -282,11 +283,12 @@ extern __inline__ unsigned int name(unsigned int port)	\
 extern __inline__ void name(unsigned int val, unsigned int port) \
 extern __inline__ void name(unsigned int val, unsigned int port) \
 {							\
 {							\
 	__asm__ __volatile__(				\
 	__asm__ __volatile__(				\
-		op " %0,0,%1\n"				\
+		"0:" op " %0,0,%1\n"			\
 		"1:	sync\n"				\
 		"1:	sync\n"				\
 		"2:\n"					\
 		"2:\n"					\
 		".section __ex_table,\"a\"\n"		\
 		".section __ex_table,\"a\"\n"		\
 		"	.align	2\n"			\
 		"	.align	2\n"			\
+		"	.long	0b,2b\n"		\
 		"	.long	1b,2b\n"		\
 		"	.long	1b,2b\n"		\
 		".previous"				\
 		".previous"				\
 		: : "r" (val), "r" (port + ___IO_BASE));	\
 		: : "r" (val), "r" (port + ___IO_BASE));	\

+ 1 - 1
include/asm-ppc/kgdb.h

@@ -31,7 +31,7 @@ extern void breakpoint(void);
 /* For taking exceptions
 /* For taking exceptions
  * these are defined in traps.c
  * 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_bpt)(struct pt_regs *regs);
 extern int (*debugger_sstep)(struct pt_regs *regs);
 extern int (*debugger_sstep)(struct pt_regs *regs);
 extern int (*debugger_iabr_match)(struct pt_regs *regs);
 extern int (*debugger_iabr_match)(struct pt_regs *regs);

+ 1 - 1
include/asm-ppc/prom.h

@@ -93,7 +93,7 @@ extern int device_is_compatible(struct device_node *device, const char *);
 extern int machine_is_compatible(const char *compat);
 extern int machine_is_compatible(const char *compat);
 extern unsigned char *get_property(struct device_node *node, const char *name,
 extern unsigned char *get_property(struct device_node *node, const char *name,
 				   int *lenp);
 				   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 void prom_get_irq_senses(unsigned char *, int, int);
 extern int prom_n_addr_cells(struct device_node* np);
 extern int prom_n_addr_cells(struct device_node* np);
 extern int prom_n_size_cells(struct device_node* np);
 extern int prom_n_size_cells(struct device_node* np);

+ 0 - 30
include/asm-ppc64/ide.h

@@ -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 */

+ 8 - 0
include/asm-ppc64/pci.h

@@ -162,6 +162,14 @@ pcibios_fixup_device_resources(struct pci_dev *dev, struct pci_bus *bus);
 
 
 extern struct pci_controller *init_phb_dynamic(struct device_node *dn);
 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 int pci_read_irq_line(struct pci_dev *dev);
 
 
 extern void pcibios_add_platform_entries(struct pci_dev *dev);
 extern void pcibios_add_platform_entries(struct pci_dev *dev);

+ 0 - 108
include/asm-ppc64/ppcdebug.h

@@ -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 */

+ 1 - 1
include/asm-ppc64/prom.h

@@ -213,6 +213,6 @@ extern int prom_n_addr_cells(struct device_node* np);
 extern int prom_n_size_cells(struct device_node* np);
 extern int prom_n_size_cells(struct device_node* np);
 extern int prom_n_intr_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_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 */
 #endif /* _PPC64_PROM_H */

+ 0 - 3
include/asm-ppc64/udbg.h

@@ -23,9 +23,6 @@ extern int udbg_read(char *buf, int buflen);
 
 
 extern void register_early_udbg_console(void);
 extern void register_early_udbg_console(void);
 extern void udbg_printf(const char *fmt, ...);
 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);
 extern void udbg_init_uart(void __iomem *comport, unsigned int speed);
 
 

+ 3 - 6
include/linux/proc_fs.h

@@ -139,15 +139,12 @@ extern void proc_tty_unregister_driver(struct tty_driver *driver);
 /*
 /*
  * proc_devtree.c
  * proc_devtree.c
  */
  */
+#ifdef CONFIG_PROC_DEVICETREE
 struct device_node;
 struct device_node;
+struct property;
 extern void proc_device_tree_init(void);
 extern void proc_device_tree_init(void);
-#ifdef CONFIG_PROC_DEVICETREE
 extern void proc_device_tree_add_node(struct device_node *, struct proc_dir_entry *);
 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 */
 #endif /* CONFIG_PROC_DEVICETREE */
 
 
 extern struct proc_dir_entry *proc_symlink(const char *,
 extern struct proc_dir_entry *proc_symlink(const char *,