mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2025-01-07 13:43:51 +00:00
Merge git://git.kernel.org/pub/scm/linux/kernel/git/paulus/powerpc-merge
This commit is contained in:
commit
f093182d31
@ -404,6 +404,14 @@ config CPU_FREQ_PMAC
|
||||
this currently includes some models of iBook & Titanium
|
||||
PowerBook.
|
||||
|
||||
config CPU_FREQ_PMAC64
|
||||
bool "Support for some Apple G5s"
|
||||
depends on CPU_FREQ && PMAC_SMU && PPC64
|
||||
select CPU_FREQ_TABLE
|
||||
help
|
||||
This adds support for frequency switching on Apple iMac G5,
|
||||
and some of the more recent desktop G5 machines as well.
|
||||
|
||||
config PPC601_SYNC_FIX
|
||||
bool "Workarounds for PPC601 bugs"
|
||||
depends on 6xx && (PPC_PREP || PPC_PMAC)
|
||||
@ -484,6 +492,7 @@ source "fs/Kconfig.binfmt"
|
||||
config FORCE_MAX_ZONEORDER
|
||||
int
|
||||
depends on PPC64
|
||||
default "9" if PPC_64K_PAGES
|
||||
default "13"
|
||||
|
||||
config MATH_EMULATION
|
||||
|
@ -1,18 +1,32 @@
|
||||
#
|
||||
# Automatically generated make config: don't edit
|
||||
# Linux kernel version: 2.6.14-rc4
|
||||
# Thu Oct 20 08:30:23 2005
|
||||
# Linux kernel version: 2.6.14
|
||||
# Mon Nov 7 13:37:59 2005
|
||||
#
|
||||
CONFIG_PPC64=y
|
||||
CONFIG_64BIT=y
|
||||
CONFIG_PPC_MERGE=y
|
||||
CONFIG_MMU=y
|
||||
CONFIG_GENERIC_HARDIRQS=y
|
||||
CONFIG_RWSEM_XCHGADD_ALGORITHM=y
|
||||
CONFIG_GENERIC_CALIBRATE_DELAY=y
|
||||
CONFIG_GENERIC_ISA_DMA=y
|
||||
CONFIG_PPC=y
|
||||
CONFIG_EARLY_PRINTK=y
|
||||
CONFIG_COMPAT=y
|
||||
CONFIG_SYSVIPC_COMPAT=y
|
||||
CONFIG_SCHED_NO_NO_OMIT_FRAME_POINTER=y
|
||||
CONFIG_ARCH_MAY_HAVE_PC_FDC=y
|
||||
CONFIG_FORCE_MAX_ZONEORDER=13
|
||||
|
||||
#
|
||||
# Processor support
|
||||
#
|
||||
CONFIG_POWER4_ONLY=y
|
||||
CONFIG_POWER4=y
|
||||
CONFIG_PPC_FPU=y
|
||||
CONFIG_ALTIVEC=y
|
||||
CONFIG_PPC_STD_MMU=y
|
||||
CONFIG_SMP=y
|
||||
CONFIG_NR_CPUS=2
|
||||
|
||||
#
|
||||
# Code maturity level options
|
||||
@ -67,30 +81,60 @@ CONFIG_MODVERSIONS=y
|
||||
CONFIG_MODULE_SRCVERSION_ALL=y
|
||||
CONFIG_KMOD=y
|
||||
CONFIG_STOP_MACHINE=y
|
||||
CONFIG_SYSVIPC_COMPAT=y
|
||||
|
||||
#
|
||||
# Platform support
|
||||
#
|
||||
# CONFIG_PPC_ISERIES is not set
|
||||
CONFIG_PPC_MULTIPLATFORM=y
|
||||
# CONFIG_PPC_ISERIES is not set
|
||||
# CONFIG_EMBEDDED6xx is not set
|
||||
# CONFIG_APUS is not set
|
||||
# CONFIG_PPC_PSERIES is not set
|
||||
# CONFIG_PPC_BPA is not set
|
||||
CONFIG_PPC_PMAC=y
|
||||
# CONFIG_PPC_MAPLE is not set
|
||||
CONFIG_PPC=y
|
||||
CONFIG_PPC64=y
|
||||
CONFIG_PPC_OF=y
|
||||
CONFIG_MPIC=y
|
||||
CONFIG_ALTIVEC=y
|
||||
CONFIG_KEXEC=y
|
||||
CONFIG_U3_DART=y
|
||||
CONFIG_PPC_PMAC64=y
|
||||
CONFIG_BOOTX_TEXT=y
|
||||
CONFIG_POWER4_ONLY=y
|
||||
# CONFIG_PPC_MAPLE is not set
|
||||
# CONFIG_PPC_CELL is not set
|
||||
CONFIG_PPC_OF=y
|
||||
CONFIG_U3_DART=y
|
||||
CONFIG_MPIC=y
|
||||
# CONFIG_PPC_RTAS is not set
|
||||
# CONFIG_MMIO_NVRAM is not set
|
||||
# CONFIG_PPC_MPC106 is not set
|
||||
CONFIG_GENERIC_TBSYNC=y
|
||||
CONFIG_CPU_FREQ=y
|
||||
CONFIG_CPU_FREQ_TABLE=y
|
||||
# CONFIG_CPU_FREQ_DEBUG is not set
|
||||
CONFIG_CPU_FREQ_STAT=y
|
||||
# CONFIG_CPU_FREQ_STAT_DETAILS is not set
|
||||
CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y
|
||||
# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set
|
||||
CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
|
||||
CONFIG_CPU_FREQ_GOV_POWERSAVE=y
|
||||
CONFIG_CPU_FREQ_GOV_USERSPACE=y
|
||||
# CONFIG_CPU_FREQ_GOV_ONDEMAND is not set
|
||||
# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set
|
||||
CONFIG_CPU_FREQ_PMAC64=y
|
||||
# CONFIG_WANT_EARLY_SERIAL is not set
|
||||
|
||||
#
|
||||
# Kernel options
|
||||
#
|
||||
# CONFIG_HZ_100 is not set
|
||||
CONFIG_HZ_250=y
|
||||
# CONFIG_HZ_1000 is not set
|
||||
CONFIG_HZ=250
|
||||
CONFIG_PREEMPT_NONE=y
|
||||
# CONFIG_PREEMPT_VOLUNTARY is not set
|
||||
# CONFIG_PREEMPT is not set
|
||||
# CONFIG_PREEMPT_BKL is not set
|
||||
CONFIG_BINFMT_ELF=y
|
||||
# CONFIG_BINFMT_MISC is not set
|
||||
CONFIG_FORCE_MAX_ZONEORDER=13
|
||||
CONFIG_IOMMU_VMERGE=y
|
||||
CONFIG_SMP=y
|
||||
CONFIG_NR_CPUS=2
|
||||
# CONFIG_HOTPLUG_CPU is not set
|
||||
CONFIG_KEXEC=y
|
||||
CONFIG_IRQ_ALL_CPUS=y
|
||||
# CONFIG_NUMA is not set
|
||||
CONFIG_ARCH_SELECT_MEMORY_MODEL=y
|
||||
CONFIG_ARCH_FLATMEM_ENABLE=y
|
||||
CONFIG_SELECT_MEMORY_MODEL=y
|
||||
@ -100,28 +144,21 @@ CONFIG_FLATMEM_MANUAL=y
|
||||
CONFIG_FLATMEM=y
|
||||
CONFIG_FLAT_NODE_MEM_MAP=y
|
||||
# CONFIG_SPARSEMEM_STATIC is not set
|
||||
# CONFIG_NUMA is not set
|
||||
CONFIG_SPLIT_PTLOCK_CPUS=4
|
||||
# CONFIG_PPC_64K_PAGES is not set
|
||||
# CONFIG_SCHED_SMT is not set
|
||||
CONFIG_PREEMPT_NONE=y
|
||||
# CONFIG_PREEMPT_VOLUNTARY is not set
|
||||
# CONFIG_PREEMPT is not set
|
||||
# CONFIG_PREEMPT_BKL is not set
|
||||
# CONFIG_HZ_100 is not set
|
||||
CONFIG_HZ_250=y
|
||||
# CONFIG_HZ_1000 is not set
|
||||
CONFIG_HZ=250
|
||||
CONFIG_GENERIC_HARDIRQS=y
|
||||
CONFIG_SECCOMP=y
|
||||
CONFIG_BINFMT_ELF=y
|
||||
# CONFIG_BINFMT_MISC is not set
|
||||
# CONFIG_HOTPLUG_CPU is not set
|
||||
CONFIG_PROC_DEVICETREE=y
|
||||
# CONFIG_CMDLINE_BOOL is not set
|
||||
# CONFIG_PM is not set
|
||||
CONFIG_SECCOMP=y
|
||||
CONFIG_ISA_DMA_API=y
|
||||
|
||||
#
|
||||
# Bus Options
|
||||
# Bus options
|
||||
#
|
||||
CONFIG_GENERIC_ISA_DMA=y
|
||||
# CONFIG_PPC_I8259 is not set
|
||||
# CONFIG_PPC_INDIRECT_PCI is not set
|
||||
CONFIG_PCI=y
|
||||
CONFIG_PCI_DOMAINS=y
|
||||
CONFIG_PCI_LEGACY_PROC=y
|
||||
@ -136,6 +173,7 @@ CONFIG_PCI_LEGACY_PROC=y
|
||||
# PCI Hotplug Support
|
||||
#
|
||||
# CONFIG_HOTPLUG_PCI is not set
|
||||
CONFIG_KERNEL_START=0xc000000000000000
|
||||
|
||||
#
|
||||
# Networking
|
||||
@ -276,6 +314,10 @@ CONFIG_LLC=y
|
||||
# CONFIG_NET_DIVERT is not set
|
||||
# CONFIG_ECONET is not set
|
||||
# CONFIG_WAN_ROUTER is not set
|
||||
|
||||
#
|
||||
# QoS and/or fair queueing
|
||||
#
|
||||
# CONFIG_NET_SCHED is not set
|
||||
CONFIG_NET_CLS_ROUTE=y
|
||||
|
||||
@ -348,6 +390,11 @@ CONFIG_IOSCHED_NOOP=y
|
||||
CONFIG_IOSCHED_AS=y
|
||||
CONFIG_IOSCHED_DEADLINE=y
|
||||
CONFIG_IOSCHED_CFQ=y
|
||||
CONFIG_DEFAULT_AS=y
|
||||
# CONFIG_DEFAULT_DEADLINE is not set
|
||||
# CONFIG_DEFAULT_CFQ is not set
|
||||
# CONFIG_DEFAULT_NOOP is not set
|
||||
CONFIG_DEFAULT_IOSCHED="anticipatory"
|
||||
# CONFIG_ATA_OVER_ETH is not set
|
||||
|
||||
#
|
||||
@ -449,6 +496,7 @@ CONFIG_SCSI_SPI_ATTRS=y
|
||||
#
|
||||
# SCSI low-level drivers
|
||||
#
|
||||
# CONFIG_ISCSI_TCP is not set
|
||||
# CONFIG_BLK_DEV_3W_XXXX_RAID is not set
|
||||
# CONFIG_SCSI_3W_9XXX is not set
|
||||
# CONFIG_SCSI_ACARD is not set
|
||||
@ -465,10 +513,12 @@ CONFIG_SCSI_SATA_SVW=y
|
||||
# CONFIG_SCSI_ATA_PIIX is not set
|
||||
# CONFIG_SCSI_SATA_MV is not set
|
||||
# CONFIG_SCSI_SATA_NV is not set
|
||||
# CONFIG_SCSI_SATA_PROMISE is not set
|
||||
# CONFIG_SCSI_PDC_ADMA is not set
|
||||
# CONFIG_SCSI_SATA_QSTOR is not set
|
||||
# CONFIG_SCSI_SATA_PROMISE is not set
|
||||
# CONFIG_SCSI_SATA_SX4 is not set
|
||||
# CONFIG_SCSI_SATA_SIL is not set
|
||||
# CONFIG_SCSI_SATA_SIL24 is not set
|
||||
# CONFIG_SCSI_SATA_SIS is not set
|
||||
# CONFIG_SCSI_SATA_ULI is not set
|
||||
# CONFIG_SCSI_SATA_VIA is not set
|
||||
@ -567,6 +617,9 @@ CONFIG_IEEE1394_RAWIO=y
|
||||
CONFIG_ADB_PMU=y
|
||||
CONFIG_PMAC_SMU=y
|
||||
CONFIG_THERM_PM72=y
|
||||
CONFIG_WINDFARM=y
|
||||
CONFIG_WINDFARM_PM81=y
|
||||
CONFIG_WINDFARM_PM91=y
|
||||
|
||||
#
|
||||
# Network device support
|
||||
@ -603,6 +656,7 @@ CONFIG_SUNGEM=y
|
||||
# CONFIG_NET_TULIP is not set
|
||||
# CONFIG_HP100 is not set
|
||||
# CONFIG_NET_PCI is not set
|
||||
# CONFIG_FEC_8XX is not set
|
||||
|
||||
#
|
||||
# Ethernet (1000 Mbit)
|
||||
@ -768,6 +822,7 @@ CONFIG_MAX_RAW_DEVS=256
|
||||
# TPM devices
|
||||
#
|
||||
# CONFIG_TCG_TPM is not set
|
||||
# CONFIG_TELCLOCK is not set
|
||||
|
||||
#
|
||||
# I2C support
|
||||
@ -820,6 +875,7 @@ CONFIG_I2C_PMAC_SMU=y
|
||||
# CONFIG_SENSORS_PCF8591 is not set
|
||||
# CONFIG_SENSORS_RTC8564 is not set
|
||||
# CONFIG_SENSORS_MAX6875 is not set
|
||||
# CONFIG_RTC_X1205_I2C is not set
|
||||
# CONFIG_I2C_DEBUG_CORE is not set
|
||||
# CONFIG_I2C_DEBUG_ALGO is not set
|
||||
# CONFIG_I2C_DEBUG_BUS is not set
|
||||
@ -876,10 +932,9 @@ CONFIG_FB_OF=y
|
||||
# CONFIG_FB_ASILIANT is not set
|
||||
# CONFIG_FB_IMSTT is not set
|
||||
# CONFIG_FB_VGA16 is not set
|
||||
# CONFIG_FB_NVIDIA is not set
|
||||
CONFIG_FB_RIVA=y
|
||||
# CONFIG_FB_RIVA_I2C is not set
|
||||
# CONFIG_FB_RIVA_DEBUG is not set
|
||||
CONFIG_FB_NVIDIA=y
|
||||
CONFIG_FB_NVIDIA_I2C=y
|
||||
# CONFIG_FB_RIVA is not set
|
||||
# CONFIG_FB_MATROX is not set
|
||||
# CONFIG_FB_RADEON_OLD is not set
|
||||
CONFIG_FB_RADEON=y
|
||||
@ -924,7 +979,96 @@ CONFIG_LCD_DEVICE=y
|
||||
#
|
||||
# Sound
|
||||
#
|
||||
# CONFIG_SOUND is not set
|
||||
CONFIG_SOUND=m
|
||||
|
||||
#
|
||||
# Advanced Linux Sound Architecture
|
||||
#
|
||||
CONFIG_SND=m
|
||||
CONFIG_SND_TIMER=m
|
||||
CONFIG_SND_PCM=m
|
||||
CONFIG_SND_HWDEP=m
|
||||
CONFIG_SND_RAWMIDI=m
|
||||
CONFIG_SND_SEQUENCER=m
|
||||
# CONFIG_SND_SEQ_DUMMY is not set
|
||||
CONFIG_SND_OSSEMUL=y
|
||||
CONFIG_SND_MIXER_OSS=m
|
||||
CONFIG_SND_PCM_OSS=m
|
||||
CONFIG_SND_SEQUENCER_OSS=y
|
||||
# CONFIG_SND_VERBOSE_PRINTK is not set
|
||||
# CONFIG_SND_DEBUG is not set
|
||||
CONFIG_SND_GENERIC_DRIVER=y
|
||||
|
||||
#
|
||||
# Generic devices
|
||||
#
|
||||
# CONFIG_SND_DUMMY is not set
|
||||
# CONFIG_SND_VIRMIDI is not set
|
||||
# CONFIG_SND_MTPAV is not set
|
||||
# CONFIG_SND_SERIAL_U16550 is not set
|
||||
# CONFIG_SND_MPU401 is not set
|
||||
|
||||
#
|
||||
# PCI devices
|
||||
#
|
||||
# CONFIG_SND_ALI5451 is not set
|
||||
# CONFIG_SND_ATIIXP is not set
|
||||
# CONFIG_SND_ATIIXP_MODEM is not set
|
||||
# CONFIG_SND_AU8810 is not set
|
||||
# CONFIG_SND_AU8820 is not set
|
||||
# CONFIG_SND_AU8830 is not set
|
||||
# CONFIG_SND_AZT3328 is not set
|
||||
# CONFIG_SND_BT87X is not set
|
||||
# CONFIG_SND_CS46XX is not set
|
||||
# CONFIG_SND_CS4281 is not set
|
||||
# CONFIG_SND_EMU10K1 is not set
|
||||
# CONFIG_SND_EMU10K1X is not set
|
||||
# CONFIG_SND_CA0106 is not set
|
||||
# CONFIG_SND_KORG1212 is not set
|
||||
# CONFIG_SND_MIXART is not set
|
||||
# CONFIG_SND_NM256 is not set
|
||||
# CONFIG_SND_RME32 is not set
|
||||
# CONFIG_SND_RME96 is not set
|
||||
# CONFIG_SND_RME9652 is not set
|
||||
# CONFIG_SND_HDSP is not set
|
||||
# CONFIG_SND_HDSPM is not set
|
||||
# CONFIG_SND_TRIDENT is not set
|
||||
# CONFIG_SND_YMFPCI is not set
|
||||
# CONFIG_SND_AD1889 is not set
|
||||
# CONFIG_SND_ALS4000 is not set
|
||||
# CONFIG_SND_CMIPCI is not set
|
||||
# CONFIG_SND_ENS1370 is not set
|
||||
# CONFIG_SND_ENS1371 is not set
|
||||
# CONFIG_SND_ES1938 is not set
|
||||
# CONFIG_SND_ES1968 is not set
|
||||
# CONFIG_SND_MAESTRO3 is not set
|
||||
# CONFIG_SND_FM801 is not set
|
||||
# CONFIG_SND_ICE1712 is not set
|
||||
# CONFIG_SND_ICE1724 is not set
|
||||
# CONFIG_SND_INTEL8X0 is not set
|
||||
# CONFIG_SND_INTEL8X0M is not set
|
||||
# CONFIG_SND_SONICVIBES is not set
|
||||
# CONFIG_SND_VIA82XX is not set
|
||||
# CONFIG_SND_VIA82XX_MODEM is not set
|
||||
# CONFIG_SND_VX222 is not set
|
||||
# CONFIG_SND_HDA_INTEL is not set
|
||||
|
||||
#
|
||||
# ALSA PowerMac devices
|
||||
#
|
||||
CONFIG_SND_POWERMAC=m
|
||||
CONFIG_SND_POWERMAC_AUTO_DRC=y
|
||||
|
||||
#
|
||||
# USB devices
|
||||
#
|
||||
CONFIG_SND_USB_AUDIO=m
|
||||
# CONFIG_SND_USB_USX2Y is not set
|
||||
|
||||
#
|
||||
# Open Sound System
|
||||
#
|
||||
# CONFIG_SOUND_PRIME is not set
|
||||
|
||||
#
|
||||
# USB support
|
||||
@ -958,12 +1102,16 @@ CONFIG_USB_OHCI_LITTLE_ENDIAN=y
|
||||
#
|
||||
# USB Device Class drivers
|
||||
#
|
||||
# CONFIG_USB_BLUETOOTH_TTY is not set
|
||||
# CONFIG_OBSOLETE_OSS_USB_DRIVER is not set
|
||||
CONFIG_USB_ACM=m
|
||||
CONFIG_USB_PRINTER=y
|
||||
|
||||
#
|
||||
# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' may also be needed; see USB_STORAGE Help for more information
|
||||
# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support'
|
||||
#
|
||||
|
||||
#
|
||||
# may also be needed; see USB_STORAGE Help for more information
|
||||
#
|
||||
CONFIG_USB_STORAGE=y
|
||||
# CONFIG_USB_STORAGE_DEBUG is not set
|
||||
@ -1074,6 +1222,7 @@ CONFIG_USB_SERIAL_KEYSPAN_USA49WLC=y
|
||||
CONFIG_USB_SERIAL_KLSI=m
|
||||
CONFIG_USB_SERIAL_KOBIL_SCT=m
|
||||
CONFIG_USB_SERIAL_MCT_U232=m
|
||||
# CONFIG_USB_SERIAL_NOKIA_DKU2 is not set
|
||||
CONFIG_USB_SERIAL_PL2303=m
|
||||
# CONFIG_USB_SERIAL_HP4X is not set
|
||||
CONFIG_USB_SERIAL_SAFE=m
|
||||
@ -1310,6 +1459,20 @@ CONFIG_NLS_ISO8859_15=y
|
||||
# CONFIG_NLS_KOI8_U is not set
|
||||
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
|
||||
#
|
||||
@ -1331,12 +1494,14 @@ CONFIG_DETECT_SOFTLOCKUP=y
|
||||
# CONFIG_DEBUG_KOBJECT is not set
|
||||
# CONFIG_DEBUG_INFO is not set
|
||||
CONFIG_DEBUG_FS=y
|
||||
# CONFIG_DEBUG_VM is not set
|
||||
# CONFIG_RCU_TORTURE_TEST is not set
|
||||
# CONFIG_DEBUG_STACKOVERFLOW is not set
|
||||
# CONFIG_KPROBES is not set
|
||||
# CONFIG_DEBUG_STACK_USAGE is not set
|
||||
# CONFIG_DEBUGGER is not set
|
||||
# CONFIG_PPCDBG is not set
|
||||
CONFIG_IRQSTACKS=y
|
||||
CONFIG_BOOTX_TEXT=y
|
||||
|
||||
#
|
||||
# Security options
|
||||
@ -1376,17 +1541,3 @@ CONFIG_CRYPTO_TEST=m
|
||||
#
|
||||
# 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
|
||||
|
@ -603,6 +603,76 @@ _GLOBAL(real_writeb)
|
||||
blr
|
||||
#endif /* defined(CONFIG_PPC_PMAC) || defined(CONFIG_PPC_MAPLE) */
|
||||
|
||||
/*
|
||||
* SCOM access functions for 970 (FX only for now)
|
||||
*
|
||||
* unsigned long scom970_read(unsigned int address);
|
||||
* void scom970_write(unsigned int address, unsigned long value);
|
||||
*
|
||||
* The address passed in is the 24 bits register address. This code
|
||||
* is 970 specific and will not check the status bits, so you should
|
||||
* know what you are doing.
|
||||
*/
|
||||
_GLOBAL(scom970_read)
|
||||
/* interrupts off */
|
||||
mfmsr r4
|
||||
ori r0,r4,MSR_EE
|
||||
xori r0,r0,MSR_EE
|
||||
mtmsrd r0,1
|
||||
|
||||
/* rotate 24 bits SCOM address 8 bits left and mask out it's low 8 bits
|
||||
* (including parity). On current CPUs they must be 0'd,
|
||||
* and finally or in RW bit
|
||||
*/
|
||||
rlwinm r3,r3,8,0,15
|
||||
ori r3,r3,0x8000
|
||||
|
||||
/* do the actual scom read */
|
||||
sync
|
||||
mtspr SPRN_SCOMC,r3
|
||||
isync
|
||||
mfspr r3,SPRN_SCOMD
|
||||
isync
|
||||
mfspr r0,SPRN_SCOMC
|
||||
isync
|
||||
|
||||
/* XXX: fixup result on some buggy 970's (ouch ! we lost a bit, bah
|
||||
* that's the best we can do). Not implemented yet as we don't use
|
||||
* the scom on any of the bogus CPUs yet, but may have to be done
|
||||
* ultimately
|
||||
*/
|
||||
|
||||
/* restore interrupts */
|
||||
mtmsrd r4,1
|
||||
blr
|
||||
|
||||
|
||||
_GLOBAL(scom970_write)
|
||||
/* interrupts off */
|
||||
mfmsr r5
|
||||
ori r0,r5,MSR_EE
|
||||
xori r0,r0,MSR_EE
|
||||
mtmsrd r0,1
|
||||
|
||||
/* rotate 24 bits SCOM address 8 bits left and mask out it's low 8 bits
|
||||
* (including parity). On current CPUs they must be 0'd.
|
||||
*/
|
||||
|
||||
rlwinm r3,r3,8,0,15
|
||||
|
||||
sync
|
||||
mtspr SPRN_SCOMD,r4 /* write data */
|
||||
isync
|
||||
mtspr SPRN_SCOMC,r3 /* write command */
|
||||
isync
|
||||
mfspr 3,SPRN_SCOMC
|
||||
isync
|
||||
|
||||
/* restore interrupts */
|
||||
mtmsrd r5,1
|
||||
blr
|
||||
|
||||
|
||||
/*
|
||||
* Create a kernel thread
|
||||
* kernel_thread(fn, arg, flags)
|
||||
|
@ -46,10 +46,10 @@
|
||||
#include <asm/processor.h>
|
||||
#include <asm/mmu.h>
|
||||
#include <asm/prom.h>
|
||||
#include <asm/machdep.h>
|
||||
#ifdef CONFIG_PPC64
|
||||
#include <asm/firmware.h>
|
||||
#include <asm/time.h>
|
||||
#include <asm/machdep.h>
|
||||
#endif
|
||||
|
||||
extern unsigned long _get_SP(void);
|
||||
@ -203,10 +203,8 @@ int dump_spe(struct pt_regs *regs, elf_vrregset_t *evrregs)
|
||||
|
||||
int set_dabr(unsigned long dabr)
|
||||
{
|
||||
#ifdef CONFIG_PPC64
|
||||
if (ppc_md.set_dabr)
|
||||
return ppc_md.set_dabr(dabr);
|
||||
#endif
|
||||
|
||||
mtspr(SPRN_DABR, dabr);
|
||||
return 0;
|
||||
|
@ -1974,14 +1974,29 @@ EXPORT_SYMBOL(get_property);
|
||||
/*
|
||||
* Add a property to a node
|
||||
*/
|
||||
void prom_add_property(struct device_node* np, struct property* prop)
|
||||
int prom_add_property(struct device_node* np, struct property* prop)
|
||||
{
|
||||
struct property **next = &np->properties;
|
||||
struct property **next;
|
||||
|
||||
prop->next = NULL;
|
||||
while (*next)
|
||||
write_lock(&devtree_lock);
|
||||
next = &np->properties;
|
||||
while (*next) {
|
||||
if (strcmp(prop->name, (*next)->name) == 0) {
|
||||
/* duplicate ! don't insert it */
|
||||
write_unlock(&devtree_lock);
|
||||
return -1;
|
||||
}
|
||||
next = &(*next)->next;
|
||||
}
|
||||
*next = prop;
|
||||
write_unlock(&devtree_lock);
|
||||
|
||||
/* try to add to proc as well if it was initialized */
|
||||
if (np->pde)
|
||||
proc_device_tree_add_prop(np->pde, prop);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* I quickly hacked that one, check against spec ! */
|
||||
|
@ -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)
|
||||
{
|
||||
return call_prom("getprop", 4, 1, node, ADDR(pname),
|
||||
(u32)(unsigned long) value, (u32) valuelen);
|
||||
}
|
||||
|
||||
static int __init prom_getproplen(phandle node, const char *pname)
|
||||
static int inline prom_getproplen(phandle node, const char *pname)
|
||||
{
|
||||
return call_prom("getproplen", 2, 1, node, ADDR(pname));
|
||||
}
|
||||
|
||||
static int __init prom_setprop(phandle node, const char *pname,
|
||||
static int inline prom_setprop(phandle node, const char *pname,
|
||||
void *value, size_t valuelen)
|
||||
{
|
||||
return call_prom("setprop", 4, 1, node, ADDR(pname),
|
||||
@ -1408,8 +1408,9 @@ static int __init prom_find_machine_type(void)
|
||||
struct prom_t *_prom = &RELOC(prom);
|
||||
char compat[256];
|
||||
int len, i = 0;
|
||||
#ifdef CONFIG_PPC64
|
||||
phandle rtas;
|
||||
|
||||
#endif
|
||||
len = prom_getprop(_prom->root, "compatible",
|
||||
compat, sizeof(compat)-1);
|
||||
if (len > 0) {
|
||||
@ -1872,7 +1873,7 @@ static void __init fixup_device_tree(void)
|
||||
if (prom_getprop(u3, "device-rev", &u3_rev, sizeof(u3_rev))
|
||||
== PROM_ERROR)
|
||||
return;
|
||||
if (u3_rev != 0x35 && u3_rev != 0x37)
|
||||
if (u3_rev < 0x35 || u3_rev > 0x39)
|
||||
return;
|
||||
/* does it need fixup ? */
|
||||
if (prom_getproplen(i2c, "interrupts") > 0)
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include <asm/prom.h>
|
||||
#include <asm/rtas.h>
|
||||
@ -83,7 +84,7 @@ void call_rtas_display_status_delay(unsigned char c)
|
||||
while (width-- > 0)
|
||||
call_rtas_display_status(' ');
|
||||
width = 16;
|
||||
udelay(500000);
|
||||
mdelay(500);
|
||||
pending_newline = 1;
|
||||
} else {
|
||||
if (pending_newline) {
|
||||
@ -608,7 +609,6 @@ asmlinkage int ppc_rtas(struct rtas_args __user *uargs)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
/* This version can't take the spinlock, because it never returns */
|
||||
|
||||
struct rtas_args rtas_stop_self_args = {
|
||||
@ -633,7 +633,6 @@ void rtas_stop_self(void)
|
||||
|
||||
panic("Alas, I survived.\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Call early during boot, before mem init or bootmem, to retreive the RTAS
|
||||
|
@ -405,6 +405,46 @@ static int __init set_preferred_console(void)
|
||||
console_initcall(set_preferred_console);
|
||||
#endif /* CONFIG_PPC_MULTIPLATFORM */
|
||||
|
||||
void __init check_for_initrd(void)
|
||||
{
|
||||
#ifdef CONFIG_BLK_DEV_INITRD
|
||||
unsigned long *prop;
|
||||
|
||||
DBG(" -> check_for_initrd()\n");
|
||||
|
||||
if (of_chosen) {
|
||||
prop = (unsigned long *)get_property(of_chosen,
|
||||
"linux,initrd-start", NULL);
|
||||
if (prop != NULL) {
|
||||
initrd_start = (unsigned long)__va(*prop);
|
||||
prop = (unsigned long *)get_property(of_chosen,
|
||||
"linux,initrd-end", NULL);
|
||||
if (prop != NULL) {
|
||||
initrd_end = (unsigned long)__va(*prop);
|
||||
initrd_below_start_ok = 1;
|
||||
} else
|
||||
initrd_start = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* If we were passed an initrd, set the ROOT_DEV properly if the values
|
||||
* look sensible. If not, clear initrd reference.
|
||||
*/
|
||||
if (initrd_start >= KERNELBASE && initrd_end >= KERNELBASE &&
|
||||
initrd_end > initrd_start)
|
||||
ROOT_DEV = Root_RAM0;
|
||||
else {
|
||||
printk("Bogus initrd %08lx %08lx\n", initrd_start, initrd_end);
|
||||
initrd_start = initrd_end = 0;
|
||||
}
|
||||
|
||||
if (initrd_start)
|
||||
printk("Found initrd at 0x%lx:0x%lx\n", initrd_start, initrd_end);
|
||||
|
||||
DBG(" <- check_for_initrd()\n");
|
||||
#endif /* CONFIG_BLK_DEV_INITRD */
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
|
||||
/**
|
||||
|
@ -286,6 +286,7 @@ void __init setup_arch(char **cmdline_p)
|
||||
loops_per_jiffy = 500000000 / HZ;
|
||||
|
||||
unflatten_device_tree();
|
||||
check_for_initrd();
|
||||
finish_device_tree();
|
||||
|
||||
smp_setup_cpu_maps();
|
||||
|
@ -41,7 +41,6 @@
|
||||
#include <asm/elf.h>
|
||||
#include <asm/machdep.h>
|
||||
#include <asm/paca.h>
|
||||
#include <asm/ppcdebug.h>
|
||||
#include <asm/time.h>
|
||||
#include <asm/cputable.h>
|
||||
#include <asm/sections.h>
|
||||
@ -60,6 +59,7 @@
|
||||
#include <asm/firmware.h>
|
||||
#include <asm/systemcfg.h>
|
||||
#include <asm/xmon.h>
|
||||
#include <asm/udbg.h>
|
||||
|
||||
#ifdef DEBUG
|
||||
#define DBG(fmt...) udbg_printf(fmt)
|
||||
@ -243,12 +243,6 @@ void __init early_setup(unsigned long dt_ptr)
|
||||
|
||||
DBG(" -> early_setup()\n");
|
||||
|
||||
/*
|
||||
* Fill the default DBG level (do we want to keep
|
||||
* that old mecanism around forever ?)
|
||||
*/
|
||||
ppcdbg_initialize();
|
||||
|
||||
/*
|
||||
* Do early initializations using the flattened device
|
||||
* tree, like retreiving the physical memory map or
|
||||
@ -401,43 +395,6 @@ static void __init initialize_cache_info(void)
|
||||
DBG(" <- initialize_cache_info()\n");
|
||||
}
|
||||
|
||||
static void __init check_for_initrd(void)
|
||||
{
|
||||
#ifdef CONFIG_BLK_DEV_INITRD
|
||||
u64 *prop;
|
||||
|
||||
DBG(" -> check_for_initrd()\n");
|
||||
|
||||
if (of_chosen) {
|
||||
prop = (u64 *)get_property(of_chosen,
|
||||
"linux,initrd-start", NULL);
|
||||
if (prop != NULL) {
|
||||
initrd_start = (unsigned long)__va(*prop);
|
||||
prop = (u64 *)get_property(of_chosen,
|
||||
"linux,initrd-end", NULL);
|
||||
if (prop != NULL) {
|
||||
initrd_end = (unsigned long)__va(*prop);
|
||||
initrd_below_start_ok = 1;
|
||||
} else
|
||||
initrd_start = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* If we were passed an initrd, set the ROOT_DEV properly if the values
|
||||
* look sensible. If not, clear initrd reference.
|
||||
*/
|
||||
if (initrd_start >= KERNELBASE && initrd_end >= KERNELBASE &&
|
||||
initrd_end > initrd_start)
|
||||
ROOT_DEV = Root_RAM0;
|
||||
else
|
||||
initrd_start = initrd_end = 0;
|
||||
|
||||
if (initrd_start)
|
||||
printk("Found initrd at 0x%lx:0x%lx\n", initrd_start, initrd_end);
|
||||
|
||||
DBG(" <- check_for_initrd()\n");
|
||||
#endif /* CONFIG_BLK_DEV_INITRD */
|
||||
}
|
||||
|
||||
/*
|
||||
* Do some initial setup of the system. The parameters are those which
|
||||
@ -521,7 +478,6 @@ void __init setup_system(void)
|
||||
|
||||
printk("-----------------------------------------------------\n");
|
||||
printk("ppc64_pft_size = 0x%lx\n", ppc64_pft_size);
|
||||
printk("ppc64_debug_switch = 0x%lx\n", ppc64_debug_switch);
|
||||
printk("ppc64_interrupt_controller = 0x%ld\n", ppc64_interrupt_controller);
|
||||
printk("systemcfg = 0x%p\n", systemcfg);
|
||||
printk("systemcfg->platform = 0x%x\n", systemcfg->platform);
|
||||
|
@ -44,7 +44,6 @@
|
||||
#include <asm/cacheflush.h>
|
||||
#ifdef CONFIG_PPC64
|
||||
#include "ppc32.h"
|
||||
#include <asm/ppcdebug.h>
|
||||
#include <asm/unistd.h>
|
||||
#include <asm/vdso.h>
|
||||
#else
|
||||
|
@ -33,7 +33,6 @@
|
||||
#include <asm/ucontext.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/pgtable.h>
|
||||
#include <asm/ppcdebug.h>
|
||||
#include <asm/unistd.h>
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/vdso.h>
|
||||
|
@ -40,7 +40,6 @@
|
||||
#include <asm/prom.h>
|
||||
#include <asm/smp.h>
|
||||
#include <asm/time.h>
|
||||
#include <asm/xmon.h>
|
||||
#include <asm/machdep.h>
|
||||
#include <asm/cputable.h>
|
||||
#include <asm/system.h>
|
||||
|
@ -61,6 +61,7 @@
|
||||
#include <asm/prom.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/div64.h>
|
||||
#include <asm/smp.h>
|
||||
#ifdef CONFIG_PPC64
|
||||
#include <asm/systemcfg.h>
|
||||
#include <asm/firmware.h>
|
||||
@ -119,10 +120,6 @@ static unsigned adjusting_time = 0;
|
||||
unsigned long ppc_proc_freq;
|
||||
unsigned long ppc_tb_freq;
|
||||
|
||||
#ifdef CONFIG_PPC32 /* XXX for now */
|
||||
#define boot_cpuid 0
|
||||
#endif
|
||||
|
||||
u64 tb_last_jiffy __cacheline_aligned_in_smp;
|
||||
unsigned long tb_last_stamp;
|
||||
|
||||
|
@ -39,7 +39,6 @@
|
||||
#include <asm/io.h>
|
||||
#include <asm/machdep.h>
|
||||
#include <asm/rtas.h>
|
||||
#include <asm/xmon.h>
|
||||
#include <asm/pmc.h>
|
||||
#ifdef CONFIG_PPC32
|
||||
#include <asm/reg.h>
|
||||
@ -748,22 +747,12 @@ static int check_bug_trap(struct pt_regs *regs)
|
||||
return 0;
|
||||
if (bug->line & BUG_WARNING_TRAP) {
|
||||
/* this is a WARN_ON rather than BUG/BUG_ON */
|
||||
#ifdef CONFIG_XMON
|
||||
xmon_printf(KERN_ERR "Badness in %s at %s:%ld\n",
|
||||
bug->function, bug->file,
|
||||
bug->line & ~BUG_WARNING_TRAP);
|
||||
#endif /* CONFIG_XMON */
|
||||
printk(KERN_ERR "Badness in %s at %s:%ld\n",
|
||||
bug->function, bug->file,
|
||||
bug->line & ~BUG_WARNING_TRAP);
|
||||
dump_stack();
|
||||
return 1;
|
||||
}
|
||||
#ifdef CONFIG_XMON
|
||||
xmon_printf(KERN_CRIT "kernel BUG in %s at %s:%ld!\n",
|
||||
bug->function, bug->file, bug->line);
|
||||
xmon(regs);
|
||||
#endif /* CONFIG_XMON */
|
||||
printk(KERN_CRIT "kernel BUG in %s at %s:%ld!\n",
|
||||
bug->function, bug->file, bug->line);
|
||||
|
||||
|
@ -23,6 +23,7 @@
|
||||
#if defined(CONFIG_PPC_SPLPAR) || defined(CONFIG_PPC_ISERIES)
|
||||
#include <asm/hvcall.h>
|
||||
#include <asm/iseries/hv_call.h>
|
||||
#include <asm/smp.h>
|
||||
|
||||
void __spin_yield(raw_spinlock_t *lock)
|
||||
{
|
||||
|
@ -389,5 +389,22 @@ void bad_page_fault(struct pt_regs *regs, unsigned long address, int sig)
|
||||
}
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
@ -33,7 +33,6 @@
|
||||
#include <linux/init.h>
|
||||
#include <linux/signal.h>
|
||||
|
||||
#include <asm/ppcdebug.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/pgtable.h>
|
||||
#include <asm/mmu.h>
|
||||
@ -409,12 +408,6 @@ void __init htab_initialize(void)
|
||||
htab_size_bytes = htab_get_table_size();
|
||||
pteg_count = htab_size_bytes >> 7;
|
||||
|
||||
/* For debug, make the HTAB 1/8 as big as it normally would be. */
|
||||
ifppcdebug(PPCDBG_HTABSIZE) {
|
||||
pteg_count >>= 3;
|
||||
htab_size_bytes = pteg_count << 7;
|
||||
}
|
||||
|
||||
htab_hash_mask = pteg_count - 1;
|
||||
|
||||
if (systemcfg->platform & PLATFORM_LPAR) {
|
||||
@ -514,6 +507,9 @@ unsigned int hash_page_do_lazy_icache(unsigned int pp, pte_t pte, int trap)
|
||||
{
|
||||
struct page *page;
|
||||
|
||||
if (!pfn_valid(pte_pfn(pte)))
|
||||
return pp;
|
||||
|
||||
page = pte_page(pte);
|
||||
|
||||
/* page is dirty */
|
||||
|
@ -57,7 +57,6 @@
|
||||
#include <asm/processor.h>
|
||||
#include <asm/mmzone.h>
|
||||
#include <asm/cputable.h>
|
||||
#include <asm/ppcdebug.h>
|
||||
#include <asm/sections.h>
|
||||
#include <asm/system.h>
|
||||
#include <asm/iommu.h>
|
||||
|
@ -358,7 +358,7 @@ void __init mem_init(void)
|
||||
}
|
||||
|
||||
codesize = (unsigned long)&_sdata - (unsigned long)&_stext;
|
||||
datasize = (unsigned long)&__init_begin - (unsigned long)&_sdata;
|
||||
datasize = (unsigned long)&_edata - (unsigned long)&_sdata;
|
||||
initsize = (unsigned long)&__init_end - (unsigned long)&__init_begin;
|
||||
bsssize = (unsigned long)&__bss_stop - (unsigned long)&__bss_start;
|
||||
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include <asm/machdep.h>
|
||||
#include <asm/abs_addr.h>
|
||||
#include <asm/system.h>
|
||||
#include <asm/smp.h>
|
||||
|
||||
static int numa_enabled = 1;
|
||||
|
||||
|
@ -59,7 +59,6 @@
|
||||
#include <asm/processor.h>
|
||||
#include <asm/mmzone.h>
|
||||
#include <asm/cputable.h>
|
||||
#include <asm/ppcdebug.h>
|
||||
#include <asm/sections.h>
|
||||
#include <asm/system.h>
|
||||
#include <asm/iommu.h>
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include <asm/systemcfg.h>
|
||||
#include <asm/rtas.h>
|
||||
#include <asm/oprofile_impl.h>
|
||||
#include <asm/reg.h>
|
||||
|
||||
#define dbg(args...)
|
||||
|
||||
@ -81,6 +82,26 @@ static void power4_reg_setup(struct op_counter_config *ctr,
|
||||
|
||||
extern void ppc64_enable_pmcs(void);
|
||||
|
||||
/*
|
||||
* Older CPUs require the MMCRA sample bit to be always set, but newer
|
||||
* CPUs only want it set for some groups. Eventually we will remove all
|
||||
* knowledge of this bit in the kernel, oprofile userspace should be
|
||||
* setting it when required.
|
||||
*
|
||||
* In order to keep current installations working we force the bit for
|
||||
* those older CPUs. Once everyone has updated their oprofile userspace we
|
||||
* can remove this hack.
|
||||
*/
|
||||
static inline int mmcra_must_set_sample(void)
|
||||
{
|
||||
if (__is_processor(PV_POWER4) || __is_processor(PV_POWER4p) ||
|
||||
__is_processor(PV_970) || __is_processor(PV_970FX) ||
|
||||
__is_processor(PV_970MP))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void power4_cpu_setup(void *unused)
|
||||
{
|
||||
unsigned int mmcr0 = mmcr0_val;
|
||||
@ -98,7 +119,8 @@ static void power4_cpu_setup(void *unused)
|
||||
|
||||
mtspr(SPRN_MMCR1, mmcr1_val);
|
||||
|
||||
mmcra |= MMCRA_SAMPLE_ENABLE;
|
||||
if (mmcra_must_set_sample())
|
||||
mmcra |= MMCRA_SAMPLE_ENABLE;
|
||||
mtspr(SPRN_MMCRA, mmcra);
|
||||
|
||||
dbg("setup on cpu %d, mmcr0 %lx\n", smp_processor_id(),
|
||||
|
@ -35,7 +35,6 @@
|
||||
#include <linux/irq.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#include <asm/ppcdebug.h>
|
||||
#include <asm/iseries/hv_types.h>
|
||||
#include <asm/iseries/hv_lp_event.h>
|
||||
#include <asm/iseries/hv_call_xm.h>
|
||||
@ -227,8 +226,6 @@ static void iSeries_enable_IRQ(unsigned int irq)
|
||||
/* Unmask secondary INTA */
|
||||
mask = 0x80000000;
|
||||
HvCallPci_unmaskInterrupts(bus, subBus, deviceId, mask);
|
||||
PPCDBG(PPCDBG_BUSWALK, "iSeries_enable_IRQ 0x%02X.%02X.%02X 0x%04X\n",
|
||||
bus, subBus, deviceId, irq);
|
||||
}
|
||||
|
||||
/* This is called by iSeries_activate_IRQs */
|
||||
@ -310,8 +307,6 @@ static void iSeries_disable_IRQ(unsigned int irq)
|
||||
/* Mask secondary INTA */
|
||||
mask = 0x80000000;
|
||||
HvCallPci_maskInterrupts(bus, subBus, deviceId, mask);
|
||||
PPCDBG(PPCDBG_BUSWALK, "iSeries_disable_IRQ 0x%02X.%02X.%02X 0x%04X\n",
|
||||
bus, subBus, deviceId, irq);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -32,7 +32,6 @@
|
||||
#include <asm/prom.h>
|
||||
#include <asm/machdep.h>
|
||||
#include <asm/pci-bridge.h>
|
||||
#include <asm/ppcdebug.h>
|
||||
#include <asm/iommu.h>
|
||||
#include <asm/abs_addr.h>
|
||||
|
||||
@ -207,10 +206,6 @@ static struct device_node *build_device_node(HvBusNumber Bus,
|
||||
struct device_node *node;
|
||||
struct pci_dn *pdn;
|
||||
|
||||
PPCDBG(PPCDBG_BUSWALK,
|
||||
"-build_device_node 0x%02X.%02X.%02X Function: %02X\n",
|
||||
Bus, SubBus, AgentId, Function);
|
||||
|
||||
node = kmalloc(sizeof(struct device_node), GFP_KERNEL);
|
||||
if (node == NULL)
|
||||
return NULL;
|
||||
@ -243,8 +238,6 @@ unsigned long __init find_and_init_phbs(void)
|
||||
struct pci_controller *phb;
|
||||
HvBusNumber bus;
|
||||
|
||||
PPCDBG(PPCDBG_BUSWALK, "find_and_init_phbs Entry\n");
|
||||
|
||||
/* Check all possible buses. */
|
||||
for (bus = 0; bus < 256; bus++) {
|
||||
int ret = HvCallXm_testBus(bus);
|
||||
@ -261,9 +254,6 @@ unsigned long __init find_and_init_phbs(void)
|
||||
phb->last_busno = bus;
|
||||
phb->ops = &iSeries_pci_ops;
|
||||
|
||||
PPCDBG(PPCDBG_BUSWALK, "PCI:Create iSeries pci_controller(%p), Bus: %04X\n",
|
||||
phb, bus);
|
||||
|
||||
/* Find and connect the devices. */
|
||||
scan_PHB_slots(phb);
|
||||
}
|
||||
@ -285,11 +275,9 @@ unsigned long __init find_and_init_phbs(void)
|
||||
*/
|
||||
void iSeries_pcibios_init(void)
|
||||
{
|
||||
PPCDBG(PPCDBG_BUSWALK, "iSeries_pcibios_init Entry.\n");
|
||||
iomm_table_initialize();
|
||||
find_and_init_phbs();
|
||||
io_page_mask = -1;
|
||||
PPCDBG(PPCDBG_BUSWALK, "iSeries_pcibios_init Exit.\n");
|
||||
}
|
||||
|
||||
/*
|
||||
@ -301,8 +289,6 @@ void __init iSeries_pci_final_fixup(void)
|
||||
struct device_node *node;
|
||||
int DeviceCount = 0;
|
||||
|
||||
PPCDBG(PPCDBG_BUSWALK, "iSeries_pcibios_fixup Entry.\n");
|
||||
|
||||
/* Fix up at the device node and pci_dev relationship */
|
||||
mf_display_src(0xC9000100);
|
||||
|
||||
@ -316,9 +302,6 @@ void __init iSeries_pci_final_fixup(void)
|
||||
++DeviceCount;
|
||||
pdev->sysdata = (void *)node;
|
||||
PCI_DN(node)->pcidev = pdev;
|
||||
PPCDBG(PPCDBG_BUSWALK,
|
||||
"pdev 0x%p <==> DevNode 0x%p\n",
|
||||
pdev, node);
|
||||
allocate_device_bars(pdev);
|
||||
iSeries_Device_Information(pdev, DeviceCount);
|
||||
iommu_devnode_init_iSeries(node);
|
||||
@ -333,13 +316,10 @@ void __init iSeries_pci_final_fixup(void)
|
||||
|
||||
void pcibios_fixup_bus(struct pci_bus *PciBus)
|
||||
{
|
||||
PPCDBG(PPCDBG_BUSWALK, "iSeries_pcibios_fixup_bus(0x%04X) Entry.\n",
|
||||
PciBus->number);
|
||||
}
|
||||
|
||||
void pcibios_fixup_resources(struct pci_dev *pdev)
|
||||
{
|
||||
PPCDBG(PPCDBG_BUSWALK, "fixup_resources pdev %p\n", pdev);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -401,9 +381,6 @@ static void scan_EADS_bridge(HvBusNumber bus, HvSubBusNumber SubBus,
|
||||
printk("found device at bus %d idsel %d func %d (AgentId %x)\n",
|
||||
bus, IdSel, Function, AgentId);
|
||||
/* Connect EADs: 0x18.00.12 = 0x00 */
|
||||
PPCDBG(PPCDBG_BUSWALK,
|
||||
"PCI:Connect EADs: 0x%02X.%02X.%02X\n",
|
||||
bus, SubBus, AgentId);
|
||||
HvRc = HvCallPci_getBusUnitInfo(bus, SubBus, AgentId,
|
||||
iseries_hv_addr(BridgeInfo),
|
||||
sizeof(struct HvCallPci_BridgeInfo));
|
||||
@ -414,14 +391,6 @@ static void scan_EADS_bridge(HvBusNumber bus, HvSubBusNumber SubBus,
|
||||
BridgeInfo->maxAgents,
|
||||
BridgeInfo->maxSubBusNumber,
|
||||
BridgeInfo->logicalSlotNumber);
|
||||
PPCDBG(PPCDBG_BUSWALK,
|
||||
"PCI: BridgeInfo, Type:0x%02X, SubBus:0x%02X, MaxAgents:0x%02X, MaxSubBus: 0x%02X, LSlot: 0x%02X\n",
|
||||
BridgeInfo->busUnitInfo.deviceType,
|
||||
BridgeInfo->subBusNumber,
|
||||
BridgeInfo->maxAgents,
|
||||
BridgeInfo->maxSubBusNumber,
|
||||
BridgeInfo->logicalSlotNumber);
|
||||
|
||||
if (BridgeInfo->busUnitInfo.deviceType ==
|
||||
HvCallPci_BridgeDevice) {
|
||||
/* Scan_Bridge_Slot...: 0x18.00.12 */
|
||||
@ -454,9 +423,6 @@ static int scan_bridge_slot(HvBusNumber Bus,
|
||||
|
||||
/* iSeries_allocate_IRQ.: 0x18.00.12(0xA3) */
|
||||
Irq = iSeries_allocate_IRQ(Bus, 0, EADsIdSel);
|
||||
PPCDBG(PPCDBG_BUSWALK,
|
||||
"PCI:- allocate and assign IRQ 0x%02X.%02X.%02X = 0x%02X\n",
|
||||
Bus, 0, EADsIdSel, Irq);
|
||||
|
||||
/*
|
||||
* Connect all functions of any device found.
|
||||
@ -482,9 +448,6 @@ static int scan_bridge_slot(HvBusNumber Bus,
|
||||
printk("read vendor ID: %x\n", VendorId);
|
||||
|
||||
/* FoundDevice: 0x18.28.10 = 0x12AE */
|
||||
PPCDBG(PPCDBG_BUSWALK,
|
||||
"PCI:- FoundDevice: 0x%02X.%02X.%02X = 0x%04X, irq %d\n",
|
||||
Bus, SubBus, AgentId, VendorId, Irq);
|
||||
HvRc = HvCallPci_configStore8(Bus, SubBus, AgentId,
|
||||
PCI_INTERRUPT_LINE, Irq);
|
||||
if (HvRc != 0)
|
||||
|
@ -71,8 +71,6 @@ extern void hvlog(char *fmt, ...);
|
||||
#endif
|
||||
|
||||
/* Function Prototypes */
|
||||
extern void ppcdbg_initialize(void);
|
||||
|
||||
static void build_iSeries_Memory_Map(void);
|
||||
static void iseries_shared_idle(void);
|
||||
static void iseries_dedicated_idle(void);
|
||||
@ -309,8 +307,6 @@ static void __init iSeries_init_early(void)
|
||||
|
||||
ppc64_firmware_features = FW_FEATURE_ISERIES;
|
||||
|
||||
ppcdbg_initialize();
|
||||
|
||||
ppc64_interrupt_controller = IC_ISERIES;
|
||||
|
||||
#if defined(CONFIG_BLK_DEV_INITRD)
|
||||
|
@ -40,7 +40,6 @@
|
||||
#include <asm/paca.h>
|
||||
#include <asm/iseries/hv_call.h>
|
||||
#include <asm/time.h>
|
||||
#include <asm/ppcdebug.h>
|
||||
#include <asm/machdep.h>
|
||||
#include <asm/cputable.h>
|
||||
#include <asm/system.h>
|
||||
|
@ -1,7 +1,8 @@
|
||||
obj-y += pic.o setup.o time.o feature.o pci.o \
|
||||
sleep.o low_i2c.o cache.o
|
||||
obj-$(CONFIG_PMAC_BACKLIGHT) += backlight.o
|
||||
obj-$(CONFIG_CPU_FREQ_PMAC) += cpufreq.o
|
||||
obj-$(CONFIG_CPU_FREQ_PMAC) += cpufreq_32.o
|
||||
obj-$(CONFIG_CPU_FREQ_PMAC64) += cpufreq_64.o
|
||||
obj-$(CONFIG_NVRAM) += nvram.o
|
||||
# ppc64 pmac doesn't define CONFIG_NVRAM but needs nvram stuff
|
||||
obj-$(CONFIG_PPC64) += nvram.o
|
||||
|
@ -397,18 +397,16 @@ static int pmac_cpufreq_target( struct cpufreq_policy *policy,
|
||||
unsigned int relation)
|
||||
{
|
||||
unsigned int newstate = 0;
|
||||
int rc;
|
||||
|
||||
if (cpufreq_frequency_table_target(policy, pmac_cpu_freqs,
|
||||
target_freq, relation, &newstate))
|
||||
return -EINVAL;
|
||||
|
||||
return do_set_cpu_speed(newstate, 1);
|
||||
}
|
||||
rc = do_set_cpu_speed(newstate, 1);
|
||||
|
||||
unsigned int pmac_get_one_cpufreq(int i)
|
||||
{
|
||||
/* Supports only one CPU for now */
|
||||
return (i == 0) ? cur_freq : 0;
|
||||
ppc_proc_freq = cur_freq * 1000ul;
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int pmac_cpufreq_cpu_init(struct cpufreq_policy *policy)
|
||||
@ -474,6 +472,8 @@ static int pmac_cpufreq_resume(struct cpufreq_policy *policy)
|
||||
do_set_cpu_speed(sleep_freq == low_freq ?
|
||||
CPUFREQ_LOW : CPUFREQ_HIGH, 0);
|
||||
|
||||
ppc_proc_freq = cur_freq * 1000ul;
|
||||
|
||||
no_schedule = 0;
|
||||
return 0;
|
||||
}
|
||||
@ -547,7 +547,7 @@ static int pmac_cpufreq_init_MacRISC3(struct device_node *cpunode)
|
||||
*/
|
||||
if (low_freq < 98000000)
|
||||
low_freq = 101000000;
|
||||
|
||||
|
||||
/* Convert those to CPU core clocks */
|
||||
low_freq = (low_freq * (*ratio)) / 2000;
|
||||
hi_freq = (hi_freq * (*ratio)) / 2000;
|
||||
@ -714,6 +714,7 @@ static int __init pmac_cpufreq_setup(void)
|
||||
|
||||
pmac_cpu_freqs[CPUFREQ_LOW].frequency = low_freq;
|
||||
pmac_cpu_freqs[CPUFREQ_HIGH].frequency = hi_freq;
|
||||
ppc_proc_freq = cur_freq * 1000ul;
|
||||
|
||||
printk(KERN_INFO "Registering PowerMac CPU frequency driver\n");
|
||||
printk(KERN_INFO "Low: %d Mhz, High: %d Mhz, Boot: %d Mhz\n",
|
323
arch/powerpc/platforms/powermac/cpufreq_64.c
Normal file
323
arch/powerpc/platforms/powermac/cpufreq_64.c
Normal file
@ -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");
|
@ -193,18 +193,6 @@ static void pmac_show_cpuinfo(struct seq_file *m)
|
||||
pmac_newworld ? "NewWorld" : "OldWorld");
|
||||
}
|
||||
|
||||
static void pmac_show_percpuinfo(struct seq_file *m, int i)
|
||||
{
|
||||
#ifdef CONFIG_CPU_FREQ_PMAC
|
||||
extern unsigned int pmac_get_one_cpufreq(int i);
|
||||
unsigned int freq = pmac_get_one_cpufreq(i);
|
||||
if (freq != 0) {
|
||||
seq_printf(m, "clock\t\t: %dMHz\n", freq/1000);
|
||||
return;
|
||||
}
|
||||
#endif /* CONFIG_CPU_FREQ_PMAC */
|
||||
}
|
||||
|
||||
#ifndef CONFIG_ADB_CUDA
|
||||
int find_via_cuda(void)
|
||||
{
|
||||
@ -767,7 +755,6 @@ struct machdep_calls __initdata pmac_md = {
|
||||
.setup_arch = pmac_setup_arch,
|
||||
.init_early = pmac_init_early,
|
||||
.show_cpuinfo = pmac_show_cpuinfo,
|
||||
.show_percpuinfo = pmac_show_percpuinfo,
|
||||
.init_IRQ = pmac_pic_init,
|
||||
.get_irq = mpic_get_irq, /* changed later */
|
||||
.pcibios_fixup = pmac_pcibios_fixup,
|
||||
|
@ -37,7 +37,6 @@
|
||||
#include <asm/io.h>
|
||||
#include <asm/prom.h>
|
||||
#include <asm/rtas.h>
|
||||
#include <asm/ppcdebug.h>
|
||||
#include <asm/iommu.h>
|
||||
#include <asm/pci-bridge.h>
|
||||
#include <asm/machdep.h>
|
||||
@ -47,6 +46,7 @@
|
||||
#include <asm/firmware.h>
|
||||
#include <asm/tce.h>
|
||||
#include <asm/ppc-pci.h>
|
||||
#include <asm/udbg.h>
|
||||
|
||||
#include "plpar_wrappers.h"
|
||||
|
||||
|
@ -31,13 +31,14 @@
|
||||
#include <asm/machdep.h>
|
||||
#include <asm/abs_addr.h>
|
||||
#include <asm/mmu_context.h>
|
||||
#include <asm/ppcdebug.h>
|
||||
#include <asm/iommu.h>
|
||||
#include <asm/tlbflush.h>
|
||||
#include <asm/tlb.h>
|
||||
#include <asm/prom.h>
|
||||
#include <asm/abs_addr.h>
|
||||
#include <asm/cputable.h>
|
||||
#include <asm/udbg.h>
|
||||
#include <asm/smp.h>
|
||||
|
||||
#include "plpar_wrappers.h"
|
||||
|
||||
|
@ -107,14 +107,4 @@ static inline long plpar_put_term_char(unsigned long termno, unsigned long len,
|
||||
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 */
|
||||
|
@ -48,7 +48,7 @@
|
||||
#include <asm/ptrace.h>
|
||||
#include <asm/machdep.h>
|
||||
#include <asm/rtas.h>
|
||||
#include <asm/ppcdebug.h>
|
||||
#include <asm/udbg.h>
|
||||
|
||||
static unsigned char ras_log_buf[RTAS_ERROR_LOG_MAX];
|
||||
static DEFINE_SPINLOCK(ras_log_buf_lock);
|
||||
|
@ -65,6 +65,7 @@
|
||||
#include <asm/ppc-pci.h>
|
||||
#include <asm/i8259.h>
|
||||
#include <asm/udbg.h>
|
||||
#include <asm/smp.h>
|
||||
|
||||
#include "plpar_wrappers.h"
|
||||
|
||||
@ -353,14 +354,15 @@ static void pSeries_mach_cpu_die(void)
|
||||
|
||||
static int pseries_set_dabr(unsigned long dabr)
|
||||
{
|
||||
if (firmware_has_feature(FW_FEATURE_XDABR)) {
|
||||
/* We want to catch accesses from kernel and userspace */
|
||||
return plpar_set_xdabr(dabr, H_DABRX_KERNEL | H_DABRX_USER);
|
||||
}
|
||||
|
||||
return plpar_set_dabr(dabr);
|
||||
return plpar_hcall_norets(H_SET_DABR, dabr);
|
||||
}
|
||||
|
||||
static int pseries_set_xdabr(unsigned long dabr)
|
||||
{
|
||||
/* We want to catch accesses from kernel and userspace */
|
||||
return plpar_hcall_norets(H_SET_XDABR, dabr,
|
||||
H_DABRX_KERNEL | H_DABRX_USER);
|
||||
}
|
||||
|
||||
/*
|
||||
* Early initialization. Relocation is on but do not reference unbolted pages
|
||||
@ -396,8 +398,10 @@ static void __init pSeries_init_early(void)
|
||||
DBG("Hello World !\n");
|
||||
}
|
||||
|
||||
if (firmware_has_feature(FW_FEATURE_XDABR | FW_FEATURE_DABR))
|
||||
if (firmware_has_feature(FW_FEATURE_DABR))
|
||||
ppc_md.set_dabr = pseries_set_dabr;
|
||||
else if (firmware_has_feature(FW_FEATURE_XDABR))
|
||||
ppc_md.set_dabr = pseries_set_xdabr;
|
||||
|
||||
iommu_init_early_pSeries();
|
||||
|
||||
|
@ -207,6 +207,9 @@ void __init i8259_init(unsigned long intack_addr, int offset)
|
||||
|
||||
spin_unlock_irqrestore(&i8259_lock, flags);
|
||||
|
||||
for (i = 0; i < NUM_ISA_INTERRUPTS; ++i)
|
||||
irq_desc[offset + i].handler = &i8259_pic;
|
||||
|
||||
/* reserve our resources */
|
||||
setup_irq(offset + 2, &i8259_irqaction);
|
||||
request_resource(&ioport_resource, &pic1_iores);
|
||||
@ -216,6 +219,4 @@ void __init i8259_init(unsigned long intack_addr, int offset)
|
||||
if (intack_addr != 0)
|
||||
pci_intack = ioremap(intack_addr, 1);
|
||||
|
||||
for (i = 0; i < NUM_ISA_INTERRUPTS; ++i)
|
||||
irq_desc[offset + i].handler = &i8259_pic;
|
||||
}
|
||||
|
@ -37,7 +37,6 @@
|
||||
#include <linux/vmalloc.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/prom.h>
|
||||
#include <asm/ppcdebug.h>
|
||||
#include <asm/iommu.h>
|
||||
#include <asm/pci-bridge.h>
|
||||
#include <asm/machdep.h>
|
||||
|
@ -25,6 +25,11 @@
|
||||
#include <asm/thread_info.h>
|
||||
#include <asm/asm-offsets.h>
|
||||
|
||||
#ifdef CONFIG_8xx
|
||||
#define ISYNC_8xx isync
|
||||
#else
|
||||
#define ISYNC_8xx
|
||||
#endif
|
||||
.text
|
||||
|
||||
.align 5
|
||||
@ -800,8 +805,18 @@ _GLOBAL(_insb)
|
||||
subi r4,r4,1
|
||||
blelr-
|
||||
00: lbz r5,0(r3)
|
||||
eieio
|
||||
stbu r5,1(r4)
|
||||
01: eieio
|
||||
02: stbu r5,1(r4)
|
||||
ISYNC_8xx
|
||||
.section .fixup,"ax"
|
||||
03: blr
|
||||
.text
|
||||
.section __ex_table, "a"
|
||||
.align 2
|
||||
.long 00b, 03b
|
||||
.long 01b, 03b
|
||||
.long 02b, 03b
|
||||
.text
|
||||
bdnz 00b
|
||||
blr
|
||||
|
||||
@ -811,8 +826,18 @@ _GLOBAL(_outsb)
|
||||
subi r4,r4,1
|
||||
blelr-
|
||||
00: lbzu r5,1(r4)
|
||||
stb r5,0(r3)
|
||||
eieio
|
||||
01: stb r5,0(r3)
|
||||
02: eieio
|
||||
ISYNC_8xx
|
||||
.section .fixup,"ax"
|
||||
03: blr
|
||||
.text
|
||||
.section __ex_table, "a"
|
||||
.align 2
|
||||
.long 00b, 03b
|
||||
.long 01b, 03b
|
||||
.long 02b, 03b
|
||||
.text
|
||||
bdnz 00b
|
||||
blr
|
||||
|
||||
@ -822,8 +847,18 @@ _GLOBAL(_insw)
|
||||
subi r4,r4,2
|
||||
blelr-
|
||||
00: lhbrx r5,0,r3
|
||||
eieio
|
||||
sthu r5,2(r4)
|
||||
01: eieio
|
||||
02: sthu r5,2(r4)
|
||||
ISYNC_8xx
|
||||
.section .fixup,"ax"
|
||||
03: blr
|
||||
.text
|
||||
.section __ex_table, "a"
|
||||
.align 2
|
||||
.long 00b, 03b
|
||||
.long 01b, 03b
|
||||
.long 02b, 03b
|
||||
.text
|
||||
bdnz 00b
|
||||
blr
|
||||
|
||||
@ -833,8 +868,18 @@ _GLOBAL(_outsw)
|
||||
subi r4,r4,2
|
||||
blelr-
|
||||
00: lhzu r5,2(r4)
|
||||
eieio
|
||||
sthbrx r5,0,r3
|
||||
01: eieio
|
||||
02: sthbrx r5,0,r3
|
||||
ISYNC_8xx
|
||||
.section .fixup,"ax"
|
||||
03: blr
|
||||
.text
|
||||
.section __ex_table, "a"
|
||||
.align 2
|
||||
.long 00b, 03b
|
||||
.long 01b, 03b
|
||||
.long 02b, 03b
|
||||
.text
|
||||
bdnz 00b
|
||||
blr
|
||||
|
||||
@ -844,8 +889,18 @@ _GLOBAL(_insl)
|
||||
subi r4,r4,4
|
||||
blelr-
|
||||
00: lwbrx r5,0,r3
|
||||
eieio
|
||||
stwu r5,4(r4)
|
||||
01: eieio
|
||||
02: stwu r5,4(r4)
|
||||
ISYNC_8xx
|
||||
.section .fixup,"ax"
|
||||
03: blr
|
||||
.text
|
||||
.section __ex_table, "a"
|
||||
.align 2
|
||||
.long 00b, 03b
|
||||
.long 01b, 03b
|
||||
.long 02b, 03b
|
||||
.text
|
||||
bdnz 00b
|
||||
blr
|
||||
|
||||
@ -855,8 +910,18 @@ _GLOBAL(_outsl)
|
||||
subi r4,r4,4
|
||||
blelr-
|
||||
00: lwzu r5,4(r4)
|
||||
stwbrx r5,0,r3
|
||||
eieio
|
||||
01: stwbrx r5,0,r3
|
||||
02: eieio
|
||||
ISYNC_8xx
|
||||
.section .fixup,"ax"
|
||||
03: blr
|
||||
.text
|
||||
.section __ex_table, "a"
|
||||
.align 2
|
||||
.long 00b, 03b
|
||||
.long 01b, 03b
|
||||
.long 02b, 03b
|
||||
.text
|
||||
bdnz 00b
|
||||
blr
|
||||
|
||||
@ -867,8 +932,18 @@ _GLOBAL(_insw_ns)
|
||||
subi r4,r4,2
|
||||
blelr-
|
||||
00: lhz r5,0(r3)
|
||||
eieio
|
||||
sthu r5,2(r4)
|
||||
01: eieio
|
||||
02: sthu r5,2(r4)
|
||||
ISYNC_8xx
|
||||
.section .fixup,"ax"
|
||||
03: blr
|
||||
.text
|
||||
.section __ex_table, "a"
|
||||
.align 2
|
||||
.long 00b, 03b
|
||||
.long 01b, 03b
|
||||
.long 02b, 03b
|
||||
.text
|
||||
bdnz 00b
|
||||
blr
|
||||
|
||||
@ -879,8 +954,18 @@ _GLOBAL(_outsw_ns)
|
||||
subi r4,r4,2
|
||||
blelr-
|
||||
00: lhzu r5,2(r4)
|
||||
sth r5,0(r3)
|
||||
eieio
|
||||
01: sth r5,0(r3)
|
||||
02: eieio
|
||||
ISYNC_8xx
|
||||
.section .fixup,"ax"
|
||||
03: blr
|
||||
.text
|
||||
.section __ex_table, "a"
|
||||
.align 2
|
||||
.long 00b, 03b
|
||||
.long 01b, 03b
|
||||
.long 02b, 03b
|
||||
.text
|
||||
bdnz 00b
|
||||
blr
|
||||
|
||||
@ -891,8 +976,18 @@ _GLOBAL(_insl_ns)
|
||||
subi r4,r4,4
|
||||
blelr-
|
||||
00: lwz r5,0(r3)
|
||||
eieio
|
||||
stwu r5,4(r4)
|
||||
01: eieio
|
||||
02: stwu r5,4(r4)
|
||||
ISYNC_8xx
|
||||
.section .fixup,"ax"
|
||||
03: blr
|
||||
.text
|
||||
.section __ex_table, "a"
|
||||
.align 2
|
||||
.long 00b, 03b
|
||||
.long 01b, 03b
|
||||
.long 02b, 03b
|
||||
.text
|
||||
bdnz 00b
|
||||
blr
|
||||
|
||||
@ -903,8 +998,18 @@ _GLOBAL(_outsl_ns)
|
||||
subi r4,r4,4
|
||||
blelr-
|
||||
00: lwzu r5,4(r4)
|
||||
stw r5,0(r3)
|
||||
eieio
|
||||
01: stw r5,0(r3)
|
||||
02: eieio
|
||||
ISYNC_8xx
|
||||
.section .fixup,"ax"
|
||||
03: blr
|
||||
.text
|
||||
.section __ex_table, "a"
|
||||
.align 2
|
||||
.long 00b, 03b
|
||||
.long 01b, 03b
|
||||
.long 02b, 03b
|
||||
.text
|
||||
bdnz 00b
|
||||
blr
|
||||
|
||||
|
@ -49,7 +49,7 @@ extern int xmon_sstep(struct pt_regs *regs);
|
||||
extern int xmon_iabr_match(struct pt_regs *regs);
|
||||
extern int xmon_dabr_match(struct pt_regs *regs);
|
||||
|
||||
void (*debugger)(struct pt_regs *regs) = xmon;
|
||||
int (*debugger)(struct pt_regs *regs) = xmon;
|
||||
int (*debugger_bpt)(struct pt_regs *regs) = xmon_bpt;
|
||||
int (*debugger_sstep)(struct pt_regs *regs) = xmon_sstep;
|
||||
int (*debugger_iabr_match)(struct pt_regs *regs) = xmon_iabr_match;
|
||||
@ -57,7 +57,7 @@ int (*debugger_dabr_match)(struct pt_regs *regs) = xmon_dabr_match;
|
||||
void (*debugger_fault_handler)(struct pt_regs *regs);
|
||||
#else
|
||||
#ifdef CONFIG_KGDB
|
||||
void (*debugger)(struct pt_regs *regs);
|
||||
int (*debugger)(struct pt_regs *regs);
|
||||
int (*debugger_bpt)(struct pt_regs *regs);
|
||||
int (*debugger_sstep)(struct pt_regs *regs);
|
||||
int (*debugger_iabr_match)(struct pt_regs *regs);
|
||||
@ -159,7 +159,7 @@ void _exception(int signr, struct pt_regs *regs, int code, unsigned long addr)
|
||||
*/
|
||||
static inline int check_io_access(struct pt_regs *regs)
|
||||
{
|
||||
#ifdef CONFIG_PPC_PMAC
|
||||
#if defined CONFIG_PPC_PMAC || defined CONFIG_8xx
|
||||
unsigned long msr = regs->msr;
|
||||
const struct exception_table_entry *entry;
|
||||
unsigned int *nip = (unsigned int *)regs->nip;
|
||||
@ -178,7 +178,11 @@ static inline int check_io_access(struct pt_regs *regs)
|
||||
nip -= 2;
|
||||
else if (*nip == 0x4c00012c) /* isync */
|
||||
--nip;
|
||||
if (*nip == 0x7c0004ac || (*nip >> 26) == 3) {
|
||||
/* eieio from I/O string functions */
|
||||
else if ((*nip) == 0x7c0006ac || *(nip+1) == 0x7c0006ac)
|
||||
nip += 2;
|
||||
if (*nip == 0x7c0004ac || (*nip >> 26) == 3 ||
|
||||
(*(nip+1) >> 26) == 3) {
|
||||
/* sync or twi */
|
||||
unsigned int rb;
|
||||
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include <linux/irq.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/sched.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/8xx_immap.h>
|
||||
#include <syslib/m8xx_wdt.h>
|
||||
|
||||
@ -29,8 +30,8 @@ void m8xx_wdt_reset(void)
|
||||
{
|
||||
volatile immap_t *imap = (volatile immap_t *)IMAP_ADDR;
|
||||
|
||||
out_be16(imap->im_siu_conf.sc_swsr, 0x556c); /* write magic1 */
|
||||
out_be16(imap->im_siu_conf.sc_swsr, 0xaa39); /* write magic2 */
|
||||
out_be16(&imap->im_siu_conf.sc_swsr, 0x556c); /* write magic1 */
|
||||
out_be16(&imap->im_siu_conf.sc_swsr, 0xaa39); /* write magic2 */
|
||||
}
|
||||
|
||||
static irqreturn_t m8xx_wdt_interrupt(int irq, void *dev, struct pt_regs *regs)
|
||||
@ -39,7 +40,7 @@ static irqreturn_t m8xx_wdt_interrupt(int irq, void *dev, struct pt_regs *regs)
|
||||
|
||||
m8xx_wdt_reset();
|
||||
|
||||
out_be16(imap->im_sit.sit_piscr, in_be16(imap->im_sit.sit_piscr | PISCR_PS)); /* clear irq */
|
||||
out_be16(&imap->im_sit.sit_piscr, in_be16(&imap->im_sit.sit_piscr) | PISCR_PS); /* clear irq */
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
@ -51,7 +52,7 @@ void __init m8xx_wdt_handler_install(bd_t * binfo)
|
||||
u32 sypcr;
|
||||
u32 pitrtclk;
|
||||
|
||||
sypcr = in_be32(imap->im_siu_conf.sc_sypcr);
|
||||
sypcr = in_be32(&imap->im_siu_conf.sc_sypcr);
|
||||
|
||||
if (!(sypcr & 0x04)) {
|
||||
printk(KERN_NOTICE "m8xx_wdt: wdt disabled (SYPCR: 0x%08X)\n",
|
||||
@ -87,9 +88,9 @@ void __init m8xx_wdt_handler_install(bd_t * binfo)
|
||||
else
|
||||
pitc = pitrtclk * wdt_timeout / binfo->bi_intfreq / 2;
|
||||
|
||||
out_be32(imap->im_sit.sit_pitc, pitc << 16);
|
||||
out_be32(&imap->im_sit.sit_pitc, pitc << 16);
|
||||
|
||||
out_be16(imap->im_sit.sit_piscr, (mk_int_int_mask(PIT_INTERRUPT) << 8) | PISCR_PIE | PISCR_PTE);
|
||||
out_be16(&imap->im_sit.sit_piscr, (mk_int_int_mask(PIT_INTERRUPT) << 8) | PISCR_PIE | PISCR_PTE);
|
||||
|
||||
if (setup_irq(PIT_INTERRUPT, &m8xx_wdt_irqaction))
|
||||
panic("m8xx_wdt: error setting up the watchdog irq!");
|
||||
|
@ -1165,7 +1165,7 @@ get_property(struct device_node *np, const char *name, int *lenp)
|
||||
/*
|
||||
* Add a property to a node
|
||||
*/
|
||||
void
|
||||
int
|
||||
prom_add_property(struct device_node* np, struct property* prop)
|
||||
{
|
||||
struct property **next = &np->properties;
|
||||
@ -1174,6 +1174,8 @@ prom_add_property(struct device_node* np, struct property* prop)
|
||||
while (*next)
|
||||
next = &(*next)->next;
|
||||
*next = prop;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* I quickly hacked that one, check against spec ! */
|
||||
|
@ -220,8 +220,7 @@ static void get_tb(unsigned *p)
|
||||
p[1] = lo;
|
||||
}
|
||||
|
||||
void
|
||||
xmon(struct pt_regs *excp)
|
||||
int xmon(struct pt_regs *excp)
|
||||
{
|
||||
struct pt_regs regs;
|
||||
int msr, cmd;
|
||||
@ -290,6 +289,8 @@ xmon(struct pt_regs *excp)
|
||||
#endif /* CONFIG_SMP */
|
||||
set_msr(msr); /* restore interrupt enable */
|
||||
get_tb(start_tb[smp_processor_id()]);
|
||||
|
||||
return cmd != 'X';
|
||||
}
|
||||
|
||||
irqreturn_t
|
||||
|
@ -56,6 +56,7 @@ config PPC_STD_MMU
|
||||
# max order + 1
|
||||
config FORCE_MAX_ZONEORDER
|
||||
int
|
||||
default "9" if PPC_64K_PAGES
|
||||
default "13"
|
||||
|
||||
source "init/Kconfig"
|
||||
@ -173,6 +174,16 @@ config KEXEC
|
||||
support. As of this writing the exact hardware interface is
|
||||
strongly in flux, so no good recommendation can be made.
|
||||
|
||||
source "drivers/cpufreq/Kconfig"
|
||||
|
||||
config CPU_FREQ_PMAC64
|
||||
bool "Support for some Apple G5s"
|
||||
depends on CPU_FREQ && PMAC_SMU && PPC64
|
||||
select CPU_FREQ_TABLE
|
||||
help
|
||||
This adds support for frequency switching on Apple iMac G5,
|
||||
and some of the more recent desktop G5 machines as well.
|
||||
|
||||
config IBMVIO
|
||||
depends on PPC_PSERIES || PPC_ISERIES
|
||||
bool
|
||||
|
@ -55,10 +55,6 @@ config XMON_DEFAULT
|
||||
xmon is normally disabled unless booted with 'xmon=on'.
|
||||
Use 'xmon=off' to disable xmon init during runtime.
|
||||
|
||||
config PPCDBG
|
||||
bool "Include PPCDBG realtime debugging"
|
||||
depends on DEBUG_KERNEL
|
||||
|
||||
config IRQSTACKS
|
||||
bool "Use separate kernel stacks when processing interrupts"
|
||||
help
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include <asm/time.h>
|
||||
#include <asm/systemcfg.h>
|
||||
#include <asm/machdep.h>
|
||||
#include <asm/smp.h>
|
||||
|
||||
extern void power4_idle(void);
|
||||
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include <asm/mmu.h>
|
||||
#include <asm/sections.h> /* _end */
|
||||
#include <asm/prom.h>
|
||||
#include <asm/smp.h>
|
||||
|
||||
#define HASH_GROUP_SIZE 0x80 /* size of each hash group, asm/mmu.h */
|
||||
|
||||
|
@ -560,7 +560,7 @@ _GLOBAL(real_readb)
|
||||
isync
|
||||
blr
|
||||
|
||||
/*
|
||||
/*
|
||||
* Do an IO access in real mode
|
||||
*/
|
||||
_GLOBAL(real_writeb)
|
||||
@ -592,6 +592,76 @@ _GLOBAL(real_writeb)
|
||||
blr
|
||||
#endif /* defined(CONFIG_PPC_PMAC) || defined(CONFIG_PPC_MAPLE) */
|
||||
|
||||
/*
|
||||
* SCOM access functions for 970 (FX only for now)
|
||||
*
|
||||
* unsigned long scom970_read(unsigned int address);
|
||||
* void scom970_write(unsigned int address, unsigned long value);
|
||||
*
|
||||
* The address passed in is the 24 bits register address. This code
|
||||
* is 970 specific and will not check the status bits, so you should
|
||||
* know what you are doing.
|
||||
*/
|
||||
_GLOBAL(scom970_read)
|
||||
/* interrupts off */
|
||||
mfmsr r4
|
||||
ori r0,r4,MSR_EE
|
||||
xori r0,r0,MSR_EE
|
||||
mtmsrd r0,1
|
||||
|
||||
/* rotate 24 bits SCOM address 8 bits left and mask out it's low 8 bits
|
||||
* (including parity). On current CPUs they must be 0'd,
|
||||
* and finally or in RW bit
|
||||
*/
|
||||
rlwinm r3,r3,8,0,15
|
||||
ori r3,r3,0x8000
|
||||
|
||||
/* do the actual scom read */
|
||||
sync
|
||||
mtspr SPRN_SCOMC,r3
|
||||
isync
|
||||
mfspr r3,SPRN_SCOMD
|
||||
isync
|
||||
mfspr r0,SPRN_SCOMC
|
||||
isync
|
||||
|
||||
/* XXX: fixup result on some buggy 970's (ouch ! we lost a bit, bah
|
||||
* that's the best we can do). Not implemented yet as we don't use
|
||||
* the scom on any of the bogus CPUs yet, but may have to be done
|
||||
* ultimately
|
||||
*/
|
||||
|
||||
/* restore interrupts */
|
||||
mtmsrd r4,1
|
||||
blr
|
||||
|
||||
|
||||
_GLOBAL(scom970_write)
|
||||
/* interrupts off */
|
||||
mfmsr r5
|
||||
ori r0,r5,MSR_EE
|
||||
xori r0,r0,MSR_EE
|
||||
mtmsrd r0,1
|
||||
|
||||
/* rotate 24 bits SCOM address 8 bits left and mask out it's low 8 bits
|
||||
* (including parity). On current CPUs they must be 0'd.
|
||||
*/
|
||||
|
||||
rlwinm r3,r3,8,0,15
|
||||
|
||||
sync
|
||||
mtspr SPRN_SCOMD,r4 /* write data */
|
||||
isync
|
||||
mtspr SPRN_SCOMC,r3 /* write command */
|
||||
isync
|
||||
mfspr 3,SPRN_SCOMC
|
||||
isync
|
||||
|
||||
/* restore interrupts */
|
||||
mtmsrd r5,1
|
||||
blr
|
||||
|
||||
|
||||
/*
|
||||
* Create a kernel thread
|
||||
* kernel_thread(fn, arg, flags)
|
||||
|
@ -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;
|
||||
const char *type;
|
||||
@ -354,10 +354,9 @@ static struct pci_dev *of_create_pci_dev(struct device_node *node,
|
||||
|
||||
return dev;
|
||||
}
|
||||
EXPORT_SYMBOL(of_create_pci_dev);
|
||||
|
||||
static void of_scan_pci_bridge(struct device_node *node, struct pci_dev *dev);
|
||||
|
||||
static void __devinit of_scan_bus(struct device_node *node,
|
||||
void __devinit of_scan_bus(struct device_node *node,
|
||||
struct pci_bus *bus)
|
||||
{
|
||||
struct device_node *child = NULL;
|
||||
@ -381,9 +380,10 @@ static void __devinit of_scan_bus(struct device_node *node,
|
||||
|
||||
do_bus_setup(bus);
|
||||
}
|
||||
EXPORT_SYMBOL(of_scan_bus);
|
||||
|
||||
static void __devinit of_scan_pci_bridge(struct device_node *node,
|
||||
struct pci_dev *dev)
|
||||
void __devinit of_scan_pci_bridge(struct device_node *node,
|
||||
struct pci_dev *dev)
|
||||
{
|
||||
struct pci_bus *bus;
|
||||
u32 *busrange, *ranges;
|
||||
@ -464,9 +464,10 @@ static void __devinit of_scan_pci_bridge(struct device_node *node,
|
||||
else if (mode == PCI_PROBE_NORMAL)
|
||||
pci_scan_child_bus(bus);
|
||||
}
|
||||
EXPORT_SYMBOL(of_scan_pci_bridge);
|
||||
#endif /* CONFIG_PPC_MULTIPLATFORM */
|
||||
|
||||
static void __devinit scan_phb(struct pci_controller *hose)
|
||||
void __devinit scan_phb(struct pci_controller *hose)
|
||||
{
|
||||
struct pci_bus *bus;
|
||||
struct device_node *node = hose->arch_data;
|
||||
|
@ -31,6 +31,7 @@
|
||||
#include <linux/initrd.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <asm/prom.h>
|
||||
#include <asm/rtas.h>
|
||||
@ -46,7 +47,6 @@
|
||||
#include <asm/pgtable.h>
|
||||
#include <asm/pci.h>
|
||||
#include <asm/iommu.h>
|
||||
#include <asm/ppcdebug.h>
|
||||
#include <asm/btext.h>
|
||||
#include <asm/sections.h>
|
||||
#include <asm/machdep.h>
|
||||
@ -1866,17 +1866,32 @@ get_property(struct device_node *np, const char *name, int *lenp)
|
||||
EXPORT_SYMBOL(get_property);
|
||||
|
||||
/*
|
||||
* Add a property to a node
|
||||
* Add a property to a node.
|
||||
*/
|
||||
void
|
||||
int
|
||||
prom_add_property(struct device_node* np, struct property* prop)
|
||||
{
|
||||
struct property **next = &np->properties;
|
||||
struct property **next;
|
||||
|
||||
prop->next = NULL;
|
||||
while (*next)
|
||||
write_lock(&devtree_lock);
|
||||
next = &np->properties;
|
||||
while (*next) {
|
||||
if (strcmp(prop->name, (*next)->name) == 0) {
|
||||
/* duplicate ! don't insert it */
|
||||
write_unlock(&devtree_lock);
|
||||
return -1;
|
||||
}
|
||||
next = &(*next)->next;
|
||||
}
|
||||
*next = prop;
|
||||
write_unlock(&devtree_lock);
|
||||
|
||||
/* try to add to proc as well if it was initialized */
|
||||
if (np->pde)
|
||||
proc_device_tree_add_prop(np->pde, prop);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
@ -44,7 +44,6 @@
|
||||
#include <asm/pgtable.h>
|
||||
#include <asm/pci.h>
|
||||
#include <asm/iommu.h>
|
||||
#include <asm/ppcdebug.h>
|
||||
#include <asm/btext.h>
|
||||
#include <asm/sections.h>
|
||||
#include <asm/machdep.h>
|
||||
@ -1825,7 +1824,7 @@ static void __init fixup_device_tree(void)
|
||||
if (prom_getprop(u3, "device-rev", &u3_rev, sizeof(u3_rev))
|
||||
== PROM_ERROR)
|
||||
return;
|
||||
if (u3_rev != 0x35 && u3_rev != 0x37)
|
||||
if (u3_rev < 0x35 || u3_rev > 0x39)
|
||||
return;
|
||||
/* does it need fixup ? */
|
||||
if (prom_getproplen(i2c, "interrupts") > 0)
|
||||
|
@ -440,7 +440,6 @@ struct pci_controller * __devinit init_phb_dynamic(struct device_node *dn)
|
||||
struct device_node *root = of_find_node_by_path("/");
|
||||
unsigned int root_size_cells = 0;
|
||||
struct pci_controller *phb;
|
||||
struct pci_bus *bus;
|
||||
int primary;
|
||||
|
||||
root_size_cells = prom_n_size_cells(root);
|
||||
@ -456,10 +455,7 @@ struct pci_controller * __devinit init_phb_dynamic(struct device_node *dn)
|
||||
of_node_put(root);
|
||||
|
||||
pci_devs_phb_init_dynamic(phb);
|
||||
phb->last_busno = 0xff;
|
||||
bus = pci_scan_bus(phb->first_busno, phb->ops, phb->arch_data);
|
||||
phb->bus = bus;
|
||||
phb->last_busno = bus->subordinate;
|
||||
scan_phb(phb);
|
||||
|
||||
return phb;
|
||||
}
|
||||
|
@ -10,12 +10,10 @@
|
||||
*/
|
||||
|
||||
#include <stdarg.h>
|
||||
#define WANT_PPCDBG_TAB /* Only defined here */
|
||||
#include <linux/config.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/console.h>
|
||||
#include <asm/ppcdebug.h>
|
||||
#include <asm/processor.h>
|
||||
|
||||
void (*udbg_putc)(unsigned char c);
|
||||
@ -89,59 +87,6 @@ void udbg_printf(const char *fmt, ...)
|
||||
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
|
||||
*/
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include <linux/devfs_fs_kernel.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/dbdma.h>
|
||||
#include <asm/prom.h>
|
||||
@ -176,6 +177,7 @@ struct swim3 {
|
||||
|
||||
struct floppy_state {
|
||||
enum swim_state state;
|
||||
spinlock_t lock;
|
||||
struct swim3 __iomem *swim3; /* hardware registers */
|
||||
struct dbdma_regs __iomem *dma; /* DMA controller registers */
|
||||
int swim3_intr; /* interrupt number for SWIM3 */
|
||||
@ -304,7 +306,6 @@ static void do_fd_request(request_queue_t * q)
|
||||
#endif /* CONFIG_PMAC_MEDIABAY */
|
||||
start_request(&floppy_states[i]);
|
||||
}
|
||||
sti();
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
save_flags(flags); cli();
|
||||
spin_lock_irqsave(&fs->lock, flags);
|
||||
if (fs->timeout_pending)
|
||||
del_timer(&fs->timeout);
|
||||
fs->timeout.expires = jiffies + nticks;
|
||||
@ -378,7 +379,7 @@ static void set_timeout(struct floppy_state *fs, int nticks,
|
||||
fs->timeout.data = (unsigned long) fs;
|
||||
add_timer(&fs->timeout);
|
||||
fs->timeout_pending = 1;
|
||||
restore_flags(flags);
|
||||
spin_unlock_irqrestore(&fs->lock, flags);
|
||||
}
|
||||
|
||||
static inline void scan_track(struct floppy_state *fs)
|
||||
@ -790,14 +791,13 @@ static int grab_drive(struct floppy_state *fs, enum swim_state state,
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
save_flags(flags);
|
||||
cli();
|
||||
spin_lock_irqsave(&fs->lock, flags);
|
||||
if (fs->state != idle) {
|
||||
++fs->wanted;
|
||||
while (fs->state != available) {
|
||||
if (interruptible && signal_pending(current)) {
|
||||
--fs->wanted;
|
||||
restore_flags(flags);
|
||||
spin_unlock_irqrestore(&fs->lock, flags);
|
||||
return -EINTR;
|
||||
}
|
||||
interruptible_sleep_on(&fs->wait);
|
||||
@ -805,7 +805,7 @@ static int grab_drive(struct floppy_state *fs, enum swim_state state,
|
||||
--fs->wanted;
|
||||
}
|
||||
fs->state = state;
|
||||
restore_flags(flags);
|
||||
spin_unlock_irqrestore(&fs->lock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -813,11 +813,10 @@ static void release_drive(struct floppy_state *fs)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
save_flags(flags);
|
||||
cli();
|
||||
spin_lock_irqsave(&fs->lock, flags);
|
||||
fs->state = idle;
|
||||
start_request(fs);
|
||||
restore_flags(flags);
|
||||
spin_unlock_irqrestore(&fs->lock, flags);
|
||||
}
|
||||
|
||||
static int fd_eject(struct floppy_state *fs)
|
||||
@ -1109,6 +1108,7 @@ static int swim3_add_device(struct device_node *swim)
|
||||
pmac_call_feature(PMAC_FTR_SWIM3_ENABLE, swim, 0, 1);
|
||||
|
||||
memset(fs, 0, sizeof(*fs));
|
||||
spin_lock_init(&fs->lock);
|
||||
fs->state = idle;
|
||||
fs->swim3 = (struct swim3 __iomem *)
|
||||
ioremap(swim->addrs[0].address, 0x200);
|
||||
|
@ -497,16 +497,19 @@ pmu_hd_blink_init(void)
|
||||
if (pmu_get_model() != PMU_KEYLARGO_BASED)
|
||||
return 0;
|
||||
|
||||
dt = find_devices("device-tree");
|
||||
dt = of_find_node_by_path("/");
|
||||
if (dt == NULL)
|
||||
return 0;
|
||||
model = (const char *)get_property(dt, "model", NULL);
|
||||
if (model == NULL)
|
||||
return 0;
|
||||
if (strncmp(model, "PowerBook", strlen("PowerBook")) != 0 &&
|
||||
strncmp(model, "iBook", strlen("iBook")) != 0)
|
||||
strncmp(model, "iBook", strlen("iBook")) != 0) {
|
||||
of_node_put(dt);
|
||||
return 0;
|
||||
|
||||
}
|
||||
of_node_put(dt);
|
||||
|
||||
pmu_blink_on.complete = 1;
|
||||
pmu_blink_off.complete = 1;
|
||||
spin_lock_init(&pmu_blink_lock);
|
||||
|
@ -169,6 +169,25 @@ config THERM_PM72
|
||||
This driver provides thermostat and fan control for the desktop
|
||||
G5 machines.
|
||||
|
||||
config WINDFARM
|
||||
tristate "New PowerMac thermal control infrastructure"
|
||||
|
||||
config WINDFARM_PM81
|
||||
tristate "Support for thermal management on iMac G5"
|
||||
depends on WINDFARM && I2C && CPU_FREQ_PMAC64 && PMAC_SMU
|
||||
select I2C_PMAC_SMU
|
||||
help
|
||||
This driver provides thermal control for the iMacG5
|
||||
|
||||
config WINDFARM_PM91
|
||||
tristate "Support for thermal management on PowerMac9,1"
|
||||
depends on WINDFARM && I2C && CPU_FREQ_PMAC64 && PMAC_SMU
|
||||
select I2C_PMAC_SMU
|
||||
help
|
||||
This driver provides thermal control for the PowerMac9,1
|
||||
which is the recent (SMU based) single CPU desktop G5
|
||||
|
||||
|
||||
config ANSLCD
|
||||
tristate "Support for ANS LCD display"
|
||||
depends on ADB_CUDA && PPC_PMAC
|
||||
|
@ -26,3 +26,12 @@ obj-$(CONFIG_ADB_MACIO) += macio-adb.o
|
||||
obj-$(CONFIG_THERM_PM72) += therm_pm72.o
|
||||
obj-$(CONFIG_THERM_WINDTUNNEL) += therm_windtunnel.o
|
||||
obj-$(CONFIG_THERM_ADT746X) += therm_adt746x.o
|
||||
obj-$(CONFIG_WINDFARM) += windfarm_core.o
|
||||
obj-$(CONFIG_WINDFARM_PM81) += windfarm_smu_controls.o \
|
||||
windfarm_smu_sensors.o \
|
||||
windfarm_lm75_sensor.o windfarm_pid.o \
|
||||
windfarm_cpufreq_clamp.o windfarm_pm81.o
|
||||
obj-$(CONFIG_WINDFARM_PM91) += windfarm_smu_controls.o \
|
||||
windfarm_smu_sensors.o \
|
||||
windfarm_lm75_sensor.o windfarm_pid.o \
|
||||
windfarm_cpufreq_clamp.o windfarm_pm91.o
|
||||
|
@ -47,13 +47,13 @@
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/of_device.h>
|
||||
|
||||
#define VERSION "0.6"
|
||||
#define VERSION "0.7"
|
||||
#define AUTHOR "(c) 2005 Benjamin Herrenschmidt, IBM Corp."
|
||||
|
||||
#undef DEBUG_SMU
|
||||
|
||||
#ifdef DEBUG_SMU
|
||||
#define DPRINTK(fmt, args...) do { printk(KERN_DEBUG fmt , ##args); } while (0)
|
||||
#define DPRINTK(fmt, args...) do { udbg_printf(KERN_DEBUG fmt , ##args); } while (0)
|
||||
#else
|
||||
#define DPRINTK(fmt, args...) do { } while (0)
|
||||
#endif
|
||||
@ -92,7 +92,7 @@ struct smu_device {
|
||||
* for now, just hard code that
|
||||
*/
|
||||
static struct smu_device *smu;
|
||||
|
||||
static DECLARE_MUTEX(smu_part_access);
|
||||
|
||||
/*
|
||||
* SMU driver low level stuff
|
||||
@ -113,9 +113,11 @@ static void smu_start_cmd(void)
|
||||
|
||||
DPRINTK("SMU: starting cmd %x, %d bytes data\n", cmd->cmd,
|
||||
cmd->data_len);
|
||||
DPRINTK("SMU: data buffer: %02x %02x %02x %02x ...\n",
|
||||
DPRINTK("SMU: data buffer: %02x %02x %02x %02x %02x %02x %02x %02x\n",
|
||||
((u8 *)cmd->data_buf)[0], ((u8 *)cmd->data_buf)[1],
|
||||
((u8 *)cmd->data_buf)[2], ((u8 *)cmd->data_buf)[3]);
|
||||
((u8 *)cmd->data_buf)[2], ((u8 *)cmd->data_buf)[3],
|
||||
((u8 *)cmd->data_buf)[4], ((u8 *)cmd->data_buf)[5],
|
||||
((u8 *)cmd->data_buf)[6], ((u8 *)cmd->data_buf)[7]);
|
||||
|
||||
/* Fill the SMU command buffer */
|
||||
smu->cmd_buf->cmd = cmd->cmd;
|
||||
@ -440,7 +442,7 @@ int smu_present(void)
|
||||
EXPORT_SYMBOL(smu_present);
|
||||
|
||||
|
||||
int smu_init (void)
|
||||
int __init smu_init (void)
|
||||
{
|
||||
struct device_node *np;
|
||||
u32 *data;
|
||||
@ -588,6 +590,8 @@ static void smu_expose_childs(void *unused)
|
||||
sprintf(name, "smu-i2c-%02x", *reg);
|
||||
of_platform_device_create(np, name, &smu->of_dev->dev);
|
||||
}
|
||||
if (device_is_compatible(np, "smu-sensors"))
|
||||
of_platform_device_create(np, "smu-sensors", &smu->of_dev->dev);
|
||||
}
|
||||
|
||||
}
|
||||
@ -845,6 +849,156 @@ int smu_queue_i2c(struct smu_i2c_cmd *cmd)
|
||||
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 = ∁
|
||||
params[0] = SMU_CMD_MISC_ee_GET_DATABLOCK_REC;
|
||||
params[1] = 0x4;
|
||||
*((u32 *)¶ms[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) {
|
||||
pp->mode = smu_file_events;
|
||||
return 0;
|
||||
} else if (hdr.cmdtype == SMU_CMDTYPE_GET_PARTITION) {
|
||||
struct smu_sdbp_header *part;
|
||||
part = __smu_get_sdb_partition(hdr.cmd, NULL, 1);
|
||||
if (part == NULL)
|
||||
return -EINVAL;
|
||||
else if (IS_ERR(part))
|
||||
return PTR_ERR(part);
|
||||
return 0;
|
||||
} else if (hdr.cmdtype != SMU_CMDTYPE_SMU)
|
||||
return -EINVAL;
|
||||
else if (pp->mode != smu_file_commands)
|
||||
|
@ -2053,6 +2053,7 @@ pmu_register_sleep_notifier(struct pmu_sleep_notifier *n)
|
||||
__list_add(&n->list, list->prev, list);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(pmu_register_sleep_notifier);
|
||||
|
||||
int
|
||||
pmu_unregister_sleep_notifier(struct pmu_sleep_notifier* n)
|
||||
@ -2063,6 +2064,7 @@ pmu_unregister_sleep_notifier(struct pmu_sleep_notifier* n)
|
||||
n->list.next = NULL;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(pmu_unregister_sleep_notifier);
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
#if defined(CONFIG_PM) && defined(CONFIG_PPC32)
|
||||
@ -2667,10 +2669,10 @@ powerbook_sleep_3400(void)
|
||||
asleep = 1;
|
||||
|
||||
/* Put the CPU into sleep mode */
|
||||
asm volatile("mfspr %0,1008" : "=r" (hid0) :);
|
||||
hid0 = mfspr(SPRN_HID0);
|
||||
hid0 = (hid0 & ~(HID0_NAP | HID0_DOZE)) | HID0_SLEEP;
|
||||
asm volatile("mtspr 1008,%0" : : "r" (hid0));
|
||||
_nmask_and_or_msr(0, MSR_POW | MSR_EE);
|
||||
mtspr(SPRN_HID0, hid0);
|
||||
mtmsr(mfmsr() | MSR_POW | MSR_EE);
|
||||
udelay(10);
|
||||
|
||||
/* OK, we're awake again, start restoring things */
|
||||
@ -3139,8 +3141,6 @@ EXPORT_SYMBOL(pmu_i2c_stdsub_write);
|
||||
EXPORT_SYMBOL(pmu_i2c_simple_read);
|
||||
EXPORT_SYMBOL(pmu_i2c_simple_write);
|
||||
#if defined(CONFIG_PM) && defined(CONFIG_PPC32)
|
||||
EXPORT_SYMBOL(pmu_register_sleep_notifier);
|
||||
EXPORT_SYMBOL(pmu_unregister_sleep_notifier);
|
||||
EXPORT_SYMBOL(pmu_enable_irled);
|
||||
EXPORT_SYMBOL(pmu_battery_count);
|
||||
EXPORT_SYMBOL(pmu_batteries);
|
||||
|
131
drivers/macintosh/windfarm.h
Normal file
131
drivers/macintosh/windfarm.h
Normal file
@ -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
drivers/macintosh/windfarm_core.c
Normal file
426
drivers/macintosh/windfarm_core.c
Normal file
@ -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
drivers/macintosh/windfarm_cpufreq_clamp.c
Normal file
105
drivers/macintosh/windfarm_cpufreq_clamp.c
Normal file
@ -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
drivers/macintosh/windfarm_lm75_sensor.c
Normal file
263
drivers/macintosh/windfarm_lm75_sensor.c
Normal file
@ -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
drivers/macintosh/windfarm_pid.c
Normal file
145
drivers/macintosh/windfarm_pid.c
Normal file
@ -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
drivers/macintosh/windfarm_pid.h
Normal file
84
drivers/macintosh/windfarm_pid.h
Normal file
@ -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
drivers/macintosh/windfarm_pm81.c
Normal file
879
drivers/macintosh/windfarm_pm81.c
Normal file
@ -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
drivers/macintosh/windfarm_pm91.c
Normal file
814
drivers/macintosh/windfarm_pm91.c
Normal file
@ -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, ¶m);
|
||||
|
||||
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, ¶m);
|
||||
|
||||
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
drivers/macintosh/windfarm_smu_controls.c
Normal file
282
drivers/macintosh/windfarm_smu_controls.c
Normal file
@ -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 = ∁
|
||||
|
||||
/* 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
drivers/macintosh/windfarm_smu_sensors.c
Normal file
479
drivers/macintosh/windfarm_smu_sensors.c
Normal file
@ -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, &s);
|
||||
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");
|
||||
|
@ -48,6 +48,39 @@ static int property_read_proc(char *page, char **start, off_t off,
|
||||
* 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.
|
||||
*/
|
||||
@ -57,11 +90,9 @@ void proc_device_tree_add_node(struct device_node *np,
|
||||
struct property *pp;
|
||||
struct proc_dir_entry *ent;
|
||||
struct device_node *child;
|
||||
struct proc_dir_entry *list = NULL, **lastp;
|
||||
const char *p;
|
||||
|
||||
set_node_proc_entry(np, de);
|
||||
lastp = &list;
|
||||
for (child = NULL; (child = of_get_next_child(np, child));) {
|
||||
p = strrchr(child->full_name, '/');
|
||||
if (!p)
|
||||
@ -71,9 +102,6 @@ void proc_device_tree_add_node(struct device_node *np,
|
||||
ent = proc_mkdir(p, de);
|
||||
if (ent == 0)
|
||||
break;
|
||||
*lastp = ent;
|
||||
ent->next = NULL;
|
||||
lastp = &ent->next;
|
||||
proc_device_tree_add_node(child, ent);
|
||||
}
|
||||
of_node_put(child);
|
||||
@ -84,7 +112,7 @@ void proc_device_tree_add_node(struct device_node *np,
|
||||
* properties are quite unimportant for us though, thus we
|
||||
* simply "skip" them here, but we do have to check.
|
||||
*/
|
||||
for (ent = list; ent != NULL; ent = ent->next)
|
||||
for (ent = de->subdir; ent != NULL; ent = ent->next)
|
||||
if (!strcmp(ent->name, pp->name))
|
||||
break;
|
||||
if (ent != NULL) {
|
||||
@ -94,25 +122,10 @@ void proc_device_tree_add_node(struct device_node *np,
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Unfortunately proc_register puts each new entry
|
||||
* at the beginning of the list. So we rearrange them.
|
||||
*/
|
||||
ent = create_proc_read_entry(pp->name,
|
||||
strncmp(pp->name, "security-", 9)
|
||||
? S_IRUGO : S_IRUSR, de,
|
||||
property_read_proc, pp);
|
||||
ent = __proc_device_tree_add_prop(de, pp);
|
||||
if (ent == 0)
|
||||
break;
|
||||
if (!strncmp(pp->name, "security-", 9))
|
||||
ent->size = 0; /* don't leak number of password chars */
|
||||
else
|
||||
ent->size = pp->length;
|
||||
ent->next = NULL;
|
||||
*lastp = ent;
|
||||
lastp = &ent->next;
|
||||
}
|
||||
de->subdir = list;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1,24 +1,27 @@
|
||||
/*
|
||||
* linux/include/asm-ppc/ide.h
|
||||
* Copyright (C) 1994-1996 Linus Torvalds & authors
|
||||
*
|
||||
* Copyright (C) 1994-1996 Linus Torvalds & authors */
|
||||
|
||||
/*
|
||||
* This file contains the ppc architecture specific IDE code.
|
||||
* This file contains the powerpc architecture specific IDE code.
|
||||
*/
|
||||
|
||||
#ifndef __ASMPPC_IDE_H
|
||||
#define __ASMPPC_IDE_H
|
||||
#ifndef _ASM_POWERPC_IDE_H
|
||||
#define _ASM_POWERPC_IDE_H
|
||||
|
||||
#ifdef __KERNEL__
|
||||
|
||||
#ifndef __powerpc64__
|
||||
#include <linux/sched.h>
|
||||
#include <asm/mpc8xx.h>
|
||||
|
||||
#ifndef MAX_HWIFS
|
||||
#define MAX_HWIFS 8
|
||||
#endif
|
||||
|
||||
#ifndef MAX_HWIFS
|
||||
#ifdef __powerpc64__
|
||||
#define MAX_HWIFS 10
|
||||
#else
|
||||
#define MAX_HWIFS 8
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef __powerpc64__
|
||||
#include <linux/config.h>
|
||||
#include <linux/hdreg.h>
|
||||
#include <linux/ioport.h>
|
||||
@ -59,9 +62,6 @@ static __inline__ unsigned long ide_default_io_base(int index)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define IDE_ARCH_OBSOLETE_INIT
|
||||
#define ide_default_io_ctl(base) ((base) + 0x206) /* obsolete */
|
||||
|
||||
#ifdef CONFIG_PCI
|
||||
#define ide_init_default_irq(base) (0)
|
||||
#else
|
||||
@ -73,6 +73,11 @@ 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)
|
||||
#endif
|
||||
|
||||
#endif /* __powerpc64__ */
|
||||
|
||||
#define IDE_ARCH_OBSOLETE_INIT
|
||||
#define ide_default_io_ctl(base) ((base) + 0x206) /* obsolete */
|
||||
|
||||
#endif /* __KERNEL__ */
|
||||
|
||||
#endif /* __ASMPPC_IDE_H */
|
||||
#endif /* _ASM_POWERPC_IDE_H */
|
@ -82,7 +82,6 @@ struct machdep_calls {
|
||||
void (*iommu_dev_setup)(struct pci_dev *dev);
|
||||
void (*iommu_bus_setup)(struct pci_bus *bus);
|
||||
void (*irq_bus_setup)(struct pci_bus *bus);
|
||||
int (*set_dabr)(unsigned long dabr);
|
||||
#endif
|
||||
|
||||
int (*probe)(int platform);
|
||||
@ -158,6 +157,9 @@ struct machdep_calls {
|
||||
platform, called once per cpu. */
|
||||
void (*enable_pmcs)(void);
|
||||
|
||||
/* Set DABR for this platform, leave empty for default implemenation */
|
||||
int (*set_dabr)(unsigned long dabr);
|
||||
|
||||
#ifdef CONFIG_PPC32 /* XXX for now */
|
||||
/* A general init function, called by ppc_init in init/main.c.
|
||||
May be NULL. */
|
||||
|
@ -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_dynamic(struct pci_controller *phb);
|
||||
void __devinit scan_phb(struct pci_controller *hose);
|
||||
|
||||
/* PCI address cache management routines */
|
||||
void pci_addr_cache_insert_device(struct pci_dev *dev);
|
||||
|
@ -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_intr_cells(struct device_node* np);
|
||||
extern void prom_get_irq_senses(unsigned char *senses, int off, int max);
|
||||
extern void prom_add_property(struct device_node* np, struct property* prop);
|
||||
extern int prom_add_property(struct device_node* np, struct property* prop);
|
||||
|
||||
#ifdef CONFIG_PPC32
|
||||
/*
|
||||
|
@ -396,6 +396,9 @@
|
||||
#define SPRN_VRSAVE 0x100 /* Vector Register Save Register */
|
||||
#define SPRN_XER 0x001 /* Fixed Point Exception Register */
|
||||
|
||||
#define SPRN_SCOMC 0x114 /* SCOM Access Control */
|
||||
#define SPRN_SCOMD 0x115 /* SCOM Access DATA */
|
||||
|
||||
/* Performance monitor SPRs */
|
||||
#ifdef CONFIG_PPC64
|
||||
#define SPRN_MMCR0 795
|
||||
@ -594,7 +597,11 @@ static inline void ppc64_runlatch_off(void)
|
||||
mtspr(SPRN_CTRLT, ctrl);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
extern unsigned long scom970_read(unsigned int address);
|
||||
extern void scom970_write(unsigned int address, unsigned long value);
|
||||
|
||||
#endif /* CONFIG_PPC64 */
|
||||
|
||||
#define __get_SP() ({unsigned long sp; \
|
||||
asm volatile("mr %0,1": "=r" (sp)); sp;})
|
||||
|
@ -86,7 +86,6 @@ extern void __cpu_die(unsigned int cpu);
|
||||
#else
|
||||
/* for UP */
|
||||
#define smp_setup_cpu_maps()
|
||||
#define smp_release_cpus()
|
||||
|
||||
#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 set_hard_smp_processor_id(CPU, VAL) \
|
||||
do { (paca[(CPU)].hw_cpu_id = (VAL)); } while (0)
|
||||
|
||||
extern void smp_release_cpus(void);
|
||||
|
||||
#else
|
||||
/* 32-bit */
|
||||
#ifndef CONFIG_SMP
|
||||
|
@ -20,16 +20,52 @@
|
||||
/*
|
||||
* Partition info commands
|
||||
*
|
||||
* I do not know what those are for at this point
|
||||
* These commands are used to retreive the sdb-partition-XX datas from
|
||||
* the SMU. The lenght is always 2. First byte is the subcommand code
|
||||
* and second byte is the partition ID.
|
||||
*
|
||||
* The reply is 6 bytes:
|
||||
*
|
||||
* - 0..1 : partition address
|
||||
* - 2 : a byte containing the partition ID
|
||||
* - 3 : length (maybe other bits are rest of header ?)
|
||||
*
|
||||
* The data must then be obtained with calls to another command:
|
||||
* SMU_CMD_MISC_ee_GET_DATABLOCK_REC (described below).
|
||||
*/
|
||||
#define SMU_CMD_PARTITION_COMMAND 0x3e
|
||||
#define SMU_CMD_PARTITION_LATEST 0x01
|
||||
#define SMU_CMD_PARTITION_BASE 0x02
|
||||
#define SMU_CMD_PARTITION_UPDATE 0x03
|
||||
|
||||
|
||||
/*
|
||||
* Fan control
|
||||
*
|
||||
* This is a "mux" for fan control commands, first byte is the
|
||||
* "sub" command.
|
||||
* This is a "mux" for fan control commands. The command seem to
|
||||
* act differently based on the number of arguments. With 1 byte
|
||||
* of argument, this seem to be queries for fans status, setpoint,
|
||||
* etc..., while with 0xe arguments, we will set the fans speeds.
|
||||
*
|
||||
* Queries (1 byte arg):
|
||||
* ---------------------
|
||||
*
|
||||
* arg=0x01: read RPM fans status
|
||||
* arg=0x02: read RPM fans setpoint
|
||||
* arg=0x11: read PWM fans status
|
||||
* arg=0x12: read PWM fans setpoint
|
||||
*
|
||||
* the "status" queries return the current speed while the "setpoint" ones
|
||||
* return the programmed/target speed. It _seems_ that the result is a bit
|
||||
* mask in the first byte of active/available fans, followed by 6 words (16
|
||||
* bits) containing the requested speed.
|
||||
*
|
||||
* Setpoint (14 bytes arg):
|
||||
* ------------------------
|
||||
*
|
||||
* first arg byte is 0 for RPM fans and 0x10 for PWM. Second arg byte is the
|
||||
* mask of fans affected by the command. Followed by 6 words containing the
|
||||
* setpoint value for selected fans in the mask (or 0 if mask value is 0)
|
||||
*/
|
||||
#define SMU_CMD_FAN_COMMAND 0x4a
|
||||
|
||||
@ -144,7 +180,11 @@
|
||||
* - lenght 8 ("VSLEWxyz") has 3 additional bytes appended, and is
|
||||
* used to set the voltage slewing point. The SMU replies with "DONE"
|
||||
* I yet have to figure out their exact meaning of those 3 bytes in
|
||||
* both cases.
|
||||
* both cases. They seem to be:
|
||||
* x = processor mask
|
||||
* y = op. point index
|
||||
* z = processor freq. step index
|
||||
* I haven't yet decyphered result codes
|
||||
*
|
||||
*/
|
||||
#define SMU_CMD_POWER_COMMAND 0xaa
|
||||
@ -152,6 +192,14 @@
|
||||
#define SMU_CMD_POWER_SHUTDOWN "SHUTDOWN"
|
||||
#define SMU_CMD_POWER_VOLTAGE_SLEW "VSLEW"
|
||||
|
||||
/*
|
||||
* Read ADC sensors
|
||||
*
|
||||
* This command takes one byte of parameter: the sensor ID (or "reg"
|
||||
* value in the device-tree) and returns a 16 bits value
|
||||
*/
|
||||
#define SMU_CMD_READ_ADC 0xd8
|
||||
|
||||
/* Misc commands
|
||||
*
|
||||
* This command seem to be a grab bag of various things
|
||||
@ -172,6 +220,25 @@
|
||||
* Misc commands
|
||||
*
|
||||
* This command seem to be a grab bag of various things
|
||||
*
|
||||
* SMU_CMD_MISC_ee_GET_DATABLOCK_REC is used, among others, to
|
||||
* transfer blocks of data from the SMU. So far, I've decrypted it's
|
||||
* usage to retreive partition data. In order to do that, you have to
|
||||
* break your transfer in "chunks" since that command cannot transfer
|
||||
* more than a chunk at a time. The chunk size used by OF is 0xe bytes,
|
||||
* but it seems that the darwin driver will let you do 0x1e bytes if
|
||||
* your "PMU" version is >= 0x30. You can get the "PMU" version apparently
|
||||
* either in the last 16 bits of property "smu-version-pmu" or as the 16
|
||||
* bytes at offset 1 of "smu-version-info"
|
||||
*
|
||||
* For each chunk, the command takes 7 bytes of arguments:
|
||||
* byte 0: subcommand code (0x02)
|
||||
* byte 1: 0x04 (always, I don't know what it means, maybe the address
|
||||
* space to use or some other nicety. It's hard coded in OF)
|
||||
* byte 2..5: SMU address of the chunk (big endian 32 bits)
|
||||
* byte 6: size to transfer (up to max chunk size)
|
||||
*
|
||||
* The data is returned directly
|
||||
*/
|
||||
#define SMU_CMD_MISC_ee_COMMAND 0xee
|
||||
#define SMU_CMD_MISC_ee_GET_DATABLOCK_REC 0x02
|
||||
@ -333,6 +400,128 @@ extern int smu_queue_i2c(struct smu_i2c_cmd *cmd);
|
||||
|
||||
#endif /* __KERNEL__ */
|
||||
|
||||
|
||||
/*
|
||||
* - SMU "sdb" partitions informations -
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* Partition header format
|
||||
*/
|
||||
struct smu_sdbp_header {
|
||||
__u8 id;
|
||||
__u8 len;
|
||||
__u8 version;
|
||||
__u8 flags;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* demangle 16 and 32 bits integer in some SMU partitions
|
||||
* (currently, afaik, this concerns only the FVT partition
|
||||
* (0x12)
|
||||
*/
|
||||
#define SMU_U16_MIX(x) le16_to_cpu(x);
|
||||
#define SMU_U32_MIX(x) ((((x) & 0xff00ff00u) >> 8)|(((x) & 0x00ff00ffu) << 8))
|
||||
|
||||
|
||||
/* This is the definition of the SMU sdb-partition-0x12 table (called
|
||||
* CPU F/V/T operating points in Darwin). The definition for all those
|
||||
* SMU tables should be moved to some separate file
|
||||
*/
|
||||
#define SMU_SDB_FVT_ID 0x12
|
||||
|
||||
struct smu_sdbp_fvt {
|
||||
__u32 sysclk; /* Base SysClk frequency in Hz for
|
||||
* this operating point. Value need to
|
||||
* be unmixed with SMU_U32_MIX()
|
||||
*/
|
||||
__u8 pad;
|
||||
__u8 maxtemp; /* Max temp. supported by this
|
||||
* operating point
|
||||
*/
|
||||
|
||||
__u16 volts[3]; /* CPU core voltage for the 3
|
||||
* PowerTune modes, a mode with
|
||||
* 0V = not supported. Value need
|
||||
* to be unmixed with SMU_U16_MIX()
|
||||
*/
|
||||
};
|
||||
|
||||
/* This partition contains voltage & current sensor calibration
|
||||
* informations
|
||||
*/
|
||||
#define SMU_SDB_CPUVCP_ID 0x21
|
||||
|
||||
struct smu_sdbp_cpuvcp {
|
||||
__u16 volt_scale; /* u4.12 fixed point */
|
||||
__s16 volt_offset; /* s4.12 fixed point */
|
||||
__u16 curr_scale; /* u4.12 fixed point */
|
||||
__s16 curr_offset; /* s4.12 fixed point */
|
||||
__s32 power_quads[3]; /* s4.28 fixed point */
|
||||
};
|
||||
|
||||
/* This partition contains CPU thermal diode calibration
|
||||
*/
|
||||
#define SMU_SDB_CPUDIODE_ID 0x18
|
||||
|
||||
struct smu_sdbp_cpudiode {
|
||||
__u16 m_value; /* u1.15 fixed point */
|
||||
__s16 b_value; /* s10.6 fixed point */
|
||||
|
||||
};
|
||||
|
||||
/* This partition contains Slots power calibration
|
||||
*/
|
||||
#define SMU_SDB_SLOTSPOW_ID 0x78
|
||||
|
||||
struct smu_sdbp_slotspow {
|
||||
__u16 pow_scale; /* u4.12 fixed point */
|
||||
__s16 pow_offset; /* s4.12 fixed point */
|
||||
};
|
||||
|
||||
/* This partition contains machine specific version information about
|
||||
* the sensor/control layout
|
||||
*/
|
||||
#define SMU_SDB_SENSORTREE_ID 0x25
|
||||
|
||||
struct smu_sdbp_sensortree {
|
||||
u8 model_id;
|
||||
u8 unknown[3];
|
||||
};
|
||||
|
||||
/* This partition contains CPU thermal control PID informations. So far
|
||||
* only single CPU machines have been seen with an SMU, so we assume this
|
||||
* carries only informations for those
|
||||
*/
|
||||
#define SMU_SDB_CPUPIDDATA_ID 0x17
|
||||
|
||||
struct smu_sdbp_cpupiddata {
|
||||
u8 unknown1;
|
||||
u8 target_temp_delta;
|
||||
u8 unknown2;
|
||||
u8 history_len;
|
||||
s16 power_adj;
|
||||
u16 max_power;
|
||||
s32 gp,gr,gd;
|
||||
};
|
||||
|
||||
|
||||
/* Other partitions without known structures */
|
||||
#define SMU_SDB_DEBUG_SWITCHES_ID 0x05
|
||||
|
||||
#ifdef __KERNEL__
|
||||
/*
|
||||
* This returns the pointer to an SMU "sdb" partition data or NULL
|
||||
* if not found. The data format is described below
|
||||
*/
|
||||
extern struct smu_sdbp_header *smu_get_sdb_partition(int id,
|
||||
unsigned int *size);
|
||||
|
||||
#endif /* __KERNEL__ */
|
||||
|
||||
|
||||
/*
|
||||
* - Userland interface -
|
||||
*/
|
||||
@ -365,8 +554,10 @@ struct smu_user_cmd_hdr
|
||||
__u32 cmdtype;
|
||||
#define SMU_CMDTYPE_SMU 0 /* SMU command */
|
||||
#define SMU_CMDTYPE_WANTS_EVENTS 1 /* switch fd to events mode */
|
||||
#define SMU_CMDTYPE_GET_PARTITION 2 /* retreive an sdb partition */
|
||||
|
||||
__u8 cmd; /* SMU command byte */
|
||||
__u8 pad[3]; /* padding */
|
||||
__u32 data_len; /* Lenght of data following */
|
||||
};
|
||||
|
||||
|
@ -7,6 +7,7 @@ struct pt_regs;
|
||||
extern int xmon(struct pt_regs *excp);
|
||||
extern void xmon_printf(const char *fmt, ...);
|
||||
extern void xmon_init(int);
|
||||
extern void xmon_map_scc(void);
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
@ -17,18 +17,18 @@ extern unsigned long disp_BAT[2];
|
||||
extern boot_infos_t disp_bi;
|
||||
extern int boot_text_mapped;
|
||||
|
||||
void btext_init(boot_infos_t *bi);
|
||||
void btext_welcome(void);
|
||||
void btext_prepare_BAT(void);
|
||||
void btext_setup_display(int width, int height, int depth, int pitch,
|
||||
unsigned long address);
|
||||
void map_boot_text(void);
|
||||
void btext_update_display(unsigned long phys, int width, int height,
|
||||
int depth, int pitch);
|
||||
extern void init_boot_display(void);
|
||||
extern void btext_welcome(void);
|
||||
extern void btext_prepare_BAT(void);
|
||||
extern void btext_setup_display(int width, int height, int depth, int pitch,
|
||||
unsigned long address);
|
||||
extern void map_boot_text(void);
|
||||
extern void btext_update_display(unsigned long phys, int width, int height,
|
||||
int depth, int pitch);
|
||||
|
||||
void btext_drawchar(char c);
|
||||
void btext_drawstring(const char *str);
|
||||
void btext_drawhex(unsigned long v);
|
||||
extern void btext_drawchar(char c);
|
||||
extern void btext_drawstring(const char *str);
|
||||
extern void btext_drawhex(unsigned long v);
|
||||
|
||||
#endif /* __KERNEL__ */
|
||||
#endif /* __PPC_BTEXT_H */
|
||||
|
@ -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))
|
||||
|
||||
/*
|
||||
* On powermacs, we will get a machine check exception if we
|
||||
* try to read data from a non-existent I/O port. Because the
|
||||
* machine check is an asynchronous exception, it isn't
|
||||
* On powermacs and 8xx we will get a machine check exception
|
||||
* if we try to read data from a non-existent I/O port. Because
|
||||
* the machine check is an asynchronous exception, it isn't
|
||||
* well-defined which instruction SRR0 will point to when the
|
||||
* exception occurs.
|
||||
* With the sequence below (twi; isync; nop), we have found that
|
||||
@ -258,7 +258,7 @@ extern __inline__ unsigned int name(unsigned int port) \
|
||||
{ \
|
||||
unsigned int x; \
|
||||
__asm__ __volatile__( \
|
||||
op " %0,0,%1\n" \
|
||||
"0:" op " %0,0,%1\n" \
|
||||
"1: twi 0,%0,0\n" \
|
||||
"2: isync\n" \
|
||||
"3: nop\n" \
|
||||
@ -269,6 +269,7 @@ extern __inline__ unsigned int name(unsigned int port) \
|
||||
".previous\n" \
|
||||
".section __ex_table,\"a\"\n" \
|
||||
" .align 2\n" \
|
||||
" .long 0b,5b\n" \
|
||||
" .long 1b,5b\n" \
|
||||
" .long 2b,5b\n" \
|
||||
" .long 3b,5b\n" \
|
||||
@ -282,11 +283,12 @@ extern __inline__ unsigned int name(unsigned int port) \
|
||||
extern __inline__ void name(unsigned int val, unsigned int port) \
|
||||
{ \
|
||||
__asm__ __volatile__( \
|
||||
op " %0,0,%1\n" \
|
||||
"0:" op " %0,0,%1\n" \
|
||||
"1: sync\n" \
|
||||
"2:\n" \
|
||||
".section __ex_table,\"a\"\n" \
|
||||
" .align 2\n" \
|
||||
" .long 0b,2b\n" \
|
||||
" .long 1b,2b\n" \
|
||||
".previous" \
|
||||
: : "r" (val), "r" (port + ___IO_BASE)); \
|
||||
|
@ -31,7 +31,7 @@ extern void breakpoint(void);
|
||||
/* For taking exceptions
|
||||
* these are defined in traps.c
|
||||
*/
|
||||
extern void (*debugger)(struct pt_regs *regs);
|
||||
extern int (*debugger)(struct pt_regs *regs);
|
||||
extern int (*debugger_bpt)(struct pt_regs *regs);
|
||||
extern int (*debugger_sstep)(struct pt_regs *regs);
|
||||
extern int (*debugger_iabr_match)(struct pt_regs *regs);
|
||||
|
@ -93,7 +93,7 @@ extern int device_is_compatible(struct device_node *device, const char *);
|
||||
extern int machine_is_compatible(const char *compat);
|
||||
extern unsigned char *get_property(struct device_node *node, const char *name,
|
||||
int *lenp);
|
||||
extern void prom_add_property(struct device_node* np, struct property* prop);
|
||||
extern int prom_add_property(struct device_node* np, struct property* prop);
|
||||
extern void prom_get_irq_senses(unsigned char *, int, int);
|
||||
extern int prom_n_addr_cells(struct device_node* np);
|
||||
extern int prom_n_size_cells(struct device_node* np);
|
||||
|
@ -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 */
|
@ -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_dev *of_create_pci_dev(struct device_node *node,
|
||||
struct pci_bus *bus, int devfn);
|
||||
|
||||
extern void of_scan_pci_bridge(struct device_node *node,
|
||||
struct pci_dev *dev);
|
||||
|
||||
extern void of_scan_bus(struct device_node *node, struct pci_bus *bus);
|
||||
|
||||
extern int pci_read_irq_line(struct pci_dev *dev);
|
||||
|
||||
extern void pcibios_add_platform_entries(struct pci_dev *dev);
|
||||
|
@ -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 */
|
@ -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_intr_cells(struct device_node* np);
|
||||
extern void prom_get_irq_senses(unsigned char *senses, int off, int max);
|
||||
extern void prom_add_property(struct device_node* np, struct property* prop);
|
||||
extern int prom_add_property(struct device_node* np, struct property* prop);
|
||||
|
||||
#endif /* _PPC64_PROM_H */
|
||||
|
@ -23,9 +23,6 @@ extern int udbg_read(char *buf, int buflen);
|
||||
|
||||
extern void register_early_udbg_console(void);
|
||||
extern void udbg_printf(const char *fmt, ...);
|
||||
extern void udbg_ppcdbg(unsigned long flags, const char *fmt, ...);
|
||||
extern unsigned long udbg_ifdebug(unsigned long flags);
|
||||
extern void __init ppcdbg_initialize(void);
|
||||
|
||||
extern void udbg_init_uart(void __iomem *comport, unsigned int speed);
|
||||
|
||||
|
@ -139,15 +139,12 @@ extern void proc_tty_unregister_driver(struct tty_driver *driver);
|
||||
/*
|
||||
* proc_devtree.c
|
||||
*/
|
||||
struct device_node;
|
||||
extern void proc_device_tree_init(void);
|
||||
#ifdef CONFIG_PROC_DEVICETREE
|
||||
struct device_node;
|
||||
struct property;
|
||||
extern void proc_device_tree_init(void);
|
||||
extern void proc_device_tree_add_node(struct device_node *, struct proc_dir_entry *);
|
||||
#else /* !CONFIG_PROC_DEVICETREE */
|
||||
static inline void proc_device_tree_add_node(struct device_node *np, struct proc_dir_entry *pde)
|
||||
{
|
||||
return;
|
||||
}
|
||||
extern void proc_device_tree_add_prop(struct proc_dir_entry *pde, struct property *prop);
|
||||
#endif /* CONFIG_PROC_DEVICETREE */
|
||||
|
||||
extern struct proc_dir_entry *proc_symlink(const char *,
|
||||
|
Loading…
Reference in New Issue
Block a user