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

This commit is contained in:
Linus Torvalds 2005-11-07 20:23:46 -08:00
commit f093182d31
88 changed files with 5140 additions and 585 deletions

View file

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

View file

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

View file

@ -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)

View file

@ -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;

View file

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

View file

@ -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)

View file

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

View file

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

View file

@ -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();

View file

@ -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);

View file

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

View file

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

View file

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

View file

@ -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;

View file

@ -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);

View file

@ -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)
{

View file

@ -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);
}

View file

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

View file

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

View file

@ -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;

View file

@ -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;

View file

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

View file

@ -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(),

View file

@ -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);
}
/*

View file

@ -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)

View file

@ -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)

View file

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

View file

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

View file

@ -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",

View 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");

View file

@ -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,

View file

@ -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"

View file

@ -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"

View file

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

View file

@ -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);

View file

@ -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();

View file

@ -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;
}

View file

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

View file

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

View file

@ -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;

View file

@ -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!");

View file

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

View file

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

View file

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

View file

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

View file

@ -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);

View file

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

View file

@ -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)

View file

@ -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;

View file

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

View file

@ -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)

View file

@ -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;
}

View file

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

View file

@ -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);

View file

@ -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);

View file

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

View file

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

View file

@ -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 = &comp;
params[0] = SMU_CMD_MISC_ee_GET_DATABLOCK_REC;
params[1] = 0x4;
*((u32 *)&params[2]) = addr;
params[6] = clen;
rc = smu_queue_cmd(&cmd);
if (rc)
return rc;
wait_for_completion(&comp);
if (cmd.status != 0)
return rc;
if (cmd.reply_len != clen) {
printk(KERN_DEBUG "SMU: short read in "
"smu_read_datablock, got: %d, want: %d\n",
cmd.reply_len, clen);
return -EIO;
}
len -= clen;
addr += clen;
dest += clen;
}
return 0;
}
static struct smu_sdbp_header *smu_create_sdb_partition(int id)
{
DECLARE_COMPLETION(comp);
struct smu_simple_cmd cmd;
unsigned int addr, len, tlen;
struct smu_sdbp_header *hdr;
struct property *prop;
/* First query the partition info */
smu_queue_simple(&cmd, SMU_CMD_PARTITION_COMMAND, 2,
smu_done_complete, &comp,
SMU_CMD_PARTITION_LATEST, id);
wait_for_completion(&comp);
/* Partition doesn't exist (or other error) */
if (cmd.cmd.status != 0 || cmd.cmd.reply_len != 6)
return NULL;
/* Fetch address and length from reply */
addr = *((u16 *)cmd.buffer);
len = cmd.buffer[3] << 2;
/* Calucluate total length to allocate, including the 17 bytes
* for "sdb-partition-XX" that we append at the end of the buffer
*/
tlen = sizeof(struct property) + len + 18;
prop = kcalloc(tlen, 1, GFP_KERNEL);
if (prop == NULL)
return NULL;
hdr = (struct smu_sdbp_header *)(prop + 1);
prop->name = ((char *)prop) + tlen - 18;
sprintf(prop->name, "sdb-partition-%02x", id);
prop->length = len;
prop->value = (unsigned char *)hdr;
prop->next = NULL;
/* Read the datablock */
if (smu_read_datablock((u8 *)hdr, addr, len)) {
printk(KERN_DEBUG "SMU: datablock read failed while reading "
"partition %02x !\n", id);
goto failure;
}
/* Got it, check a few things and create the property */
if (hdr->id != id) {
printk(KERN_DEBUG "SMU: Reading partition %02x and got "
"%02x !\n", id, hdr->id);
goto failure;
}
if (prom_add_property(smu->of_node, prop)) {
printk(KERN_DEBUG "SMU: Failed creating sdb-partition-%02x "
"property !\n", id);
goto failure;
}
return hdr;
failure:
kfree(prop);
return NULL;
}
/* Note: Only allowed to return error code in pointers (using ERR_PTR)
* when interruptible is 1
*/
struct smu_sdbp_header *__smu_get_sdb_partition(int id, unsigned int *size,
int interruptible)
{
char pname[32];
struct smu_sdbp_header *part;
if (!smu)
return NULL;
sprintf(pname, "sdb-partition-%02x", id);
if (interruptible) {
int rc;
rc = down_interruptible(&smu_part_access);
if (rc)
return ERR_PTR(rc);
} else
down(&smu_part_access);
part = (struct smu_sdbp_header *)get_property(smu->of_node,
pname, size);
if (part == NULL) {
part = smu_create_sdb_partition(id);
if (part != NULL && size)
*size = part->len << 2;
}
up(&smu_part_access);
return part;
}
struct smu_sdbp_header *smu_get_sdb_partition(int id, unsigned int *size)
{
return __smu_get_sdb_partition(id, size, 0);
}
EXPORT_SYMBOL(smu_get_sdb_partition);
/*
@ -918,6 +1072,14 @@ static ssize_t smu_write(struct file *file, const char __user *buf,
else if (hdr.cmdtype == SMU_CMDTYPE_WANTS_EVENTS) {
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)

View file

@ -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);

View 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__ */

View 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");

View 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");

View 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");

View 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);

View 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);

View 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");

View 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, &param);
DBG("wf: Drive Fan control initialized.\n");
DBG(" itarged=%d.%03d, min=%d RPM, max=%d RPM\n",
FIX32TOPRINT(param.itarget), param.min, param.max);
return;
fail:
if (fan_hd)
wf_control_set_max(fan_hd);
}
static void wf_smu_drive_fans_tick(struct wf_smu_drive_fans_state *st)
{
s32 new_setpoint, temp;
int rc;
if (--st->ticks != 0) {
if (wf_smu_readjust)
goto readjust;
return;
}
st->ticks = st->pid.param.interval;
rc = sensor_hd_temp->ops->get_value(sensor_hd_temp, &temp);
if (rc) {
printk(KERN_WARNING "windfarm: HD temp sensor error %d\n",
rc);
wf_smu_failure_state |= FAILURE_SENSOR;
return;
}
DBG("wf_smu: Drive Fans tick ! HD temp: %d.%03d\n",
FIX32TOPRINT(temp));
if (temp > (st->pid.param.itarget + 0x50000))
wf_smu_failure_state |= FAILURE_OVERTEMP;
new_setpoint = wf_pid_run(&st->pid, temp);
DBG("wf_smu: new_setpoint: %d\n", (int)new_setpoint);
if (st->setpoint == new_setpoint)
return;
st->setpoint = new_setpoint;
readjust:
if (fan_hd && wf_smu_failure_state == 0) {
rc = fan_hd->ops->set_value(fan_hd, st->setpoint);
if (rc) {
printk(KERN_WARNING "windfarm: HD fan error %d\n",
rc);
wf_smu_failure_state |= FAILURE_FAN;
}
}
}
static void wf_smu_create_slots_fans(void)
{
struct wf_pid_param param = {
.interval = 1,
.history_len = 8,
.gd = 0x00000000,
.gp = 0x00000000,
.gr = 0x00020000,
.itarget = 0x00000000
};
/* Alloc & initialize state */
wf_smu_slots_fans = kmalloc(sizeof(struct wf_smu_slots_fans_state),
GFP_KERNEL);
if (wf_smu_slots_fans == NULL) {
printk(KERN_WARNING "windfarm: Memory allocation error"
" max fan speed\n");
goto fail;
}
wf_smu_slots_fans->ticks = 1;
/* Fill PID params */
param.additive = (fan_slots->type == WF_CONTROL_RPM_FAN);
param.min = fan_slots->ops->get_min(fan_slots);
param.max = fan_slots->ops->get_max(fan_slots);
wf_pid_init(&wf_smu_slots_fans->pid, &param);
DBG("wf: Slots Fan control initialized.\n");
DBG(" itarged=%d.%03d, min=%d RPM, max=%d RPM\n",
FIX32TOPRINT(param.itarget), param.min, param.max);
return;
fail:
if (fan_slots)
wf_control_set_max(fan_slots);
}
static void wf_smu_slots_fans_tick(struct wf_smu_slots_fans_state *st)
{
s32 new_setpoint, power;
int rc;
if (--st->ticks != 0) {
if (wf_smu_readjust)
goto readjust;
return;
}
st->ticks = st->pid.param.interval;
rc = sensor_slots_power->ops->get_value(sensor_slots_power, &power);
if (rc) {
printk(KERN_WARNING "windfarm: Slots power sensor error %d\n",
rc);
wf_smu_failure_state |= FAILURE_SENSOR;
return;
}
DBG("wf_smu: Slots Fans tick ! Slots power: %d.%03d\n",
FIX32TOPRINT(power));
#if 0 /* Check what makes a good overtemp condition */
if (power > (st->pid.param.itarget + 0x50000))
wf_smu_failure_state |= FAILURE_OVERTEMP;
#endif
new_setpoint = wf_pid_run(&st->pid, power);
DBG("wf_smu: new_setpoint: %d\n", (int)new_setpoint);
if (st->setpoint == new_setpoint)
return;
st->setpoint = new_setpoint;
readjust:
if (fan_slots && wf_smu_failure_state == 0) {
rc = fan_slots->ops->set_value(fan_slots, st->setpoint);
if (rc) {
printk(KERN_WARNING "windfarm: Slots fan error %d\n",
rc);
wf_smu_failure_state |= FAILURE_FAN;
}
}
}
/*
* ****** Attributes ******
*
*/
#define BUILD_SHOW_FUNC_FIX(name, data) \
static ssize_t show_##name(struct device *dev, \
struct device_attribute *attr, \
char *buf) \
{ \
ssize_t r; \
s32 val = 0; \
data->ops->get_value(data, &val); \
r = sprintf(buf, "%d.%03d", FIX32TOPRINT(val)); \
return r; \
} \
static DEVICE_ATTR(name,S_IRUGO,show_##name, NULL);
#define BUILD_SHOW_FUNC_INT(name, data) \
static ssize_t show_##name(struct device *dev, \
struct device_attribute *attr, \
char *buf) \
{ \
s32 val = 0; \
data->ops->get_value(data, &val); \
return sprintf(buf, "%d", val); \
} \
static DEVICE_ATTR(name,S_IRUGO,show_##name, NULL);
BUILD_SHOW_FUNC_INT(cpu_fan, fan_cpu_main);
BUILD_SHOW_FUNC_INT(hd_fan, fan_hd);
BUILD_SHOW_FUNC_INT(slots_fan, fan_slots);
BUILD_SHOW_FUNC_FIX(cpu_temp, sensor_cpu_temp);
BUILD_SHOW_FUNC_FIX(cpu_power, sensor_cpu_power);
BUILD_SHOW_FUNC_FIX(hd_temp, sensor_hd_temp);
BUILD_SHOW_FUNC_FIX(slots_power, sensor_slots_power);
/*
* ****** Setup / Init / Misc ... ******
*
*/
static void wf_smu_tick(void)
{
unsigned int last_failure = wf_smu_failure_state;
unsigned int new_failure;
if (!wf_smu_started) {
DBG("wf: creating control loops !\n");
wf_smu_create_drive_fans();
wf_smu_create_slots_fans();
wf_smu_create_cpu_fans();
wf_smu_started = 1;
}
/* Skipping ticks */
if (wf_smu_skipping && --wf_smu_skipping)
return;
wf_smu_failure_state = 0;
if (wf_smu_drive_fans)
wf_smu_drive_fans_tick(wf_smu_drive_fans);
if (wf_smu_slots_fans)
wf_smu_slots_fans_tick(wf_smu_slots_fans);
if (wf_smu_cpu_fans)
wf_smu_cpu_fans_tick(wf_smu_cpu_fans);
wf_smu_readjust = 0;
new_failure = wf_smu_failure_state & ~last_failure;
/* If entering failure mode, clamp cpufreq and ramp all
* fans to full speed.
*/
if (wf_smu_failure_state && !last_failure) {
if (cpufreq_clamp)
wf_control_set_max(cpufreq_clamp);
if (fan_cpu_main)
wf_control_set_max(fan_cpu_main);
if (fan_cpu_second)
wf_control_set_max(fan_cpu_second);
if (fan_cpu_third)
wf_control_set_max(fan_cpu_third);
if (fan_hd)
wf_control_set_max(fan_hd);
if (fan_slots)
wf_control_set_max(fan_slots);
}
/* If leaving failure mode, unclamp cpufreq and readjust
* all fans on next iteration
*/
if (!wf_smu_failure_state && last_failure) {
if (cpufreq_clamp)
wf_control_set_min(cpufreq_clamp);
wf_smu_readjust = 1;
}
/* Overtemp condition detected, notify and start skipping a couple
* ticks to let the temperature go down
*/
if (new_failure & FAILURE_OVERTEMP) {
wf_set_overtemp();
wf_smu_skipping = 2;
}
/* We only clear the overtemp condition if overtemp is cleared
* _and_ no other failure is present. Since a sensor error will
* clear the overtemp condition (can't measure temperature) at
* the control loop levels, but we don't want to keep it clear
* here in this case
*/
if (new_failure == 0 && last_failure & FAILURE_OVERTEMP)
wf_clear_overtemp();
}
static void wf_smu_new_control(struct wf_control *ct)
{
if (wf_smu_all_controls_ok)
return;
if (fan_cpu_main == NULL && !strcmp(ct->name, "cpu-rear-fan-0")) {
if (wf_get_control(ct) == 0) {
fan_cpu_main = ct;
device_create_file(wf_smu_dev, &dev_attr_cpu_fan);
}
}
if (fan_cpu_second == NULL && !strcmp(ct->name, "cpu-rear-fan-1")) {
if (wf_get_control(ct) == 0)
fan_cpu_second = ct;
}
if (fan_cpu_third == NULL && !strcmp(ct->name, "cpu-front-fan-0")) {
if (wf_get_control(ct) == 0)
fan_cpu_third = ct;
}
if (cpufreq_clamp == NULL && !strcmp(ct->name, "cpufreq-clamp")) {
if (wf_get_control(ct) == 0)
cpufreq_clamp = ct;
}
if (fan_hd == NULL && !strcmp(ct->name, "drive-bay-fan")) {
if (wf_get_control(ct) == 0) {
fan_hd = ct;
device_create_file(wf_smu_dev, &dev_attr_hd_fan);
}
}
if (fan_slots == NULL && !strcmp(ct->name, "slots-fan")) {
if (wf_get_control(ct) == 0) {
fan_slots = ct;
device_create_file(wf_smu_dev, &dev_attr_slots_fan);
}
}
if (fan_cpu_main && (fan_cpu_second || fan_cpu_third) && fan_hd &&
fan_slots && cpufreq_clamp)
wf_smu_all_controls_ok = 1;
}
static void wf_smu_new_sensor(struct wf_sensor *sr)
{
if (wf_smu_all_sensors_ok)
return;
if (sensor_cpu_power == NULL && !strcmp(sr->name, "cpu-power")) {
if (wf_get_sensor(sr) == 0) {
sensor_cpu_power = sr;
device_create_file(wf_smu_dev, &dev_attr_cpu_power);
}
}
if (sensor_cpu_temp == NULL && !strcmp(sr->name, "cpu-temp")) {
if (wf_get_sensor(sr) == 0) {
sensor_cpu_temp = sr;
device_create_file(wf_smu_dev, &dev_attr_cpu_temp);
}
}
if (sensor_hd_temp == NULL && !strcmp(sr->name, "hd-temp")) {
if (wf_get_sensor(sr) == 0) {
sensor_hd_temp = sr;
device_create_file(wf_smu_dev, &dev_attr_hd_temp);
}
}
if (sensor_slots_power == NULL && !strcmp(sr->name, "slots-power")) {
if (wf_get_sensor(sr) == 0) {
sensor_slots_power = sr;
device_create_file(wf_smu_dev, &dev_attr_slots_power);
}
}
if (sensor_cpu_power && sensor_cpu_temp &&
sensor_hd_temp && sensor_slots_power)
wf_smu_all_sensors_ok = 1;
}
static int wf_smu_notify(struct notifier_block *self,
unsigned long event, void *data)
{
switch(event) {
case WF_EVENT_NEW_CONTROL:
DBG("wf: new control %s detected\n",
((struct wf_control *)data)->name);
wf_smu_new_control(data);
wf_smu_readjust = 1;
break;
case WF_EVENT_NEW_SENSOR:
DBG("wf: new sensor %s detected\n",
((struct wf_sensor *)data)->name);
wf_smu_new_sensor(data);
break;
case WF_EVENT_TICK:
if (wf_smu_all_controls_ok && wf_smu_all_sensors_ok)
wf_smu_tick();
}
return 0;
}
static struct notifier_block wf_smu_events = {
.notifier_call = wf_smu_notify,
};
static int wf_init_pm(void)
{
printk(KERN_INFO "windfarm: Initializing for Desktop G5 model\n");
return 0;
}
static int wf_smu_probe(struct device *ddev)
{
wf_smu_dev = ddev;
wf_register_client(&wf_smu_events);
return 0;
}
static int wf_smu_remove(struct device *ddev)
{
wf_unregister_client(&wf_smu_events);
/* XXX We don't have yet a guarantee that our callback isn't
* in progress when returning from wf_unregister_client, so
* we add an arbitrary delay. I'll have to fix that in the core
*/
msleep(1000);
/* Release all sensors */
/* One more crappy race: I don't think we have any guarantee here
* that the attribute callback won't race with the sensor beeing
* disposed of, and I'm not 100% certain what best way to deal
* with that except by adding locks all over... I'll do that
* eventually but heh, who ever rmmod this module anyway ?
*/
if (sensor_cpu_power) {
device_remove_file(wf_smu_dev, &dev_attr_cpu_power);
wf_put_sensor(sensor_cpu_power);
}
if (sensor_cpu_temp) {
device_remove_file(wf_smu_dev, &dev_attr_cpu_temp);
wf_put_sensor(sensor_cpu_temp);
}
if (sensor_hd_temp) {
device_remove_file(wf_smu_dev, &dev_attr_hd_temp);
wf_put_sensor(sensor_hd_temp);
}
if (sensor_slots_power) {
device_remove_file(wf_smu_dev, &dev_attr_slots_power);
wf_put_sensor(sensor_slots_power);
}
/* Release all controls */
if (fan_cpu_main) {
device_remove_file(wf_smu_dev, &dev_attr_cpu_fan);
wf_put_control(fan_cpu_main);
}
if (fan_cpu_second)
wf_put_control(fan_cpu_second);
if (fan_cpu_third)
wf_put_control(fan_cpu_third);
if (fan_hd) {
device_remove_file(wf_smu_dev, &dev_attr_hd_fan);
wf_put_control(fan_hd);
}
if (fan_slots) {
device_remove_file(wf_smu_dev, &dev_attr_slots_fan);
wf_put_control(fan_slots);
}
if (cpufreq_clamp)
wf_put_control(cpufreq_clamp);
/* Destroy control loops state structures */
if (wf_smu_slots_fans)
kfree(wf_smu_cpu_fans);
if (wf_smu_drive_fans)
kfree(wf_smu_cpu_fans);
if (wf_smu_cpu_fans)
kfree(wf_smu_cpu_fans);
wf_smu_dev = NULL;
return 0;
}
static struct device_driver wf_smu_driver = {
.name = "windfarm",
.bus = &platform_bus_type,
.probe = wf_smu_probe,
.remove = wf_smu_remove,
};
static int __init wf_smu_init(void)
{
int rc = -ENODEV;
if (machine_is_compatible("PowerMac9,1"))
rc = wf_init_pm();
if (rc == 0) {
#ifdef MODULE
request_module("windfarm_smu_controls");
request_module("windfarm_smu_sensors");
request_module("windfarm_lm75_sensor");
#endif /* MODULE */
driver_register(&wf_smu_driver);
}
return rc;
}
static void __exit wf_smu_exit(void)
{
driver_unregister(&wf_smu_driver);
}
module_init(wf_smu_init);
module_exit(wf_smu_exit);
MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
MODULE_DESCRIPTION("Thermal control logic for PowerMac9,1");
MODULE_LICENSE("GPL");

View 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 = &comp;
/* Fill argument buffer */
memset(buffer, 0, 16);
buffer[0] = pwm ? 0x10 : 0x00;
buffer[1] = 0x01 << id;
*((u16 *)&buffer[2 + id * 2]) = value;
rc = smu_queue_cmd(&cmd);
if (rc)
return rc;
wait_for_completion(&comp);
return cmd.status;
}
static void smu_fan_release(struct wf_control *ct)
{
struct smu_fan_control *fct = to_smu_fan(ct);
kfree(fct);
}
static int smu_fan_set(struct wf_control *ct, s32 value)
{
struct smu_fan_control *fct = to_smu_fan(ct);
if (value < fct->min)
value = fct->min;
if (value > fct->max)
value = fct->max;
fct->value = value;
return smu_set_fan(fct->fan_type, fct->reg, value);
}
static int smu_fan_get(struct wf_control *ct, s32 *value)
{
struct smu_fan_control *fct = to_smu_fan(ct);
*value = fct->value; /* todo: read from SMU */
return 0;
}
static s32 smu_fan_min(struct wf_control *ct)
{
struct smu_fan_control *fct = to_smu_fan(ct);
return fct->min;
}
static s32 smu_fan_max(struct wf_control *ct)
{
struct smu_fan_control *fct = to_smu_fan(ct);
return fct->max;
}
static struct wf_control_ops smu_fan_ops = {
.set_value = smu_fan_set,
.get_value = smu_fan_get,
.get_min = smu_fan_min,
.get_max = smu_fan_max,
.release = smu_fan_release,
.owner = THIS_MODULE,
};
static struct smu_fan_control *smu_fan_create(struct device_node *node,
int pwm_fan)
{
struct smu_fan_control *fct;
s32 *v; u32 *reg;
char *l;
fct = kmalloc(sizeof(struct smu_fan_control), GFP_KERNEL);
if (fct == NULL)
return NULL;
fct->ctrl.ops = &smu_fan_ops;
l = (char *)get_property(node, "location", NULL);
if (l == NULL)
goto fail;
fct->fan_type = pwm_fan;
fct->ctrl.type = pwm_fan ? WF_CONTROL_PWM_FAN : WF_CONTROL_RPM_FAN;
/* We use the name & location here the same way we do for SMU sensors,
* see the comment in windfarm_smu_sensors.c. The locations are a bit
* less consistent here between the iMac and the desktop models, but
* that is good enough for our needs for now at least.
*
* One problem though is that Apple seem to be inconsistent with case
* and the kernel doesn't have strcasecmp =P
*/
fct->ctrl.name = NULL;
/* Names used on desktop models */
if (!strcmp(l, "Rear Fan 0") || !strcmp(l, "Rear Fan") ||
!strcmp(l, "Rear fan 0") || !strcmp(l, "Rear fan"))
fct->ctrl.name = "cpu-rear-fan-0";
else if (!strcmp(l, "Rear Fan 1") || !strcmp(l, "Rear fan 1"))
fct->ctrl.name = "cpu-rear-fan-1";
else if (!strcmp(l, "Front Fan 0") || !strcmp(l, "Front Fan") ||
!strcmp(l, "Front fan 0") || !strcmp(l, "Front fan"))
fct->ctrl.name = "cpu-front-fan-0";
else if (!strcmp(l, "Front Fan 1") || !strcmp(l, "Front fan 1"))
fct->ctrl.name = "cpu-front-fan-1";
else if (!strcmp(l, "Slots Fan") || !strcmp(l, "Slots fan"))
fct->ctrl.name = "slots-fan";
else if (!strcmp(l, "Drive Bay") || !strcmp(l, "Drive bay"))
fct->ctrl.name = "drive-bay-fan";
/* Names used on iMac models */
if (!strcmp(l, "System Fan") || !strcmp(l, "System fan"))
fct->ctrl.name = "system-fan";
else if (!strcmp(l, "CPU Fan") || !strcmp(l, "CPU fan"))
fct->ctrl.name = "cpu-fan";
else if (!strcmp(l, "Hard Drive") || !strcmp(l, "Hard drive"))
fct->ctrl.name = "drive-bay-fan";
/* Unrecognized fan, bail out */
if (fct->ctrl.name == NULL)
goto fail;
/* Get min & max values*/
v = (s32 *)get_property(node, "min-value", NULL);
if (v == NULL)
goto fail;
fct->min = *v;
v = (s32 *)get_property(node, "max-value", NULL);
if (v == NULL)
goto fail;
fct->max = *v;
/* Get "reg" value */
reg = (u32 *)get_property(node, "reg", NULL);
if (reg == NULL)
goto fail;
fct->reg = *reg;
if (wf_register_control(&fct->ctrl))
goto fail;
return fct;
fail:
kfree(fct);
return NULL;
}
static int __init smu_controls_init(void)
{
struct device_node *smu, *fans, *fan;
if (!smu_present())
return -ENODEV;
smu = of_find_node_by_type(NULL, "smu");
if (smu == NULL)
return -ENODEV;
/* Look for RPM fans */
for (fans = NULL; (fans = of_get_next_child(smu, fans)) != NULL;)
if (!strcmp(fans->name, "rpm-fans"))
break;
for (fan = NULL;
fans && (fan = of_get_next_child(fans, fan)) != NULL;) {
struct smu_fan_control *fct;
fct = smu_fan_create(fan, 0);
if (fct == NULL) {
printk(KERN_WARNING "windfarm: Failed to create SMU "
"RPM fan %s\n", fan->name);
continue;
}
list_add(&fct->link, &smu_fans);
}
of_node_put(fans);
/* Look for PWM fans */
for (fans = NULL; (fans = of_get_next_child(smu, fans)) != NULL;)
if (!strcmp(fans->name, "pwm-fans"))
break;
for (fan = NULL;
fans && (fan = of_get_next_child(fans, fan)) != NULL;) {
struct smu_fan_control *fct;
fct = smu_fan_create(fan, 1);
if (fct == NULL) {
printk(KERN_WARNING "windfarm: Failed to create SMU "
"PWM fan %s\n", fan->name);
continue;
}
list_add(&fct->link, &smu_fans);
}
of_node_put(fans);
of_node_put(smu);
return 0;
}
static void __exit smu_controls_exit(void)
{
struct smu_fan_control *fct;
while (!list_empty(&smu_fans)) {
fct = list_entry(smu_fans.next, struct smu_fan_control, link);
list_del(&fct->link);
wf_unregister_control(&fct->ctrl);
}
}
module_init(smu_controls_init);
module_exit(smu_controls_exit);
MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
MODULE_DESCRIPTION("SMU control objects for PowerMacs thermal control");
MODULE_LICENSE("GPL");

View 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, &amps);
if (rc)
return rc;
if (pow->fake_volts) {
*value = amps * 12 - 0x30000;
return 0;
}
rc = pow->volts->ops->get_value(pow->volts, &volts);
if (rc)
return rc;
power = (s32)((((u64)volts) * ((u64)amps)) >> 16);
if (!pow->quadratic) {
*value = power;
return 0;
}
tmps = (((u64)power) * ((u64)power)) >> 16;
tmpa = ((u64)cpuvcp->power_quads[0]) * tmps;
tmpb = ((u64)cpuvcp->power_quads[1]) * ((u64)power);
*value = (tmpa >> 28) + (tmpb >> 28) + (cpuvcp->power_quads[2] >> 12);
return 0;
}
static struct wf_sensor_ops smu_cpu_power_ops = {
.get_value = smu_cpu_power_get,
.release = smu_cpu_power_release,
.owner = THIS_MODULE,
};
static struct smu_cpu_power_sensor *
smu_cpu_power_create(struct wf_sensor *volts, struct wf_sensor *amps)
{
struct smu_cpu_power_sensor *pow;
pow = kmalloc(sizeof(struct smu_cpu_power_sensor), GFP_KERNEL);
if (pow == NULL)
return NULL;
pow->sens.ops = &smu_cpu_power_ops;
pow->sens.name = "cpu-power";
wf_get_sensor(volts);
pow->volts = volts;
wf_get_sensor(amps);
pow->amps = amps;
/* Some early machines need a faked voltage */
if (debugswitches && ((*debugswitches) & 0x80)) {
printk(KERN_INFO "windfarm: CPU Power sensor using faked"
" voltage !\n");
pow->fake_volts = 1;
} else
pow->fake_volts = 0;
/* Try to use quadratic transforms on PowerMac8,1 and 9,1 for now,
* I yet have to figure out what's up with 8,2 and will have to
* adjust for later, unless we can 100% trust the SDB partition...
*/
if ((machine_is_compatible("PowerMac8,1") ||
machine_is_compatible("PowerMac8,2") ||
machine_is_compatible("PowerMac9,1")) &&
cpuvcp_version >= 2) {
pow->quadratic = 1;
DBG("windfarm: CPU Power using quadratic transform\n");
} else
pow->quadratic = 0;
if (wf_register_sensor(&pow->sens))
goto fail;
return pow;
fail:
kfree(pow);
return NULL;
}
static int smu_fetch_param_partitions(void)
{
struct smu_sdbp_header *hdr;
/* Get CPU voltage/current/power calibration data */
hdr = smu_get_sdb_partition(SMU_SDB_CPUVCP_ID, NULL);
if (hdr == NULL) {
DBG("wf: cpuvcp partition (%02x) not found\n",
SMU_SDB_CPUVCP_ID);
return -ENODEV;
}
cpuvcp = (struct smu_sdbp_cpuvcp *)&hdr[1];
/* Keep version around */
cpuvcp_version = hdr->version;
/* Get CPU diode calibration data */
hdr = smu_get_sdb_partition(SMU_SDB_CPUDIODE_ID, NULL);
if (hdr == NULL) {
DBG("wf: cpudiode partition (%02x) not found\n",
SMU_SDB_CPUDIODE_ID);
return -ENODEV;
}
cpudiode = (struct smu_sdbp_cpudiode *)&hdr[1];
/* Get slots power calibration data if any */
hdr = smu_get_sdb_partition(SMU_SDB_SLOTSPOW_ID, NULL);
if (hdr != NULL)
slotspow = (struct smu_sdbp_slotspow *)&hdr[1];
/* Get debug switches if any */
hdr = smu_get_sdb_partition(SMU_SDB_DEBUG_SWITCHES_ID, NULL);
if (hdr != NULL)
debugswitches = (u8 *)&hdr[1];
return 0;
}
static int __init smu_sensors_init(void)
{
struct device_node *smu, *sensors, *s;
struct smu_ad_sensor *volt_sensor = NULL, *curr_sensor = NULL;
int rc;
if (!smu_present())
return -ENODEV;
/* Get parameters partitions */
rc = smu_fetch_param_partitions();
if (rc)
return rc;
smu = of_find_node_by_type(NULL, "smu");
if (smu == NULL)
return -ENODEV;
/* Look for sensors subdir */
for (sensors = NULL;
(sensors = of_get_next_child(smu, sensors)) != NULL;)
if (!strcmp(sensors->name, "sensors"))
break;
of_node_put(smu);
/* Create basic sensors */
for (s = NULL;
sensors && (s = of_get_next_child(sensors, s)) != NULL;) {
struct smu_ad_sensor *ads;
ads = smu_ads_create(s);
if (ads == NULL)
continue;
list_add(&ads->link, &smu_ads);
/* keep track of cpu voltage & current */
if (!strcmp(ads->sens.name, "cpu-voltage"))
volt_sensor = ads;
else if (!strcmp(ads->sens.name, "cpu-current"))
curr_sensor = ads;
}
of_node_put(sensors);
/* Create CPU power sensor if possible */
if (volt_sensor && curr_sensor)
smu_cpu_power = smu_cpu_power_create(&volt_sensor->sens,
&curr_sensor->sens);
return 0;
}
static void __exit smu_sensors_exit(void)
{
struct smu_ad_sensor *ads;
/* dispose of power sensor */
if (smu_cpu_power)
wf_unregister_sensor(&smu_cpu_power->sens);
/* dispose of basic sensors */
while (!list_empty(&smu_ads)) {
ads = list_entry(smu_ads.next, struct smu_ad_sensor, link);
list_del(&ads->link);
wf_unregister_sensor(&ads->sens);
}
}
module_init(smu_sensors_init);
module_exit(smu_sensors_exit);
MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
MODULE_DESCRIPTION("SMU sensor objects for PowerMacs thermal control");
MODULE_LICENSE("GPL");

View file

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

View file

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

View file

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

View file

@ -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);

View file

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

View file

@ -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;})

View file

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

View file

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

View file

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

View file

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

View file

@ -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)); \

View file

@ -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);

View file

@ -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);

View file

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

View file

@ -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);

View file

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

View file

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

View file

@ -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);

View file

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